aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
committerUpstream <upstream-import@none>1970-01-12 13:46:40 +0000
commit413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (patch)
tree642d637ab01ee6c54ca27d1fa96cf92a32df8053
downloadexternal_qemu-413f05aaf54fa08c0ae7e997327a4f4a473c0a8d.zip
external_qemu-413f05aaf54fa08c0ae7e997327a4f4a473c0a8d.tar.gz
external_qemu-413f05aaf54fa08c0ae7e997327a4f4a473c0a8d.tar.bz2
external/qemu 0.8.2
-rw-r--r--.cvsignore42
-rw-r--r--COPYING339
-rw-r--r--COPYING.LIB504
-rw-r--r--Changelog395
-rw-r--r--LICENSE12
-rw-r--r--Makefile163
-rw-r--r--Makefile.target544
-rw-r--r--README3
-rw-r--r--README.distrib16
-rw-r--r--TODO55
-rw-r--r--VERSION1
-rw-r--r--a.out.h431
-rw-r--r--aes.c1317
-rw-r--r--aes.h26
-rw-r--r--alpha-dis.c1960
-rw-r--r--alpha.ld128
-rw-r--r--arm-dis.c1680
-rw-r--r--arm.ld128
-rw-r--r--audio/alsaaudio.c974
-rw-r--r--audio/audio.c1871
-rw-r--r--audio/audio.h169
-rw-r--r--audio/audio_int.h280
-rw-r--r--audio/audio_template.h570
-rw-r--r--audio/coreaudio.c554
-rw-r--r--audio/dsound_template.h282
-rw-r--r--audio/dsoundaudio.c1080
-rw-r--r--audio/fmodaudio.c685
-rw-r--r--audio/mixeng.c277
-rw-r--r--audio/mixeng.h51
-rw-r--r--audio/mixeng_template.h177
-rw-r--r--audio/noaudio.c172
-rw-r--r--audio/ossaudio.c768
-rw-r--r--audio/rate_template.h111
-rw-r--r--audio/sdlaudio.c433
-rw-r--r--audio/sys-queue.h241
-rw-r--r--audio/wavaudio.c255
-rw-r--r--audio/wavcapture.c164
-rw-r--r--block-bochs.c224
-rw-r--r--block-cloop.c169
-rw-r--r--block-cow.c271
-rw-r--r--block-dmg.c297
-rw-r--r--block-qcow.c717
-rw-r--r--block-vmdk.c446
-rw-r--r--block-vpc.c242
-rw-r--r--block-vvfat.c2808
-rw-r--r--block.c858
-rw-r--r--block_int.h81
-rw-r--r--bswap.h202
-rw-r--r--cocoa.m927
-rwxr-xr-xconfigure906
-rw-r--r--console.c1090
-rw-r--r--cpu-all.h1015
-rw-r--r--cpu-defs.h125
-rw-r--r--cpu-exec.c1483
-rw-r--r--dis-asm.h455
-rw-r--r--disas.c413
-rw-r--r--disas.h21
-rw-r--r--dyngen-exec.h276
-rw-r--r--dyngen-op.h9
-rw-r--r--dyngen.c2618
-rw-r--r--dyngen.h435
-rw-r--r--elf.h1162
-rw-r--r--elf_ops.h205
-rw-r--r--exec-all.h605
-rw-r--r--exec.c2394
-rw-r--r--fpu/softfloat-macros.h720
-rw-r--r--fpu/softfloat-native.c368
-rw-r--r--fpu/softfloat-native.h359
-rw-r--r--fpu/softfloat-specialize.h464
-rw-r--r--fpu/softfloat.c5320
-rw-r--r--fpu/softfloat.h398
-rw-r--r--gdbstub.c980
-rw-r--r--gdbstub.h12
-rw-r--r--hw/acpi-dsdt.dsl559
-rw-r--r--hw/acpi-dsdt.hex278
-rw-r--r--hw/acpi.c615
-rw-r--r--hw/adb.c410
-rw-r--r--hw/adlib.c341
-rw-r--r--hw/apb_pci.c232
-rw-r--r--hw/apic.c1042
-rw-r--r--hw/arm_boot.c105
-rw-r--r--hw/arm_pic.c73
-rw-r--r--hw/arm_pic.h27
-rw-r--r--hw/arm_timer.c383
-rw-r--r--hw/cdrom.c156
-rw-r--r--hw/cirrus_vga.c3193
-rw-r--r--hw/cirrus_vga_rop.h78
-rw-r--r--hw/cirrus_vga_rop2.h281
-rw-r--r--hw/cuda.c656
-rw-r--r--hw/dma.c537
-rw-r--r--hw/es1370.c1062
-rw-r--r--hw/esp.c571
-rw-r--r--hw/fdc.c1757
-rw-r--r--hw/fmopl.c1390
-rw-r--r--hw/fmopl.h174
-rw-r--r--hw/grackle_pci.c156
-rw-r--r--hw/heathrow_pic.c168
-rw-r--r--hw/i8254.c482
-rw-r--r--hw/i8259.c561
-rw-r--r--hw/ide.c2535
-rw-r--r--hw/integratorcp.c546
-rw-r--r--hw/iommu.c258
-rw-r--r--hw/lance.c462
-rw-r--r--hw/lsi53c895a.c1571
-rw-r--r--hw/m48t59.c614
-rw-r--r--hw/m48t59.h13
-rw-r--r--hw/mc146818rtc.c463
-rw-r--r--hw/mips_r4k.c291
-rw-r--r--hw/ne2000.c816
-rw-r--r--hw/openpic.c1027
-rw-r--r--hw/parallel.c183
-rw-r--r--hw/pc.c911
-rw-r--r--hw/pci.c503
-rw-r--r--hw/pci_host.h93
-rw-r--r--hw/pckbd.c370
-rw-r--r--hw/pcnet.c1789
-rw-r--r--hw/pcspk.c147
-rw-r--r--hw/pflash_cfi02.c624
-rw-r--r--hw/piix_pci.c419
-rw-r--r--hw/pl011.c251
-rw-r--r--hw/pl050.c127
-rw-r--r--hw/pl080.c328
-rw-r--r--hw/pl110.c420
-rw-r--r--hw/pl110_template.h252
-rw-r--r--hw/pl190.c252
-rw-r--r--hw/ppc.c428
-rw-r--r--hw/ppc_chrp.c566
-rw-r--r--hw/ppc_prep.c694
-rw-r--r--hw/prep_pci.c167
-rw-r--r--hw/ps2.c566
-rw-r--r--hw/rtl8139.c3471
-rw-r--r--hw/sb16.c1451
-rw-r--r--hw/scsi-disk.c478
-rw-r--r--hw/serial.c456
-rw-r--r--hw/sh7750.c836
-rw-r--r--hw/sh7750_regnames.c128
-rw-r--r--hw/sh7750_regnames.h6
-rw-r--r--hw/sh7750_regs.h1623
-rw-r--r--hw/shix.c111
-rw-r--r--hw/slavio_intctl.c400
-rw-r--r--hw/slavio_misc.c244
-rw-r--r--hw/slavio_serial.c545
-rw-r--r--hw/slavio_timer.c288
-rw-r--r--hw/smc91c111.c714
-rw-r--r--hw/sun4m.c324
-rw-r--r--hw/sun4u.c368
-rw-r--r--hw/tc58128.c181
-rw-r--r--hw/tcx.c330
-rw-r--r--hw/unin_pci.c261
-rw-r--r--hw/usb-hid.c551
-rw-r--r--hw/usb-hub.c563
-rw-r--r--hw/usb-msd.c402
-rw-r--r--hw/usb-ohci.c1190
-rw-r--r--hw/usb-uhci.c674
-rw-r--r--hw/usb.c193
-rw-r--r--hw/usb.h178
-rw-r--r--hw/versatile_pci.c119
-rw-r--r--hw/versatilepb.c473
-rw-r--r--hw/vga.c1945
-rw-r--r--hw/vga_int.h173
-rw-r--r--hw/vga_template.h525
-rw-r--r--i386-dis.c4143
-rw-r--r--i386-vl.ld140
-rw-r--r--i386.ld140
-rw-r--r--ia64.ld211
-rw-r--r--keymaps.c145
-rw-r--r--keymaps/ar98
-rw-r--r--keymaps/common157
-rw-r--r--keymaps/da120
-rw-r--r--keymaps/de114
-rw-r--r--keymaps/de-ch169
-rw-r--r--keymaps/en-gb119
-rw-r--r--keymaps/en-us35
-rw-r--r--keymaps/es105
-rw-r--r--keymaps/et86
-rw-r--r--keymaps/fi124
-rw-r--r--keymaps/fo77
-rw-r--r--keymaps/fr181
-rw-r--r--keymaps/fr-be140
-rw-r--r--keymaps/fr-ca50
-rw-r--r--keymaps/fr-ch114
-rw-r--r--keymaps/hr125
-rw-r--r--keymaps/hu115
-rw-r--r--keymaps/is140
-rw-r--r--keymaps/it115
-rw-r--r--keymaps/ja104
-rw-r--r--keymaps/lt57
-rw-r--r--keymaps/lv128
-rw-r--r--keymaps/mk101
-rw-r--r--keymaps/modifiers17
-rw-r--r--keymaps/nl60
-rw-r--r--keymaps/nl-be3
-rw-r--r--keymaps/no119
-rw-r--r--keymaps/pl122
-rw-r--r--keymaps/pt113
-rw-r--r--keymaps/pt-br69
-rw-r--r--keymaps/ru109
-rw-r--r--keymaps/sl110
-rw-r--r--keymaps/sv82
-rw-r--r--keymaps/th131
-rw-r--r--keymaps/tr123
-rw-r--r--kqemu.c905
-rw-r--r--kqemu.h132
-rw-r--r--linux-2.6.9-qemu-fast.patch70
-rw-r--r--linux-user/arm-semi.c203
-rw-r--r--linux-user/arm/syscall.h42
-rw-r--r--linux-user/arm/syscall_nr.h262
-rw-r--r--linux-user/arm/termbits.h215
-rw-r--r--linux-user/elfload.c1240
-rw-r--r--linux-user/flat.h67
-rw-r--r--linux-user/flatload.c793
-rw-r--r--linux-user/i386/syscall.h221
-rw-r--r--linux-user/i386/syscall_nr.h274
-rw-r--r--linux-user/i386/termbits.h214
-rw-r--r--linux-user/ioctls.h302
-rw-r--r--linux-user/linuxload.c195
-rw-r--r--linux-user/main.c1716
-rw-r--r--linux-user/mips/syscall.h23
-rw-r--r--linux-user/mips/syscall_nr.h288
-rw-r--r--linux-user/mips/termbits.h229
-rw-r--r--linux-user/mmap.c417
-rw-r--r--linux-user/path.c147
-rw-r--r--linux-user/ppc/syscall.h130
-rw-r--r--linux-user/ppc/syscall_nr.h258
-rw-r--r--linux-user/ppc/termbits.h235
-rw-r--r--linux-user/qemu.h308
-rw-r--r--linux-user/sh4/syscall.h12
-rw-r--r--linux-user/sh4/syscall_nr.h292
-rw-r--r--linux-user/sh4/termbits.h274
-rw-r--r--linux-user/signal.c2067
-rw-r--r--linux-user/socket.h138
-rw-r--r--linux-user/sparc/syscall.h9
-rw-r--r--linux-user/sparc/syscall_nr.h220
-rw-r--r--linux-user/sparc/termbits.h279
-rw-r--r--linux-user/sparc64/syscall.h10
-rw-r--r--linux-user/sparc64/syscall_nr.h286
-rw-r--r--linux-user/sparc64/termbits.h279
-rw-r--r--linux-user/syscall.c3841
-rw-r--r--linux-user/syscall_defs.h1516
-rw-r--r--linux-user/syscall_types.h81
-rw-r--r--linux-user/vm86.c482
-rw-r--r--loader.c235
-rw-r--r--m68k-dis.c5051
-rw-r--r--m68k.ld177
-rw-r--r--mips-dis.c3999
-rw-r--r--monitor.c2419
-rw-r--r--osdep.c642
-rw-r--r--osdep.h55
-rw-r--r--pc-bios/Makefile24
-rw-r--r--pc-bios/README16
-rw-r--r--pc-bios/bios.binbin0 -> 65536 bytes
-rw-r--r--pc-bios/bios.diff270
-rw-r--r--pc-bios/linux_boot.S29
-rw-r--r--pc-bios/linux_boot.binbin0 -> 512 bytes
-rw-r--r--pc-bios/ohw.diff1843
-rw-r--r--pc-bios/openbios-sparc32bin0 -> 506966 bytes
-rw-r--r--pc-bios/ppc_rom.binbin0 -> 524288 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin0 -> 35328 bytes
-rw-r--r--pc-bios/vgabios.binbin0 -> 37888 bytes
-rw-r--r--pc-bios/vgabios.diff896
-rw-r--r--pc-bios/video.xbin0 -> 12192 bytes
-rw-r--r--ppc-dis.c3246
-rw-r--r--ppc.ld140
-rw-r--r--qemu-binfmt-conf.sh39
-rw-r--r--qemu-doc.texi1892
-rw-r--r--qemu-img.c716
-rw-r--r--qemu-img.texi126
-rw-r--r--qemu-tech.texi595
-rw-r--r--qemu_socket.h30
-rw-r--r--readline.c425
-rw-r--r--s390.ld204
-rw-r--r--sdl.c569
-rw-r--r--sdl_keysym.h278
-rw-r--r--sh4-dis.c2096
-rw-r--r--slirp/COPYRIGHT64
-rw-r--r--slirp/bootp.c254
-rw-r--r--slirp/bootp.h113
-rw-r--r--slirp/cksum.c141
-rw-r--r--slirp/ctl.h7
-rw-r--r--slirp/debug.c376
-rw-r--r--slirp/debug.h50
-rw-r--r--slirp/icmp_var.h69
-rw-r--r--slirp/if.c322
-rw-r--r--slirp/if.h50
-rw-r--r--slirp/ip.h313
-rw-r--r--slirp/ip_icmp.c375
-rw-r--r--slirp/ip_icmp.h164
-rw-r--r--slirp/ip_input.c697
-rw-r--r--slirp/ip_output.c205
-rw-r--r--slirp/libslirp.h41
-rw-r--r--slirp/main.h54
-rw-r--r--slirp/mbuf.c246
-rw-r--r--slirp/mbuf.h147
-rw-r--r--slirp/misc.c944
-rw-r--r--slirp/misc.h87
-rw-r--r--slirp/sbuf.c201
-rw-r--r--slirp/sbuf.h31
-rw-r--r--slirp/slirp.c665
-rw-r--r--slirp/slirp.h339
-rw-r--r--slirp/slirp_config.h206
-rw-r--r--slirp/socket.c717
-rw-r--r--slirp/socket.h104
-rw-r--r--slirp/tcp.h171
-rw-r--r--slirp/tcp_input.c1725
-rw-r--r--slirp/tcp_output.c605
-rw-r--r--slirp/tcp_subr.c1324
-rw-r--r--slirp/tcp_timer.c326
-rw-r--r--slirp/tcp_timer.h142
-rw-r--r--slirp/tcp_var.h252
-rw-r--r--slirp/tcpip.h74
-rw-r--r--slirp/tftp.c333
-rw-r--r--slirp/tftp.h32
-rw-r--r--slirp/udp.c675
-rw-r--r--slirp/udp.h110
-rw-r--r--softmmu_exec.h65
-rw-r--r--softmmu_header.h385
-rw-r--r--softmmu_template.h313
-rw-r--r--sparc-dis.c3265
-rw-r--r--sparc.ld128
-rw-r--r--tap-win32.c688
-rw-r--r--target-arm/cpu.h226
-rw-r--r--target-arm/exec.h75
-rw-r--r--target-arm/helper.c624
-rw-r--r--target-arm/nwfpe/double_cpdo.c296
-rw-r--r--target-arm/nwfpe/extended_cpdo.c273
-rw-r--r--target-arm/nwfpe/fpa11.c237
-rw-r--r--target-arm/nwfpe/fpa11.h122
-rw-r--r--target-arm/nwfpe/fpa11.inl51
-rw-r--r--target-arm/nwfpe/fpa11_cpdo.c117
-rw-r--r--target-arm/nwfpe/fpa11_cpdt.c376
-rw-r--r--target-arm/nwfpe/fpa11_cprt.c290
-rw-r--r--target-arm/nwfpe/fpopcode.c148
-rw-r--r--target-arm/nwfpe/fpopcode.h390
-rw-r--r--target-arm/nwfpe/fpsr.h108
-rw-r--r--target-arm/nwfpe/single_cpdo.c255
-rw-r--r--target-arm/op.c1203
-rw-r--r--target-arm/op_helper.c227
-rw-r--r--target-arm/op_mem.h70
-rw-r--r--target-arm/op_template.h53
-rw-r--r--target-arm/translate.c2576
-rw-r--r--target-i386/cpu.h653
-rw-r--r--target-i386/exec.h575
-rw-r--r--target-i386/helper.c3540
-rw-r--r--target-i386/helper2.c1031
-rw-r--r--target-i386/op.c2444
-rw-r--r--target-i386/opreg_template.h190
-rw-r--r--target-i386/ops_mem.h156
-rw-r--r--target-i386/ops_sse.h1374
-rw-r--r--target-i386/ops_template.h597
-rw-r--r--target-i386/ops_template_mem.h483
-rw-r--r--target-i386/translate-copy.c1323
-rw-r--r--target-i386/translate.c6523
-rw-r--r--target-mips/cpu.h279
-rw-r--r--target-mips/exec.h119
-rw-r--r--target-mips/fop_template.c99
-rw-r--r--target-mips/helper.c432
-rw-r--r--target-mips/mips-defs.h67
-rw-r--r--target-mips/op.c1155
-rw-r--r--target-mips/op_helper.c786
-rw-r--r--target-mips/op_helper_mem.c141
-rw-r--r--target-mips/op_mem.c149
-rw-r--r--target-mips/op_template.c65
-rw-r--r--target-mips/translate.c2443
-rw-r--r--target-ppc/cpu.h1025
-rw-r--r--target-ppc/exec.h90
-rw-r--r--target-ppc/helper.c1458
-rw-r--r--target-ppc/op.c1296
-rw-r--r--target-ppc/op_helper.c589
-rw-r--r--target-ppc/op_helper_mem.h100
-rw-r--r--target-ppc/op_mem.h371
-rw-r--r--target-ppc/op_template.h183
-rw-r--r--target-ppc/translate.c2701
-rw-r--r--target-ppc/translate_init.c2067
-rw-r--r--target-sh4/README.sh4150
-rw-r--r--target-sh4/cpu.h146
-rw-r--r--target-sh4/exec.h80
-rw-r--r--target-sh4/helper.c441
-rw-r--r--target-sh4/op.c967
-rw-r--r--target-sh4/op_helper.c372
-rw-r--r--target-sh4/op_mem.c78
-rw-r--r--target-sh4/translate.c1219
-rw-r--r--target-sparc/cpu.h277
-rw-r--r--target-sparc/exec.h104
-rw-r--r--target-sparc/fbranch_template.h89
-rw-r--r--target-sparc/fop_template.h81
-rw-r--r--target-sparc/helper.c589
-rw-r--r--target-sparc/op.c1597
-rw-r--r--target-sparc/op_helper.c918
-rw-r--r--target-sparc/op_mem.h114
-rw-r--r--target-sparc/op_template.h48
-rw-r--r--target-sparc/translate.c2845
-rw-r--r--tests/.cvsignore23
-rw-r--r--tests/Makefile93
-rw-r--r--tests/hello-arm.c113
-rw-r--r--tests/hello-i386.c26
-rw-r--r--tests/linux-test.c536
-rw-r--r--tests/pi_10.combin0 -> 54 bytes
-rw-r--r--tests/qruncom.c327
-rw-r--r--tests/runcom.c195
-rw-r--r--tests/sha1.c242
-rw-r--r--tests/test-i386-code16.S79
-rw-r--r--tests/test-i386-muldiv.h76
-rw-r--r--tests/test-i386-shift.h187
-rw-r--r--tests/test-i386-vm86.S104
-rw-r--r--tests/test-i386.c2629
-rw-r--r--tests/test-i386.h152
-rw-r--r--tests/test_path.c152
-rw-r--r--tests/testthread.c51
-rwxr-xr-xtexi2pod.pl428
-rw-r--r--thunk.c243
-rw-r--r--thunk.h158
-rw-r--r--translate-all.c311
-rw-r--r--translate-op.c37
-rw-r--r--usb-linux.c522
-rw-r--r--vgafont.h4611
-rw-r--r--vl.c6224
-rw-r--r--vl.h1180
-rw-r--r--vnc.c1085
-rw-r--r--vnc_keysym.h275
-rw-r--r--vnchextile.h209
-rw-r--r--x86_64.ld171
421 files changed, 241572 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..7be41c4
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,42 @@
+arm-user
+arm-softmmu
+armeb-user
+config-host.*
+dyngen
+i386
+i386-softmmu
+i386-user
+ppc-softmmu
+ppc64-softmmu
+ppc-user
+qemu-doc.html
+qemu-tech.html
+qemu-doc.info
+qemu-tech.info
+qemu.1
+qemu.pod
+qemu-img.1
+qemu-img.pod
+sparc-user
+qemu-img
+sparc-softmmu
+x86_64-softmmu
+sparc64-user
+sparc64-softmmu
+mips-softmmu
+mipsel-softmmu
+mips-user
+mipsel-user
+.gdbinit
+sh4-user
+sh4-softmmu
+*.aux
+*.cp
+*.dvi
+*.fn
+*.ky
+*.log
+*.pg
+*.toc
+*.tp
+*.vr
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..223ede7
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..58f3e5e
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,395 @@
+version 0.8.2:
+
+ - ACPI support
+ - PC VGA BIOS fixes
+ - switch to OpenBios for SPARC targets (Blue Swirl)
+ - VNC server fixes
+ - MIPS FPU support (Marius Groeger)
+ - Solaris/SPARC host support (Ben Taylor)
+ - PPC breakpoints and single stepping (Jason Wessel)
+ - USB updates (Paul Brook)
+ - UDP/TCP/telnet character devices (Jason Wessel)
+ - Windows sparse file support (Frediano Ziglio)
+ - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko)
+ - PCNET NIC support (Antony T Curtis)
+ - Support for variable frequency host CPUs
+ - Workaround for win32 SMP hosts
+ - Support for AMD Flash memories (Jocelyn Mayer)
+ - Audio capture to WAV files support (malc)
+
+version 0.8.1:
+
+ - USB tablet support (Brad Campbell, Anthony Liguori)
+ - win32 host serial support (Kazu)
+ - PC speaker support (Joachim Henke)
+ - IDE LBA48 support (Jens Axboe)
+ - SSE3 support
+ - Solaris port (Ben Taylor)
+ - Preliminary SH4 target (Samuel Tardieu)
+ - VNC server (Anthony Liguori)
+ - slirp fixes (Ed Swierk et al.)
+ - USB fixes
+ - ARM Versatile Platform Baseboard emulation (Paul Brook)
+
+version 0.8.0:
+
+ - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
+ cpu (Paul Brook)
+ - SMP support
+ - Mac OS X cocoa improvements (Mike Kronenberg)
+ - Mac OS X CoreAudio driver (Mike Kronenberg)
+ - DirectSound driver (malc)
+ - ALSA audio driver (malc)
+ - new audio options: '-soundhw' and '-audio-help' (malc)
+ - ES1370 PCI audio device (malc)
+ - Initial USB support
+ - Linux host serial port access
+ - Linux host low level parallel port access
+ - New network emulation code supporting VLANs.
+ - MIPS and MIPSel User Linux emulation
+ - MIPS fixes to boot Linux (Daniel Jacobowitz)
+ - NX bit support
+ - Initial SPARC SMP support (Blue Swirl)
+ - Major overhaul of the virtual FAT driver for read/write support
+ (Johannes Schindelin)
+
+version 0.7.2:
+
+ - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
+ - merge self modifying code handling in dirty ram page mecanism.
+ - MIPS fixes (Ralf Baechle)
+ - better user net performances
+
+version 0.7.1:
+
+ - read-only Virtual FAT support (Johannes Schindelin)
+ - Windows 2000 install disk full hack (original idea from Vladimir
+ N. Oleynik)
+ - VMDK disk image creation (Filip Navara)
+ - SPARC64 progress (Blue Swirl)
+ - initial MIPS support (Jocelyn mayer)
+ - MIPS improvements (Ralf Baechle)
+ - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne)
+ - IOAPIC support (Filip Navara)
+
+version 0.7.0:
+
+ - better BIOS translation and HDD geometry auto-detection
+ - user mode networking bug fix
+ - undocumented FPU ops support
+ - Cirrus VGA: support for 1280x1024x[8,15,16] modes
+ - 'pidfile' option
+ - .dmg disk image format support (Johannes Schindelin)
+ - keymaps support (initial patch by Johannes Schindelin)
+ - big endian ARM support (Lennert Buytenhek)
+ - added generic 64 bit target support
+ - x86_64 target support
+ - initial APIC support
+ - MMX/SSE/SSE2/PNI support
+ - PC parallel port support (Mark Jonckheere)
+ - initial SPARC64 support (Blue Swirl)
+ - SPARC target boots Linux (Blue Swirl)
+ - armv5te user mode support (Paul Brook)
+ - ARM VFP support (Paul Brook)
+ - ARM "Angel" semihosting syscalls (Paul Brook)
+ - user mode gdb stub support (Paul Brook)
+ - Samba 3 support
+ - initial Cocoa support (Pierre d'Herbemont)
+ - generic FPU emulation code
+ - Virtual PC read-only disk image support (Alex Beregszaszi)
+
+version 0.6.1:
+
+ - Mac OS X port (Pierre d'Herbemont)
+ - Virtual console support
+ - Better monitor line edition
+ - New block device layer
+ - New 'qcow' growable disk image support with AES encryption and
+ transparent decompression
+ - VMware 3 and 4 read-only disk image support (untested)
+ - Support for up to 4 serial ports
+ - TFTP server support (Magnus Damm)
+ - Port redirection support in user mode networking
+ - Support for not executable data sections
+ - Compressed loop disk image support (Johannes Schindelin)
+ - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve
+ Wormley)
+ - Fixed Fedora Core 2 problems (now you can run qemu without any
+ LD_ASSUME_KERNEL tricks on FC2)
+ - DHCP fix for Windows (accept DHCPREQUEST alone)
+ - SPARC system emulation (Blue Swirl)
+ - Automatic Samba configuration for host file access from Windows.
+ - '-loadvm' and '-full-screen' options
+ - ne2000 savevm support (Johannes Schindelin)
+ - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to
+ the virtual consoles.
+ - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert)
+ - Floppy fixes for NT4 and NT5 (Mike Nordell)
+ - NT4 IDE fixes (Ben Pfaf, Mike Nordell)
+ - SDL Audio support and SB16 fixes (malc)
+ - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi)
+ - VGA font change fix
+ - VGA read-only CRTC register fix
+
+version 0.6.0:
+
+ - minimalist FPU exception support (NetBSD FPU probe fix)
+ - cr0.ET fix (Win95 boot)
+ - *BSD port (Markus Niemisto)
+ - I/O access fix (signaled by Mark Jonckheere)
+ - IDE drives serial number fix (Mike Nordell)
+ - int13 CDROM BIOS fix (aka Solaris x86 install CD fix)
+ - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix)
+ - BSR/BSF "undefined behaviour" fix
+ - vmdk2raw: convert VMware disk images to raw images
+ - PCI support
+ - NE2K PCI support
+ - dummy VGA PCI support
+ - VGA font selection fix (Daniel Serpell)
+ - PIC reset fix (Hidemi KAWAI)
+ - PIC spurious irq support (aka Solaris install bug)
+ - added '-localtime' option
+ - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu))
+ - APM and system shutdown support
+ - Fixed system reset
+ - Support for other PC BIOSes
+ - Initial PowerMac hardware emulation
+ - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer)
+ - initial IDE BMDMA support (needed for Darwin x86)
+ - Set the default memory size for PC emulation to 128 MB
+
+version 0.5.5:
+
+ - SDL full screen support (initial patch by malc)
+ - VGA support on PowerPC PREP
+ - VBE fixes (Matthew Mastracci)
+ - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug)
+ - IDE master only fixes (aka Win98 CD-ROM probe bug)
+ - ARM load/store half word fix (Ulrich Hecht)
+ - FDC fixes for Win98
+
+version 0.5.4:
+
+ - qemu-fast fixes
+ - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
+ - keyboard/mouse fix (Mike Nordell)
+ - IDE fixes (Linux did not recognized slave drivers)
+ - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell)
+ - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer)
+ - User mode network stack
+ - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI)
+ - precise self modifying code (aka BeOS install bug)
+
+version 0.5.3:
+
+ - added Bochs VESA VBE support
+ - VGA memory map mode 3 access fix (OS/2 install fix)
+ - IDE fixes (Jens Axboe)
+ - CPU interrupt fixes
+ - fixed various TLB invalidation cases (NT install)
+ - fixed cr0.WP semantics (XP install)
+ - direct chaining support for SPARC and PowerPC (faster)
+ - ARM NWFPE support (initial patch by Ulrich Hecht)
+ - added specific x86 to x86 translator (close to native performance
+ in qemu-i386 and qemu-fast)
+ - shm syscalls support (Paul McKerras)
+ - added accurate CR0.MP/ME/TS emulation
+ - fixed DMA memory write access (Win95 boot floppy fix)
+ - graphical x86 linux loader
+ - command line monitor
+ - generic removable device support
+ - support of CD-ROM change
+ - multiple network interface support
+ - initial x86-64 host support (Gwenole Beauchesne)
+ - lret to outer priviledge fix (OS/2 install fix)
+ - task switch fixes (SkyOS boot)
+ - VM save/restore commands
+ - new timer API
+ - more precise RTC emulation (periodic timers + time updates)
+ - Win32 port (initial patch by Kazu)
+
+version 0.5.2:
+
+ - improved soft MMU speed (assembly functions and specializing)
+ - improved multitasking speed by avoiding flushing TBs when
+ switching tasks
+ - improved qemu-fast speed
+ - improved self modifying code handling (big performance gain in
+ softmmu mode).
+ - fixed IO checking
+ - fixed CD-ROM detection (win98 install CD)
+ - fixed addseg real mode bug (GRUB boot fix)
+ - added ROM memory support (win98 boot)
+ - fixed 'call Ev' in case of paging exception
+ - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically
+ when launching executables for the supported target CPUs.
+ - PowerPC system emulation update (Jocelyn Mayer)
+ - PC floppy emulation and DMA fixes (Jocelyn Mayer)
+ - polled mode for PIC (Jocelyn Mayer)
+ - fixed PTE dirty bit handling
+ - fixed xadd same reg bug
+ - fixed cmpxchg exception safeness
+ - access to virtual memory in gdb stub
+ - task gate and NT flag fixes
+ - eflags optimisation fix for string operations
+
+version 0.5.1:
+
+ - float access fixes when using soft mmu
+ - PC emulation support on PowerPC
+ - A20 support
+ - IDE CD-ROM emulation
+ - ARM fixes (Ulrich Hecht)
+ - SB16 emulation (malc)
+ - IRET and INT fixes in VM86 mode with IOPL=3
+ - Port I/Os use TSS io map
+ - Full task switching/task gate support
+ - added verr, verw, arpl, fcmovxx
+ - PowerPC target support (Jocelyn Mayer)
+ - Major SPARC target fixes (dynamically linked programs begin to work)
+
+version 0.5.0:
+
+ - full hardware level VGA emulation
+ - graphical display with SDL
+ - added PS/2 mouse and keyboard emulation
+ - popw (%esp) fix
+ - mov to/from segment data width fix
+ - added real mode support
+ - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu
+ - m68k host port (Richard Zidlicky)
+ - partial soft MMU support for memory mapped I/Os
+ - multi-target build
+ - fixed: no error code in hardware interrupts
+ - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
+ - correct single stepping thru string operations
+ - preliminary SPARC target support (Thomas M. Ogrisegg)
+ - tun-fd option (Rusty Russell)
+ - automatic IDE geometry detection
+ - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}.
+ - added man page
+ - added full soft mmu mode to launch unpatched OSes.
+
+version 0.4.3:
+
+ - x86 exception fix in case of nop instruction.
+ - gcc 3.2.2 bug workaround (RedHat 9 fix)
+ - sparc and Alpha host fixes
+ - many ARM target fixes: 'ls' and 'bash' can be launched.
+
+version 0.4.2:
+
+ - many exception handling fixes (can compile a Linux kernel inside vl)
+ - IDE emulation support
+ - initial GDB stub support
+ - deferred update support for disk images (Rusty Russell)
+ - accept User Mode Linux Copy On Write disk images
+ - SMP kernels can at least be booted
+
+version 0.4.1:
+
+ - more accurate timer support in vl.
+ - more reliable NE2000 probe in vl.
+ - added 2.5.66 kernel in vl-test.
+ - added VLTMPDIR environment variable in vl.
+
+version 0.4:
+
+ - initial support for ring 0 x86 processor emulation
+ - fixed signal handling for correct dosemu DPMI emulation
+ - fast x86 MMU emulation with mmap()
+ - fixed popl (%esp) case
+ - Linux kernel can be executed by QEMU with the 'vl' command.
+
+version 0.3:
+
+ - initial support for ARM emulation
+ - added fnsave, frstor, fnstenv, fldenv FPU instructions
+ - added FPU register save in signal emulation
+ - initial ARM port
+ - Sparc and Alpha ports work on the regression test
+ - generic ioctl number conversion
+ - fixed ioctl type conversion
+
+version 0.2:
+
+ - PowerPC disassembly and ELF symbols output (Rusty Russell)
+ - flock support (Rusty Russell)
+ - ugetrlimit support (Rusty Russell)
+ - fstat64 fix (Rusty Russell)
+ - initial Alpha port (Falk Hueffner)
+ - initial IA64 port (Matt Wilson)
+ - initial Sparc and Sparc64 port (David S. Miller)
+ - added HLT instruction
+ - LRET instruction fix.
+ - added GPF generation for I/Os.
+ - added INT3 and TF flag support.
+ - SHL instruction C flag fix.
+ - mmap emulation for host page size > 4KB
+ - self-modifying code support
+ - better VM86 support (dosemu works on non trivial programs)
+ - precise exception support (EIP is computed correctly in most cases)
+ - more precise LDT/GDT/IDT emulation
+ - faster segment load in vm86 mode
+ - direct chaining of basic blocks (faster emulation)
+
+version 0.1.6:
+
+ - automatic library search system. QEMU can now work with unpatched
+ ELF dynamic loader and libc (Rusty Russell).
+ - ISO C warning fixes (Alistair Strachan)
+ - first self-virtualizable version (works only as long as the
+ translation cache is not flushed)
+ - RH9 fixes
+
+version 0.1.5:
+
+ - ppc64 support + personality() patch (Rusty Russell)
+ - first Alpha CPU patches (Falk Hueffner)
+ - removed bfd.h dependancy
+ - fixed shrd, shld, idivl and divl on PowerPC.
+ - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
+
+version 0.1.4:
+
+ - more accurate VM86 emulation (can launch small DOS 16 bit
+ executables in wine).
+ - fixed push/pop fs/gs
+ - added iret instruction.
+ - added times() syscall and SIOCATMARK ioctl.
+
+version 0.1.3:
+
+ - S390 support (Ulrich Weigand)
+ - glibc 2.3.x compile fix (Ulrich Weigand)
+ - socketcall endian fix (Ulrich Weigand)
+ - struct sockaddr endian fix (Ulrich Weigand)
+ - sendmsg/recvmsg endian fix (Ulrich Weigand)
+ - execve endian fix (Ulrich Weigand)
+ - fdset endian fix (Ulrich Weigand)
+ - partial setsockopt syscall support (Ulrich Weigand)
+ - more accurate pushf/popf emulation
+ - first partial vm86() syscall support (can be used with runcom example).
+ - added bound, cmpxchg8b, cpuid instructions
+ - added 16 bit addressing support/override for string operations
+ - poll() fix
+
+version 0.1.2:
+
+ - compile fixes
+ - xlat instruction
+ - xchg instruction memory lock
+ - added simple vm86 example (not working with QEMU yet). The 54 byte
+ DOS executable 'pi_10.com' program was released by Bertram
+ Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
+
+version 0.1.1:
+
+ - glibc 2.2 compilation fixes
+ - added -s and -L options
+ - binary distribution of x86 glibc and wine
+ - big endian fixes in ELF loader and getdents.
+
+version 0.1:
+
+ - initial public release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bfc9a1f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+The following points clarify the QEMU licenses:
+
+1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC
+ system emulator are released under the GNU Lesser General Public
+ License.
+
+2) The Linux user mode QEMU emulator is released under the GNU General
+ Public License.
+
+3) QEMU is a trademark of Fabrice Bellard.
+
+Fabrice Bellard. \ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d6e6f61
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,163 @@
+# Makefile for QEMU.
+
+include config-host.mak
+
+.PHONY: all clean distclean dvi info install install-doc tar tarbin \
+ speed test test2 html dvi info
+
+CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I.
+ifdef CONFIG_DARWIN
+CFLAGS+= -mdynamic-no-pic
+endif
+ifeq ($(ARCH),sparc)
+CFLAGS+=-mcpu=ultrasparc
+endif
+LDFLAGS=-g
+LIBS=
+DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+TOOLS=qemu-img$(EXESUF)
+ifdef CONFIG_STATIC
+LDFLAGS+=-static
+endif
+ifdef BUILD_DOCS
+DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
+else
+DOCS=
+endif
+
+all: $(TOOLS) $(DOCS) recurse-all
+
+subdir-%: dyngen$(EXESUF)
+ $(MAKE) -C $(subst subdir-,,$@) all
+
+recurse-all: $(patsubst %,subdir-%, $(TARGET_DIRS))
+
+qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c
+ $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS)
+
+dyngen$(EXESUF): dyngen.c
+ $(HOST_CC) $(CFLAGS) $(DEFINES) -o $@ $^
+
+clean:
+# avoid old build problems by removing potentially incorrect old files
+ rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
+ rm -f *.o *.a $(TOOLS) dyngen$(EXESUF) TAGS *.pod *~ */*~
+ $(MAKE) -C tests clean
+ for d in $(TARGET_DIRS); do \
+ $(MAKE) -C $$d $@ || exit 1 ; \
+ done
+
+distclean: clean
+ rm -f config-host.mak config-host.h $(DOCS)
+ rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr}
+ for d in $(TARGET_DIRS); do \
+ rm -rf $$d || exit 1 ; \
+ done
+
+KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
+ar de en-us fi fr-be hr it lv nl pl ru th \
+common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
+
+install-doc: $(DOCS)
+ mkdir -p "$(DESTDIR)$(docdir)"
+ $(INSTALL) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
+ifndef CONFIG_WIN32
+ mkdir -p "$(DESTDIR)$(mandir)/man1"
+ $(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
+endif
+
+install: all $(if $(BUILD_DOCS),install-doc)
+ mkdir -p "$(DESTDIR)$(bindir)"
+ $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
+ mkdir -p "$(DESTDIR)$(datadir)"
+ for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
+ video.x openbios-sparc32 linux_boot.bin; do \
+ $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
+ done
+ifndef CONFIG_WIN32
+ mkdir -p "$(DESTDIR)$(datadir)/keymaps"
+ for x in $(KEYMAPS); do \
+ $(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
+ done
+endif
+ for d in $(TARGET_DIRS); do \
+ $(MAKE) -C $$d $@ || exit 1 ; \
+ done
+
+# various test targets
+test speed test2: all
+ $(MAKE) -C tests $@
+
+TAGS:
+ etags *.[ch] tests/*.[ch]
+
+cscope:
+ rm -f ./cscope.*
+ find . -name "*.[ch]" -print > ./cscope.files
+ cscope -b
+
+# documentation
+%.html: %.texi
+ texi2html -monolithic -number $<
+
+%.info: %.texi
+ makeinfo $< -o $@
+
+%.dvi: %.texi
+ texi2dvi $<
+
+qemu.1: qemu-doc.texi
+ $(SRC_PATH)/texi2pod.pl $< qemu.pod
+ pod2man --section=1 --center=" " --release=" " qemu.pod > $@
+
+qemu-img.1: qemu-img.texi
+ $(SRC_PATH)/texi2pod.pl $< qemu-img.pod
+ pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
+
+info: qemu-doc.info qemu-tech.info
+
+dvi: qemu-doc.dvi qemu-tech.dvi
+
+html: qemu-doc.html qemu-tech.html
+
+FILE=qemu-$(shell cat VERSION)
+
+# tar release (use 'make -k tar' on a checkouted tree)
+tar:
+ rm -rf /tmp/$(FILE)
+ cp -r . /tmp/$(FILE)
+ ( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS )
+ rm -rf /tmp/$(FILE)
+
+# generate a binary distribution
+tarbin:
+ ( cd / ; tar zcvf ~/qemu-$(VERSION)-i386.tar.gz \
+ $(bindir)/qemu \
+ $(bindir)/qemu-system-ppc \
+ $(bindir)/qemu-system-sparc \
+ $(bindir)/qemu-system-x86_64 \
+ $(bindir)/qemu-system-mips \
+ $(bindir)/qemu-system-mipsel \
+ $(bindir)/qemu-system-arm \
+ $(bindir)/qemu-i386 \
+ $(bindir)/qemu-arm \
+ $(bindir)/qemu-armeb \
+ $(bindir)/qemu-sparc \
+ $(bindir)/qemu-ppc \
+ $(bindir)/qemu-mips \
+ $(bindir)/qemu-mipsel \
+ $(bindir)/qemu-img \
+ $(datadir)/bios.bin \
+ $(datadir)/vgabios.bin \
+ $(datadir)/vgabios-cirrus.bin \
+ $(datadir)/ppc_rom.bin \
+ $(datadir)/video.x \
+ $(datadir)/openbios-sparc32 \
+ $(datadir)/linux_boot.bin \
+ $(docdir)/qemu-doc.html \
+ $(docdir)/qemu-tech.html \
+ $(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/Makefile.target b/Makefile.target
new file mode 100644
index 0000000..91516ed
--- /dev/null
+++ b/Makefile.target
@@ -0,0 +1,544 @@
+include config.mak
+
+TARGET_BASE_ARCH:=$(TARGET_ARCH)
+ifeq ($(TARGET_ARCH), x86_64)
+TARGET_BASE_ARCH:=i386
+endif
+ifeq ($(TARGET_ARCH), ppc64)
+TARGET_BASE_ARCH:=ppc
+endif
+ifeq ($(TARGET_ARCH), sparc64)
+TARGET_BASE_ARCH:=sparc
+endif
+TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
+VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
+DEFINES=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH)
+ifdef CONFIG_USER_ONLY
+VPATH+=:$(SRC_PATH)/linux-user
+DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH)
+endif
+CFLAGS=-Wall -O2 -g -fno-strict-aliasing
+#CFLAGS+=-Werror
+LDFLAGS=-g
+LIBS=
+HELPER_CFLAGS=$(CFLAGS)
+DYNGEN=../dyngen$(EXESUF)
+# user emulator name
+TARGET_ARCH2=$(TARGET_ARCH)
+ifeq ($(TARGET_ARCH),arm)
+ ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
+ TARGET_ARCH2=armeb
+ endif
+endif
+ifeq ($(TARGET_ARCH),sh4)
+ ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
+ TARGET_ARCH2=sh4eb
+ endif
+endif
+ifeq ($(TARGET_ARCH),mips)
+ ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
+ TARGET_ARCH2=mipsel
+ endif
+endif
+QEMU_USER=qemu-$(TARGET_ARCH2)
+# system emulator name
+ifdef CONFIG_SOFTMMU
+ifeq ($(TARGET_ARCH), i386)
+QEMU_SYSTEM=qemu$(EXESUF)
+else
+QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
+endif
+else
+QEMU_SYSTEM=qemu-fast
+endif
+
+ifdef CONFIG_USER_ONLY
+PROGS=$(QEMU_USER)
+else
+PROGS+=$(QEMU_SYSTEM)
+ifndef CONFIG_SOFTMMU
+CONFIG_STATIC=y
+endif
+endif # !CONFIG_USER_ONLY
+
+ifdef CONFIG_STATIC
+LDFLAGS+=-static
+endif
+
+ifeq ($(ARCH),i386)
+CFLAGS+=-fomit-frame-pointer
+OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
+ifeq ($(HAVE_GCC3_OPTIONS),yes)
+OP_CFLAGS+= -falign-functions=0 -fno-gcse
+else
+OP_CFLAGS+= -malign-functions=0
+endif
+
+ifdef TARGET_GPROF
+USE_I386_LD=y
+endif
+ifdef CONFIG_STATIC
+USE_I386_LD=y
+endif
+ifdef USE_I386_LD
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386.ld
+else
+# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
+# that the kernel ELF loader considers as an executable. I think this
+# is the simplest way to make it self virtualizable!
+LDFLAGS+=-Wl,-shared
+endif
+endif
+
+ifeq ($(ARCH),x86_64)
+OP_CFLAGS=$(CFLAGS) -falign-functions=0
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld
+endif
+
+ifeq ($(ARCH),ppc)
+CFLAGS+= -D__powerpc__
+OP_CFLAGS=$(CFLAGS)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld
+endif
+
+ifeq ($(ARCH),s390)
+OP_CFLAGS=$(CFLAGS)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld
+endif
+
+ifeq ($(ARCH),sparc)
+ifeq ($(CONFIG_SOLARIS),yes)
+CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g2 -ffixed-g3
+LDFLAGS+=-m32
+OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -fno-omit-frame-pointer -ffixed-i0
+else
+CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
+LDFLAGS+=-m32
+OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
+HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat
+# -static is used to avoid g1/g3 usage by the dynamic linker
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static
+endif
+endif
+
+ifeq ($(ARCH),sparc64)
+CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
+LDFLAGS+=-m64
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc64.ld
+OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
+endif
+
+ifeq ($(ARCH),alpha)
+# -msmall-data is not used because we want two-instruction relocations
+# for the constant constructions
+OP_CFLAGS=-Wall -O2 -g
+# Ensure there's only a single GP
+CFLAGS += -msmall-data
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld
+endif
+
+ifeq ($(ARCH),ia64)
+CFLAGS += -mno-sdata
+OP_CFLAGS=$(CFLAGS)
+LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
+endif
+
+ifeq ($(ARCH),arm)
+OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld
+endif
+
+ifeq ($(ARCH),m68k)
+OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer
+LDFLAGS+=-Wl,-T,m68k.ld
+endif
+
+ifeq ($(HAVE_GCC3_OPTIONS),yes)
+# very important to generate a return at the end of every operation
+OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
+endif
+
+ifeq ($(CONFIG_DARWIN),yes)
+OP_CFLAGS+= -mdynamic-no-pic
+LIBS+=-lmx
+endif
+
+#########################################################
+
+DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+LIBS+=-lm
+ifndef CONFIG_USER_ONLY
+LIBS+=-lz
+endif
+ifdef CONFIG_WIN32
+LIBS+=-lwinmm -lws2_32 -liphlpapi
+endif
+ifdef CONFIG_SOLARIS
+LIBS+=-lsocket -lnsl -lresolv
+endif
+
+# profiling code
+ifdef TARGET_GPROF
+LDFLAGS+=-p
+main.o: CFLAGS+=-p
+endif
+
+OBJS= main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o \
+ elfload.o linuxload.o
+ifdef TARGET_HAS_BFLT
+OBJS+= flatload.o
+endif
+
+ifeq ($(TARGET_ARCH), i386)
+OBJS+= vm86.o
+endif
+ifeq ($(TARGET_ARCH), arm)
+OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \
+nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \
+ nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o
+endif
+SRCS:= $(OBJS:.o=.c)
+OBJS+= libqemu.a
+
+# cpu emulator library
+LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
+ translate.o op.o
+ifdef CONFIG_SOFTFLOAT
+LIBOBJS+=fpu/softfloat.o
+else
+LIBOBJS+=fpu/softfloat-native.o
+endif
+DEFINES+=-I$(SRC_PATH)/fpu
+
+ifeq ($(TARGET_ARCH), i386)
+LIBOBJS+=helper.o helper2.o
+ifeq ($(ARCH), i386)
+LIBOBJS+=translate-copy.o
+endif
+endif
+
+ifeq ($(TARGET_ARCH), x86_64)
+LIBOBJS+=helper.o helper2.o
+endif
+
+ifeq ($(TARGET_BASE_ARCH), ppc)
+LIBOBJS+= op_helper.o helper.o
+endif
+
+ifeq ($(TARGET_ARCH), mips)
+LIBOBJS+= op_helper.o helper.o
+endif
+
+ifeq ($(TARGET_BASE_ARCH), sparc)
+LIBOBJS+= op_helper.o helper.o
+endif
+
+ifeq ($(TARGET_BASE_ARCH), arm)
+LIBOBJS+= op_helper.o helper.o
+endif
+
+ifeq ($(TARGET_BASE_ARCH), sh4)
+LIBOBJS+= op_helper.o helper.o
+endif
+
+# NOTE: the disassembler code is only needed for debugging
+LIBOBJS+=disas.o
+ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
+USE_I386_DIS=y
+endif
+ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64)
+USE_I386_DIS=y
+endif
+ifdef USE_I386_DIS
+LIBOBJS+=i386-dis.o
+endif
+ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha)
+LIBOBJS+=alpha-dis.o
+endif
+ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc)
+LIBOBJS+=ppc-dis.o
+endif
+ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips)
+LIBOBJS+=mips-dis.o
+endif
+ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
+LIBOBJS+=sparc-dis.o
+endif
+ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm)
+LIBOBJS+=arm-dis.o
+endif
+ifeq ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k)
+LIBOBJS+=m68k-dis.o
+endif
+ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4)
+LIBOBJS+=sh4-dis.o
+endif
+
+ifdef CONFIG_GDBSTUB
+OBJS+=gdbstub.o
+endif
+
+all: $(PROGS)
+
+$(QEMU_USER): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+ifeq ($(ARCH),alpha)
+# Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of
+# the address space (31 bit so sign extending doesn't matter)
+ echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
+endif
+
+# must use static linking to avoid leaving stuff in virtual address space
+VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o loader.o
+VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
+ifdef CONFIG_WIN32
+VL_OBJS+=tap-win32.o
+endif
+
+SOUND_HW = sb16.o es1370.o
+AUDIODRV = audio.o noaudio.o wavaudio.o
+ifdef CONFIG_SDL
+AUDIODRV += sdlaudio.o
+endif
+ifdef CONFIG_OSS
+AUDIODRV += ossaudio.o
+endif
+ifdef CONFIG_COREAUDIO
+AUDIODRV += coreaudio.o
+endif
+ifdef CONFIG_ALSA
+AUDIODRV += alsaaudio.o
+LIBS += -lasound
+endif
+ifdef CONFIG_DSOUND
+AUDIODRV += dsoundaudio.o
+LIBS += -lole32 -ldxguid
+endif
+ifdef CONFIG_FMOD
+AUDIODRV += fmodaudio.o
+audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
+LIBS += $(CONFIG_FMOD_LIB)
+endif
+ifdef CONFIG_ADLIB
+SOUND_HW += fmopl.o adlib.o
+endif
+AUDIODRV+= wavcapture.o
+
+# SCSI layer
+VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
+
+# USB layer
+VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
+
+# PCI network cards
+VL_OBJS+= ne2000.o rtl8139.o pcnet.o
+
+ifeq ($(TARGET_BASE_ARCH), i386)
+# Hardware support
+VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
+VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o acpi.o piix_pci.o
+VL_OBJS+= usb-uhci.o
+DEFINES += -DHAS_AUDIO
+endif
+ifeq ($(TARGET_BASE_ARCH), ppc)
+VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
+VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
+VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o
+DEFINES += -DHAS_AUDIO
+endif
+ifeq ($(TARGET_ARCH), mips)
+VL_OBJS+= mips_r4k.o dma.o vga.o serial.o i8254.o i8259.o
+#VL_OBJS+= #ide.o pckbd.o fdc.o m48t59.o
+endif
+ifeq ($(TARGET_BASE_ARCH), sparc)
+ifeq ($(TARGET_ARCH), sparc64)
+VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o
+VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
+VL_OBJS+= cirrus_vga.o parallel.o
+else
+VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o slavio_intctl.o
+VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
+endif
+endif
+ifeq ($(TARGET_BASE_ARCH), arm)
+VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
+VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o
+VL_OBJS+= versatile_pci.o
+endif
+ifeq ($(TARGET_BASE_ARCH), sh4)
+VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
+endif
+ifdef CONFIG_GDBSTUB
+VL_OBJS+=gdbstub.o
+endif
+ifdef CONFIG_SDL
+VL_OBJS+=sdl.o
+endif
+VL_OBJS+=vnc.o
+ifdef CONFIG_COCOA
+VL_OBJS+=cocoa.o
+COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
+ifdef CONFIG_COREAUDIO
+COCOA_LIBS+=-framework CoreAudio
+endif
+endif
+ifdef CONFIG_SLIRP
+DEFINES+=-I$(SRC_PATH)/slirp
+SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \
+slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \
+tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o
+VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
+endif
+
+VL_LDFLAGS=
+# specific flags are needed for non soft mmu emulator
+ifdef CONFIG_STATIC
+VL_LDFLAGS+=-static
+endif
+ifndef CONFIG_SOFTMMU
+VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
+endif
+ifndef CONFIG_DARWIN
+ifndef CONFIG_WIN32
+ifndef CONFIG_SOLARIS
+VL_LIBS=-lutil -lrt
+endif
+endif
+endif
+ifdef TARGET_GPROF
+vl.o: CFLAGS+=-p
+VL_LDFLAGS+=-p
+endif
+
+ifeq ($(ARCH),ia64)
+VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
+endif
+
+ifeq ($(ARCH),sparc64)
+VL_LDFLAGS+=-m64
+VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc64.ld
+endif
+
+ifdef CONFIG_WIN32
+SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
+endif
+
+$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
+ $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
+
+cocoa.o: cocoa.m
+ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
+
+sdl.o: sdl.c keymaps.c sdl_keysym.h
+ $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
+
+vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
+ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
+
+sdlaudio.o: sdlaudio.c
+ $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
+
+vldepend: $(VL_OBJS:.o=.c)
+ $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
+
+# libqemu
+
+libqemu.a: $(LIBOBJS)
+ rm -f $@
+ $(AR) rcs $@ $(LIBOBJS)
+
+translate.o: translate.c gen-op.h opc.h cpu.h
+
+translate-all.o: translate-all.c opc.h cpu.h
+
+translate-op.o: translate-all.c op.h opc.h cpu.h
+
+op.h: op.o $(DYNGEN)
+ $(DYNGEN) -o $@ $<
+
+opc.h: op.o $(DYNGEN)
+ $(DYNGEN) -c -o $@ $<
+
+gen-op.h: op.o $(DYNGEN)
+ $(DYNGEN) -g -o $@ $<
+
+op.o: op.c
+ $(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
+
+helper.o: helper.c
+ $(CC) $(HELPER_CFLAGS) $(DEFINES) -c -o $@ $<
+
+ifeq ($(TARGET_BASE_ARCH), i386)
+op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h
+endif
+
+ifeq ($(TARGET_ARCH), arm)
+op.o: op.c op_template.h
+pl110.o: pl110_template.h
+endif
+
+ifeq ($(TARGET_BASE_ARCH), sparc)
+op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h
+magic_load.o: elf_op.h
+endif
+
+ifeq ($(TARGET_BASE_ARCH), ppc)
+op.o: op.c op_template.h op_mem.h
+op_helper.o: op_helper_mem.h
+translate.o: translate.c translate_init.c
+endif
+
+ifeq ($(TARGET_ARCH), mips)
+op.o: op.c op_template.c op_mem.c
+op_helper.o: op_helper_mem.c
+endif
+
+loader.o: loader.c elf_ops.h
+
+acpi.o: acpi.c acpi-dsdt.hex
+
+ifdef BUILD_ACPI_TABLES
+$(SRC_PATH)/hw/acpi-dsdt.hex: acpi-dsdt.dsl
+ iasl -tc -p $@ $<
+endif
+
+ifeq ($(TARGET_ARCH), sh4)
+op.o: op.c op_mem.c cpu.h
+op_helper.o: op_helper.c exec.h cpu.h
+helper.o: helper.c exec.h cpu.h
+sh7750.o: sh7750.c sh7750_regs.h sh7750_regnames.h cpu.h
+shix.o: shix.c sh7750_regs.h sh7750_regnames.h
+sh7750_regnames.o: sh7750_regnames.c sh7750_regnames.h sh7750_regs.h
+tc58128.o: tc58128.c
+endif
+
+$(OBJS) $(LIBOBJS) $(VL_OBJS): config.h ../config-host.h
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
+
+%.o: %.S
+ $(CC) $(DEFINES) -c -o $@ $<
+
+clean:
+ rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o
+
+install: all
+ifneq ($(PROGS),)
+ $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
+endif
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
+
+ifeq (1, 0)
+audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
+fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
+CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
+endif
diff --git a/README b/README
new file mode 100644
index 0000000..1a39500
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+Read the documentation in qemu-doc.html.
+
+Fabrice Bellard. \ No newline at end of file
diff --git a/README.distrib b/README.distrib
new file mode 100644
index 0000000..a1598a2
--- /dev/null
+++ b/README.distrib
@@ -0,0 +1,16 @@
+Information about the various packages used to build the current qemu
+x86 binary distribution:
+
+* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution
+ was used to get most of the binary packages.
+
+* wine-20020411 tarball
+
+ ./configure --prefix=/usr/local/wine-i386
+
+ All exe and libs were stripped. Some compile time tools and the
+ includes were deleted.
+
+* ldconfig was launched to build the library links:
+
+ qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..516ea87
--- /dev/null
+++ b/TODO
@@ -0,0 +1,55 @@
+short term:
+----------
+- cycle counter for all archs
+- cpu_interrupt() win32/SMP fix
+- support variable tsc freq
+- USB host async
+- IDE async
+- debug option in 'configure' script + disable -fomit-frame-pointer
+- Precise VGA timings for old games/demos (malc patch)
+- merge PIC spurious interrupt patch
+- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
+- config file (at least for windows/Mac OS X)
+- update doc: PCI infos.
+- basic VGA optimizations
+- better code fetch (different exception handling + CS.limit support)
+- do not resize vga if invalid size.
+- avoid looping if only exceptions
+- TLB code protection support for PPC
+- see openMosix Doc
+- disable SMC handling for ARM/SPARC/PPC (not finished)
+- see undefined flags for BTx insn
+- user/kernel PUSHL/POPL in helper.c
+- keyboard output buffer filling timing emulation
+- return UD exception if LOCK prefix incorrectly used
+- test ldt limit < 7 ?
+- tests for each target CPU
+- fix CCOP optimisation
+- fix all remaining thread lock issues (must put TBs in a specific invalid
+ state, find a solution for tb_flush()).
+
+ppc specific:
+------------
+- TLB invalidate not needed if msr_pr changes
+- enable shift optimizations ?
+
+linux-user specific:
+-------------------
+- add IPC syscalls
+- handle rare page fault cases (in particular if page fault in helpers or
+ in syscall emulation code).
+- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
+ issues, fix 16 bit uid issues)
+- use page_unprotect_range in every suitable syscall to handle all
+ cases of self modifying code.
+- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
+- use kernel traps for unaligned accesses on ARM ?
+
+
+lower priority:
+--------------
+- int15 ah=86: use better timing
+- suppress shift_mem ops
+- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret)
+- optimize FPU operations (evaluate x87 stack pointer statically)
+- use -msoft-float on ARM
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..53a48a1
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.8.2 \ No newline at end of file
diff --git a/a.out.h b/a.out.h
new file mode 100644
index 0000000..1f978c1
--- /dev/null
+++ b/a.out.h
@@ -0,0 +1,431 @@
+/* a.out.h
+
+ Copyright 1997, 1998, 1999, 2001 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _A_OUT_H_
+#define _A_OUT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define COFF_IMAGE_WITH_PE
+#define COFF_LONG_SECTION_NAMES
+
+/*** coff information for Intel 386/486. */
+
+
+/********************** FILE HEADER **********************/
+
+struct external_filehdr {
+ short f_magic; /* magic number */
+ short f_nscns; /* number of sections */
+ unsigned long f_timdat; /* time & date stamp */
+ unsigned long f_symptr; /* file pointer to symtab */
+ unsigned long f_nsyms; /* number of symtab entries */
+ short f_opthdr; /* sizeof(optional hdr) */
+ short f_flags; /* flags */
+};
+
+/* Bits for f_flags:
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (no unresolved external references)
+ * F_LNNO line numbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
+ */
+
+#define F_RELFLG (0x0001)
+#define F_EXEC (0x0002)
+#define F_LNNO (0x0004)
+#define F_LSYMS (0x0008)
+
+
+
+#define I386MAGIC 0x14c
+#define I386PTXMAGIC 0x154
+#define I386AIXMAGIC 0x175
+
+/* This is Lynx's all-platform magic number for executables. */
+
+#define LYNXCOFFMAGIC 0415
+
+#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \
+ && (x).f_magic != I386AIXMAGIC \
+ && (x).f_magic != I386PTXMAGIC \
+ && (x).f_magic != LYNXCOFFMAGIC)
+
+#define FILHDR struct external_filehdr
+#define FILHSZ 20
+
+
+/********************** AOUT "OPTIONAL HEADER"=
+ **********************/
+
+
+typedef struct
+{
+ unsigned short magic; /* type of file */
+ unsigned short vstamp; /* version stamp */
+ unsigned long tsize; /* text size in bytes, padded to FW bdry*/
+ unsigned long dsize; /* initialized data " " */
+ unsigned long bsize; /* uninitialized data " " */
+ unsigned long entry; /* entry pt. */
+ unsigned long text_start; /* base of text used for this file */
+ unsigned long data_start; /* base of data used for this file=
+ */
+}
+AOUTHDR;
+
+#define AOUTSZ 28
+#define AOUTHDRSZ 28
+
+#define OMAGIC 0404 /* object files, eg as output */
+#define ZMAGIC 0413 /* demand load format, eg normal ld output */
+#define STMAGIC 0401 /* target shlib */
+#define SHMAGIC 0443 /* host shlib */
+
+
+/* define some NT default values */
+/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */
+#define NT_SECTION_ALIGNMENT 0x1000
+#define NT_FILE_ALIGNMENT 0x200
+#define NT_DEF_RESERVE 0x100000
+#define NT_DEF_COMMIT 0x1000
+
+/********************** SECTION HEADER **********************/
+
+
+struct external_scnhdr {
+ char s_name[8]; /* section name */
+ unsigned long s_paddr; /* physical address, offset
+ of last addr in scn */
+ unsigned long s_vaddr; /* virtual address */
+ unsigned long s_size; /* section size */
+ unsigned long s_scnptr; /* file ptr to raw data for section */
+ unsigned long s_relptr; /* file ptr to relocation */
+ unsigned long s_lnnoptr; /* file ptr to line numbers */
+ unsigned short s_nreloc; /* number of relocation entries */
+ unsigned short s_nlnno; /* number of line number entries*/
+ unsigned long s_flags; /* flags */
+};
+
+#define SCNHDR struct external_scnhdr
+#define SCNHSZ 40
+
+/*
+ * names of "special" sections
+ */
+#define _TEXT ".text"
+#define _DATA ".data"
+#define _BSS ".bss"
+#define _COMMENT ".comment"
+#define _LIB ".lib"
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+struct external_lineno {
+ union {
+ unsigned long l_symndx; /* function name symbol index, iff l_lnno 0 */
+ unsigned long l_paddr; /* (physical) address of line number */
+ } l_addr;
+ unsigned short l_lnno; /* line number */
+};
+
+#define LINENO struct external_lineno
+#define LINESZ 6
+
+/********************** SYMBOLS **********************/
+
+#define E_SYMNMLEN 8 /* # characters in a symbol name */
+#define E_FILNMLEN 14 /* # characters in a file name */
+#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+struct __attribute__((packed)) external_syment
+{
+ union {
+ char e_name[E_SYMNMLEN];
+ struct {
+ unsigned long e_zeroes;
+ unsigned long e_offset;
+ } e;
+ } e;
+ unsigned long e_value;
+ unsigned short e_scnum;
+ unsigned short e_type;
+ char e_sclass[1];
+ char e_numaux[1];
+};
+
+#define N_BTMASK (0xf)
+#define N_TMASK (0x30)
+#define N_BTSHFT (4)
+#define N_TSHIFT (2)
+
+union external_auxent {
+ struct {
+ unsigned long x_tagndx; /* str, un, or enum tag indx */
+ union {
+ struct {
+ unsigned short x_lnno; /* declaration line number */
+ unsigned short x_size; /* str/union/array size */
+ } x_lnsz;
+ unsigned long x_fsize; /* size of function */
+ } x_misc;
+ union {
+ struct { /* if ISFCN, tag, or .bb */
+ unsigned long x_lnnoptr;/* ptr to fcn line # */
+ unsigned long x_endndx; /* entry ndx past block end */
+ } x_fcn;
+ struct { /* if ISARY, up to 4 dimen. */
+ char x_dimen[E_DIMNUM][2];
+ } x_ary;
+ } x_fcnary;
+ unsigned short x_tvndx; /* tv index */
+ } x_sym;
+
+ union {
+ char x_fname[E_FILNMLEN];
+ struct {
+ unsigned long x_zeroes;
+ unsigned long x_offset;
+ } x_n;
+ } x_file;
+
+ struct {
+ unsigned long x_scnlen; /* section length */
+ unsigned short x_nreloc; /* # relocation entries */
+ unsigned short x_nlinno; /* # line numbers */
+ unsigned long x_checksum; /* section COMDAT checksum */
+ unsigned short x_associated;/* COMDAT associated section index */
+ char x_comdat[1]; /* COMDAT selection number */
+ } x_scn;
+
+ struct {
+ unsigned long x_tvfill; /* tv fill value */
+ unsigned short x_tvlen; /* length of .tv */
+ char x_tvran[2][2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+
+};
+
+#define SYMENT struct external_syment
+#define SYMESZ 18
+#define AUXENT union external_auxent
+#define AUXESZ 18
+
+#define _ETEXT "etext"
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct external_reloc {
+ char r_vaddr[4];
+ char r_symndx[4];
+ char r_type[2];
+};
+
+#define RELOC struct external_reloc
+#define RELSZ 10
+
+/* end of coff/i386.h */
+
+/* PE COFF header information */
+
+#ifndef _PE_H
+#define _PE_H
+
+/* NT specific file attributes */
+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
+#define IMAGE_FILE_32BIT_MACHINE 0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
+#define IMAGE_FILE_SYSTEM 0x1000
+#define IMAGE_FILE_DLL 0x2000
+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
+
+/* additional flags to be set for section headers to allow the NT loader to
+ read and write to the section data (to replace the addresses of data in
+ dlls for one thing); also to execute the section in .text's case=
+ */
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+#define IMAGE_SCN_MEM_WRITE 0x80000000
+
+/*
+ * Section characteristics added for ppc-nt
+ */
+
+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */
+
+#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
+
+#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */
+#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */
+#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */
+#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */
+
+#define IMAGE_SCN_MEM_FARDATA 0x00008000
+
+#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
+#define IMAGE_SCN_MEM_16BIT 0x00020000
+#define IMAGE_SCN_MEM_LOCKED 0x00040000
+#define IMAGE_SCN_MEM_PRELOAD 0x00080000
+
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */
+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
+
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */
+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */
+#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */
+
+/* COMDAT selection codes. */
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */
+#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */
+#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */
+
+/* Magic values that are true for all dos/nt implementations */
+#define DOSMAGIC 0x5a4d
+#define NT_SIGNATURE 0x00004550
+
+/* NT allows long filenames, we want to accommodate this. This may break
+ some of the bfd functions */
+#undef FILNMLEN
+#define FILNMLEN 18 /* # characters in a file name */
+
+
+#ifdef COFF_IMAGE_WITH_PE
+/* The filehdr is only weired in images */
+
+#undef FILHDR
+struct external_PE_filehdr
+{
+ /* DOS header fields */
+ unsigned short e_magic; /* Magic number, 0x5a4d */
+ unsigned short e_cblp; /* Bytes on last page of file, 0x90 */
+ unsigned short e_cp; /* Pages in file, 0x3 */
+ unsigned short e_crlc; /* Relocations, 0x0 */
+ unsigned short e_cparhdr; /* Size of header in paragraphs, 0x4 */
+ unsigned short e_minalloc; /* Minimum extra paragraphs needed, 0x0 */
+ unsigned short e_maxalloc; /* Maximum extra paragraphs needed, 0xFFFF */
+ unsigned short e_ss; /* Initial (relative) SS value, 0x0 */
+ unsigned short e_sp; /* Initial SP value, 0xb8 */
+ unsigned short e_csum; /* Checksum, 0x0 */
+ unsigned short e_ip; /* Initial IP value, 0x0 */
+ unsigned short e_cs; /* Initial (relative) CS value, 0x0 */
+ unsigned short e_lfarlc; /* File address of relocation table, 0x40 */
+ unsigned short e_ovno; /* Overlay number, 0x0 */
+ char e_res[4][2]; /* Reserved words, all 0x0 */
+ unsigned short e_oemid; /* OEM identifier (for e_oeminfo), 0x0 */
+ unsigned short e_oeminfo; /* OEM information; e_oemid specific, 0x0 */
+ char e_res2[10][2]; /* Reserved words, all 0x0 */
+ unsigned long e_lfanew; /* File address of new exe header, 0x80 */
+ char dos_message[16][4]; /* other stuff, always follow DOS header */
+ unsigned int nt_signature; /* required NT signature, 0x4550 */
+
+ /* From standard header */
+
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ unsigned long f_timdat; /* time & date stamp */
+ unsigned long f_symptr; /* file pointer to symtab */
+ unsigned long f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+};
+
+
+#define FILHDR struct external_PE_filehdr
+#undef FILHSZ
+#define FILHSZ 152
+
+#endif
+
+typedef struct
+{
+ unsigned short magic; /* type of file */
+ unsigned short vstamp; /* version stamp */
+ unsigned long tsize; /* text size in bytes, padded to FW bdry*/
+ unsigned long dsize; /* initialized data " " */
+ unsigned long bsize; /* uninitialized data " " */
+ unsigned long entry; /* entry pt. */
+ unsigned long text_start; /* base of text used for this file */
+ unsigned long data_start; /* base of all data used for this file */
+
+ /* NT extra fields; see internal.h for descriptions */
+ unsigned long ImageBase;
+ unsigned long SectionAlignment;
+ unsigned long FileAlignment;
+ unsigned short MajorOperatingSystemVersion;
+ unsigned short MinorOperatingSystemVersion;
+ unsigned short MajorImageVersion;
+ unsigned short MinorImageVersion;
+ unsigned short MajorSubsystemVersion;
+ unsigned short MinorSubsystemVersion;
+ char Reserved1[4];
+ unsigned long SizeOfImage;
+ unsigned long SizeOfHeaders;
+ unsigned long CheckSum;
+ unsigned short Subsystem;
+ unsigned short DllCharacteristics;
+ unsigned long SizeOfStackReserve;
+ unsigned long SizeOfStackCommit;
+ unsigned long SizeOfHeapReserve;
+ unsigned long SizeOfHeapCommit;
+ unsigned long LoaderFlags;
+ unsigned long NumberOfRvaAndSizes;
+ /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
+ char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
+
+} PEAOUTHDR;
+
+
+#undef AOUTSZ
+#define AOUTSZ (AOUTHDRSZ + 196)
+
+#undef E_FILNMLEN
+#define E_FILNMLEN 18 /* # characters in a file name */
+#endif
+
+/* end of coff/pe.h */
+
+#define DT_NON (0) /* no derived type */
+#define DT_PTR (1) /* pointer */
+#define DT_FCN (2) /* function */
+#define DT_ARY (3) /* array */
+
+#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _A_OUT_H_ */
+
diff --git a/aes.c b/aes.c
new file mode 100644
index 0000000..cd4484f
--- /dev/null
+++ b/aes.c
@@ -0,0 +1,1317 @@
+/**
+ *
+ * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project.
+ */
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "vl.h"
+#include "aes.h"
+
+#define NDEBUG
+#include <assert.h>
+
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+#define MAXKC (256/32)
+#define MAXKB (256/8)
+#define MAXNR 14
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i = 0;
+ u32 temp;
+
+ if (!userKey || !key)
+ return -1;
+ if (bits != 128 && bits != 192 && bits != 256)
+ return -2;
+
+ rk = key->rd_key;
+
+ if (bits==128)
+ key->rounds = 10;
+ else if (bits==192)
+ key->rounds = 12;
+ else
+ key->rounds = 14;
+
+ rk[0] = GETU32(userKey );
+ rk[1] = GETU32(userKey + 4);
+ rk[2] = GETU32(userKey + 8);
+ rk[3] = GETU32(userKey + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(userKey + 16);
+ rk[5] = GETU32(userKey + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(userKey + 24);
+ rk[7] = GETU32(userKey + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (Te4[(temp >> 24) ] & 0xff000000) ^
+ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i, j, status;
+ u32 temp;
+
+ /* first, start with an encryption schedule */
+ status = AES_set_encrypt_key(userKey, bits, key);
+ if (status < 0)
+ return status;
+
+ rk = key->rd_key;
+
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+ for (i = 1; i < (key->rounds); i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];
+ }
+ return 0;
+}
+
+#ifndef AES_ASM
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] & 0xff000000) ^
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] & 0xff000000) ^
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] & 0xff000000) ^
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] & 0xff000000) ^
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] & 0xff000000) ^
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] & 0xff000000) ^
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] & 0xff000000) ^
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] & 0xff000000) ^
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+#endif /* AES_ASM */
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc)
+{
+
+ unsigned long n;
+ unsigned long len = length;
+ unsigned char tmp[AES_BLOCK_SIZE];
+
+ assert(in && out && key && ivec);
+
+ if (enc) {
+ while (len >= AES_BLOCK_SIZE) {
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ AES_encrypt(tmp, out, key);
+ memcpy(ivec, out, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ for(n=0; n < len; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ for(n=len; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = ivec[n];
+ AES_encrypt(tmp, tmp, key);
+ memcpy(out, tmp, AES_BLOCK_SIZE);
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ } else {
+ while (len >= AES_BLOCK_SIZE) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(in, out, key);
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ out[n] ^= ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(tmp, tmp, key);
+ for(n=0; n < len; ++n)
+ out[n] = tmp[n] ^ ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/aes.h b/aes.h
new file mode 100644
index 0000000..a0167eb
--- /dev/null
+++ b/aes.h
@@ -0,0 +1,26 @@
+#ifndef QEMU_AES_H
+#define QEMU_AES_H
+
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+struct aes_key_st {
+ uint32_t rd_key[4 *(AES_MAXNR + 1)];
+ int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc);
+
+#endif
diff --git a/alpha-dis.c b/alpha-dis.c
new file mode 100644
index 0000000..81a55e9
--- /dev/null
+++ b/alpha-dis.c
@@ -0,0 +1,1960 @@
+/* alpha-dis.c -- Disassemble Alpha AXP instructions
+ Copyright 1996, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by Richard Henderson <rth@tamu.edu>,
+ patterned after the PPC opcode handling written by Ian Lance Taylor.
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include <stdio.h>
+#include "dis-asm.h"
+
+/* The opcode table is an array of struct alpha_opcode. */
+
+struct alpha_opcode
+{
+ /* The opcode name. */
+ const char *name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ unsigned opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ unsigned mask;
+
+ /* One bit flags for the opcode. These are primarily used to
+ indicate specific processors and environments support the
+ instructions. The defined values are listed below. */
+ unsigned flags;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[4];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+extern const struct alpha_opcode alpha_opcodes[];
+extern const unsigned alpha_num_opcodes;
+
+/* Values defined for the flags field of a struct alpha_opcode. */
+
+/* CPU Availability */
+#define AXP_OPCODE_BASE 0x0001 /* Base architecture -- all cpus. */
+#define AXP_OPCODE_EV4 0x0002 /* EV4 specific PALcode insns. */
+#define AXP_OPCODE_EV5 0x0004 /* EV5 specific PALcode insns. */
+#define AXP_OPCODE_EV6 0x0008 /* EV6 specific PALcode insns. */
+#define AXP_OPCODE_BWX 0x0100 /* Byte/word extension (amask bit 0). */
+#define AXP_OPCODE_CIX 0x0200 /* "Count" extension (amask bit 1). */
+#define AXP_OPCODE_MAX 0x0400 /* Multimedia extension (amask bit 8). */
+
+#define AXP_OPCODE_NOPAL (~(AXP_OPCODE_EV4|AXP_OPCODE_EV5|AXP_OPCODE_EV6))
+
+/* A macro to extract the major opcode from an instruction. */
+#define AXP_OP(i) (((i) >> 26) & 0x3F)
+
+/* The total number of major opcodes. */
+#define AXP_NOPS 0x40
+
+
+/* The operands table is an array of struct alpha_operand. */
+
+struct alpha_operand
+{
+ /* The number of bits in the operand. */
+ unsigned int bits : 5;
+
+ /* How far the operand is left shifted in the instruction. */
+ unsigned int shift : 5;
+
+ /* The default relocation type for this operand. */
+ signed int default_reloc : 16;
+
+ /* One bit syntax flags. */
+ unsigned int flags : 16;
+
+ /* Insertion function. This is used by the assembler. To insert an
+ operand value into an instruction, check this field.
+
+ If it is NULL, execute
+ i |= (op & ((1 << o->bits) - 1)) << o->shift;
+ (i is the instruction which we are filling in, o is a pointer to
+ this structure, and op is the opcode value; this assumes twos
+ complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction and the operand value. It will return the new value
+ of the instruction. If the ERRMSG argument is not NULL, then if
+ the operand value is illegal, *ERRMSG will be set to a warning
+ string (the operand will be inserted in any case). If the
+ operand value is legal, *ERRMSG will be unchanged (most operands
+ can accept any value). */
+ unsigned (*insert) PARAMS ((unsigned instruction, int op,
+ const char **errmsg));
+
+ /* Extraction function. This is used by the disassembler. To
+ extract this operand type from an instruction, check this field.
+
+ If it is NULL, compute
+ op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+ if ((o->flags & AXP_OPERAND_SIGNED) != 0
+ && (op & (1 << (o->bits - 1))) != 0)
+ op -= 1 << o->bits;
+ (i is the instruction, o is a pointer to this structure, and op
+ is the result; this assumes twos complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction value. It will return the value of the operand. If
+ the INVALID argument is not NULL, *INVALID will be set to
+ non-zero if this operand type can not actually be extracted from
+ this operand (i.e., the instruction does not match). If the
+ operand is valid, *INVALID will not be changed. */
+ int (*extract) PARAMS ((unsigned instruction, int *invalid));
+};
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the alpha_opcodes table. */
+
+extern const struct alpha_operand alpha_operands[];
+extern const unsigned alpha_num_operands;
+
+/* Values defined for the flags field of a struct alpha_operand. */
+
+/* Mask for selecting the type for typecheck purposes */
+#define AXP_OPERAND_TYPECHECK_MASK \
+ (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA | AXP_OPERAND_IR | \
+ AXP_OPERAND_FPR | AXP_OPERAND_RELATIVE | AXP_OPERAND_SIGNED | \
+ AXP_OPERAND_UNSIGNED)
+
+/* This operand does not actually exist in the assembler input. This
+ is used to support extended mnemonics, for which two operands fields
+ are identical. The assembler should call the insert function with
+ any op value. The disassembler should call the extract function,
+ ignore the return value, and check the value placed in the invalid
+ argument. */
+#define AXP_OPERAND_FAKE 01
+
+/* The operand should be wrapped in parentheses rather than separated
+ from the previous by a comma. This is used for the load and store
+ instructions which want their operands to look like "Ra,disp(Rb)". */
+#define AXP_OPERAND_PARENS 02
+
+/* Used in combination with PARENS, this supresses the supression of
+ the comma. This is used for "jmp Ra,(Rb),hint". */
+#define AXP_OPERAND_COMMA 04
+
+/* This operand names an integer register. */
+#define AXP_OPERAND_IR 010
+
+/* This operand names a floating point register. */
+#define AXP_OPERAND_FPR 020
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define AXP_OPERAND_RELATIVE 040
+
+/* This operand takes signed values. */
+#define AXP_OPERAND_SIGNED 0100
+
+/* This operand takes unsigned values. This exists primarily so that
+ a flags value of 0 can be treated as end-of-arguments. */
+#define AXP_OPERAND_UNSIGNED 0200
+
+/* Supress overflow detection on this field. This is used for hints. */
+#define AXP_OPERAND_NOOVERFLOW 0400
+
+/* Mask for optional argument default value. */
+#define AXP_OPERAND_OPTIONAL_MASK 07000
+
+/* This operand defaults to zero. This is used for jump hints. */
+#define AXP_OPERAND_DEFAULT_ZERO 01000
+
+/* This operand should default to the first (real) operand and is used
+ in conjunction with AXP_OPERAND_OPTIONAL. This allows
+ "and $0,3,$0" to be written as "and $0,3", etc. I don't like
+ it, but it's what DEC does. */
+#define AXP_OPERAND_DEFAULT_FIRST 02000
+
+/* Similarly, this operand should default to the second (real) operand.
+ This allows "negl $0" instead of "negl $0,$0". */
+#define AXP_OPERAND_DEFAULT_SECOND 04000
+
+
+/* Register common names */
+
+#define AXP_REG_V0 0
+#define AXP_REG_T0 1
+#define AXP_REG_T1 2
+#define AXP_REG_T2 3
+#define AXP_REG_T3 4
+#define AXP_REG_T4 5
+#define AXP_REG_T5 6
+#define AXP_REG_T6 7
+#define AXP_REG_T7 8
+#define AXP_REG_S0 9
+#define AXP_REG_S1 10
+#define AXP_REG_S2 11
+#define AXP_REG_S3 12
+#define AXP_REG_S4 13
+#define AXP_REG_S5 14
+#define AXP_REG_FP 15
+#define AXP_REG_A0 16
+#define AXP_REG_A1 17
+#define AXP_REG_A2 18
+#define AXP_REG_A3 19
+#define AXP_REG_A4 20
+#define AXP_REG_A5 21
+#define AXP_REG_T8 22
+#define AXP_REG_T9 23
+#define AXP_REG_T10 24
+#define AXP_REG_T11 25
+#define AXP_REG_RA 26
+#define AXP_REG_PV 27
+#define AXP_REG_T12 27
+#define AXP_REG_AT 28
+#define AXP_REG_GP 29
+#define AXP_REG_SP 30
+#define AXP_REG_ZERO 31
+
+#define bfd_mach_alpha_ev4 0x10
+#define bfd_mach_alpha_ev5 0x20
+#define bfd_mach_alpha_ev6 0x30
+
+enum bfd_reloc_code_real {
+ BFD_RELOC_23_PCREL_S2,
+ BFD_RELOC_ALPHA_HINT
+};
+
+/* This file holds the Alpha AXP opcode table. The opcode table includes
+ almost all of the extended instruction mnemonics. This permits the
+ disassembler to use them, and simplifies the assembler logic, at the
+ cost of increasing the table size. The table is strictly constant
+ data, so the compiler should be able to put it in the text segment.
+
+ This file also holds the operand table. All knowledge about inserting
+ and extracting operands from instructions is kept in this file.
+
+ The information for the base instruction set was compiled from the
+ _Alpha Architecture Handbook_, Digital Order Number EC-QD2KB-TE,
+ version 2.
+
+ The information for the post-ev5 architecture extensions BWX, CIX and
+ MAX came from version 3 of this same document, which is also available
+ on-line at http://ftp.digital.com/pub/Digital/info/semiconductor
+ /literature/alphahb2.pdf
+
+ The information for the EV4 PALcode instructions was compiled from
+ _DECchip 21064 and DECchip 21064A Alpha AXP Microprocessors Hardware
+ Reference Manual_, Digital Order Number EC-Q9ZUA-TE, preliminary
+ revision dated June 1994.
+
+ The information for the EV5 PALcode instructions was compiled from
+ _Alpha 21164 Microprocessor Hardware Reference Manual_, Digital
+ Order Number EC-QAEQB-TE, preliminary revision dated April 1995. */
+
+/* Local insertion and extraction functions */
+
+static unsigned insert_rba PARAMS((unsigned, int, const char **));
+static unsigned insert_rca PARAMS((unsigned, int, const char **));
+static unsigned insert_za PARAMS((unsigned, int, const char **));
+static unsigned insert_zb PARAMS((unsigned, int, const char **));
+static unsigned insert_zc PARAMS((unsigned, int, const char **));
+static unsigned insert_bdisp PARAMS((unsigned, int, const char **));
+static unsigned insert_jhint PARAMS((unsigned, int, const char **));
+static unsigned insert_ev6hwjhint PARAMS((unsigned, int, const char **));
+
+static int extract_rba PARAMS((unsigned, int *));
+static int extract_rca PARAMS((unsigned, int *));
+static int extract_za PARAMS((unsigned, int *));
+static int extract_zb PARAMS((unsigned, int *));
+static int extract_zc PARAMS((unsigned, int *));
+static int extract_bdisp PARAMS((unsigned, int *));
+static int extract_jhint PARAMS((unsigned, int *));
+static int extract_ev6hwjhint PARAMS((unsigned, int *));
+
+
+/* The operands table */
+
+const struct alpha_operand alpha_operands[] =
+{
+ /* The fields are bits, shift, insert, extract, flags */
+ /* The zero index is used to indicate end-of-list */
+#define UNUSED 0
+ { 0, 0, 0, 0, 0, 0 },
+
+ /* The plain integer register fields */
+#define RA (UNUSED + 1)
+ { 5, 21, 0, AXP_OPERAND_IR, 0, 0 },
+#define RB (RA + 1)
+ { 5, 16, 0, AXP_OPERAND_IR, 0, 0 },
+#define RC (RB + 1)
+ { 5, 0, 0, AXP_OPERAND_IR, 0, 0 },
+
+ /* The plain fp register fields */
+#define FA (RC + 1)
+ { 5, 21, 0, AXP_OPERAND_FPR, 0, 0 },
+#define FB (FA + 1)
+ { 5, 16, 0, AXP_OPERAND_FPR, 0, 0 },
+#define FC (FB + 1)
+ { 5, 0, 0, AXP_OPERAND_FPR, 0, 0 },
+
+ /* The integer registers when they are ZERO */
+#define ZA (FC + 1)
+ { 5, 21, 0, AXP_OPERAND_FAKE, insert_za, extract_za },
+#define ZB (ZA + 1)
+ { 5, 16, 0, AXP_OPERAND_FAKE, insert_zb, extract_zb },
+#define ZC (ZB + 1)
+ { 5, 0, 0, AXP_OPERAND_FAKE, insert_zc, extract_zc },
+
+ /* The RB field when it needs parentheses */
+#define PRB (ZC + 1)
+ { 5, 16, 0, AXP_OPERAND_IR|AXP_OPERAND_PARENS, 0, 0 },
+
+ /* The RB field when it needs parentheses _and_ a preceding comma */
+#define CPRB (PRB + 1)
+ { 5, 16, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_PARENS|AXP_OPERAND_COMMA, 0, 0 },
+
+ /* The RB field when it must be the same as the RA field */
+#define RBA (CPRB + 1)
+ { 5, 16, 0, AXP_OPERAND_FAKE, insert_rba, extract_rba },
+
+ /* The RC field when it must be the same as the RB field */
+#define RCA (RBA + 1)
+ { 5, 0, 0, AXP_OPERAND_FAKE, insert_rca, extract_rca },
+
+ /* The RC field when it can *default* to RA */
+#define DRC1 (RCA + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 },
+
+ /* The RC field when it can *default* to RB */
+#define DRC2 (DRC1 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 },
+
+ /* The FC field when it can *default* to RA */
+#define DFC1 (DRC2 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 },
+
+ /* The FC field when it can *default* to RB */
+#define DFC2 (DFC1 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 },
+
+ /* The unsigned 8-bit literal of Operate format insns */
+#define LIT (DFC2 + 1)
+ { 8, 13, -LIT, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The signed 16-bit displacement of Memory format insns. From here
+ we can't tell what relocation should be used, so don't use a default. */
+#define MDISP (LIT + 1)
+ { 16, 0, -MDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The signed "23-bit" aligned displacement of Branch format insns */
+#define BDISP (MDISP + 1)
+ { 21, 0, BFD_RELOC_23_PCREL_S2,
+ AXP_OPERAND_RELATIVE, insert_bdisp, extract_bdisp },
+
+ /* The 26-bit PALcode function */
+#define PALFN (BDISP + 1)
+ { 26, 0, -PALFN, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The optional signed "16-bit" aligned displacement of the JMP/JSR hint */
+#define JMPHINT (PALFN + 1)
+ { 14, 0, BFD_RELOC_ALPHA_HINT,
+ AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW,
+ insert_jhint, extract_jhint },
+
+ /* The optional hint to RET/JSR_COROUTINE */
+#define RETHINT (JMPHINT + 1)
+ { 14, 0, -RETHINT,
+ AXP_OPERAND_UNSIGNED|AXP_OPERAND_DEFAULT_ZERO, 0, 0 },
+
+ /* The 12-bit displacement for the ev[46] hw_{ld,st} (pal1b/pal1f) insns */
+#define EV4HWDISP (RETHINT + 1)
+#define EV6HWDISP (EV4HWDISP)
+ { 12, 0, -EV4HWDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The 5-bit index for the ev4 hw_m[ft]pr (pal19/pal1d) insns */
+#define EV4HWINDEX (EV4HWDISP + 1)
+ { 5, 0, -EV4HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 8-bit index for the oddly unqualified hw_m[tf]pr insns
+ that occur in DEC PALcode. */
+#define EV4EXTHWINDEX (EV4HWINDEX + 1)
+ { 8, 0, -EV4EXTHWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 10-bit displacement for the ev5 hw_{ld,st} (pal1b/pal1f) insns */
+#define EV5HWDISP (EV4EXTHWINDEX + 1)
+ { 10, 0, -EV5HWDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The 16-bit index for the ev5 hw_m[ft]pr (pal19/pal1d) insns */
+#define EV5HWINDEX (EV5HWDISP + 1)
+ { 16, 0, -EV5HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 16-bit combined index/scoreboard mask for the ev6
+ hw_m[ft]pr (pal19/pal1d) insns */
+#define EV6HWINDEX (EV5HWINDEX + 1)
+ { 16, 0, -EV6HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 13-bit branch hint for the ev6 hw_jmp/jsr (pal1e) insn */
+#define EV6HWJMPHINT (EV6HWINDEX+ 1)
+ { 8, 0, -EV6HWJMPHINT,
+ AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW,
+ insert_ev6hwjhint, extract_ev6hwjhint }
+};
+
+const unsigned alpha_num_operands = sizeof(alpha_operands)/sizeof(*alpha_operands);
+
+/* The RB field when it is the same as the RA field in the same insn.
+ This operand is marked fake. The insertion function just copies
+ the RA field into the RB field, and the extraction function just
+ checks that the fields are the same. */
+
+/*ARGSUSED*/
+static unsigned
+insert_rba(insn, value, errmsg)
+ unsigned insn;
+ int value ATTRIBUTE_UNUSED;
+ const char **errmsg ATTRIBUTE_UNUSED;
+{
+ return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static int
+extract_rba(insn, invalid)
+ unsigned insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+
+/* The same for the RC field */
+
+/*ARGSUSED*/
+static unsigned
+insert_rca(insn, value, errmsg)
+ unsigned insn;
+ int value ATTRIBUTE_UNUSED;
+ const char **errmsg ATTRIBUTE_UNUSED;
+{
+ return insn | ((insn >> 21) & 0x1f);
+}
+
+static int
+extract_rca(insn, invalid)
+ unsigned insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != (insn & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+
+/* Fake arguments in which the registers must be set to ZERO */
+
+/*ARGSUSED*/
+static unsigned
+insert_za(insn, value, errmsg)
+ unsigned insn;
+ int value ATTRIBUTE_UNUSED;
+ const char **errmsg ATTRIBUTE_UNUSED;
+{
+ return insn | (31 << 21);
+}
+
+static int
+extract_za(insn, invalid)
+ unsigned insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+/*ARGSUSED*/
+static unsigned
+insert_zb(insn, value, errmsg)
+ unsigned insn;
+ int value ATTRIBUTE_UNUSED;
+ const char **errmsg ATTRIBUTE_UNUSED;
+{
+ return insn | (31 << 16);
+}
+
+static int
+extract_zb(insn, invalid)
+ unsigned insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+/*ARGSUSED*/
+static unsigned
+insert_zc(insn, value, errmsg)
+ unsigned insn;
+ int value ATTRIBUTE_UNUSED;
+ const char **errmsg ATTRIBUTE_UNUSED;
+{
+ return insn | 31;
+}
+
+static int
+extract_zc(insn, invalid)
+ unsigned insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL && (insn & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+
+/* The displacement field of a Branch format insn. */
+
+static unsigned
+insert_bdisp(insn, value, errmsg)
+ unsigned insn;
+ int value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("branch operand unaligned");
+ return insn | ((value / 4) & 0x1FFFFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_bdisp(insn, invalid)
+ unsigned insn;
+ int *invalid ATTRIBUTE_UNUSED;
+{
+ return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000);
+}
+
+
+/* The hint field of a JMP/JSR insn. */
+
+static unsigned
+insert_jhint(insn, value, errmsg)
+ unsigned insn;
+ int value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("jump hint unaligned");
+ return insn | ((value / 4) & 0x3FFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_jhint(insn, invalid)
+ unsigned insn;
+ int *invalid ATTRIBUTE_UNUSED;
+{
+ return 4 * (((insn & 0x3FFF) ^ 0x2000) - 0x2000);
+}
+
+/* The hint field of an EV6 HW_JMP/JSR insn. */
+
+static unsigned
+insert_ev6hwjhint(insn, value, errmsg)
+ unsigned insn;
+ int value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("jump hint unaligned");
+ return insn | ((value / 4) & 0x1FFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_ev6hwjhint(insn, invalid)
+ unsigned insn;
+ int *invalid ATTRIBUTE_UNUSED;
+{
+ return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000);
+}
+
+
+/* Macros used to form opcodes */
+
+/* The main opcode */
+#define OP(x) (((x) & 0x3F) << 26)
+#define OP_MASK 0xFC000000
+
+/* Branch format instructions */
+#define BRA_(oo) OP(oo)
+#define BRA_MASK OP_MASK
+#define BRA(oo) BRA_(oo), BRA_MASK
+
+/* Floating point format instructions */
+#define FP_(oo,fff) (OP(oo) | (((fff) & 0x7FF) << 5))
+#define FP_MASK (OP_MASK | 0xFFE0)
+#define FP(oo,fff) FP_(oo,fff), FP_MASK
+
+/* Memory format instructions */
+#define MEM_(oo) OP(oo)
+#define MEM_MASK OP_MASK
+#define MEM(oo) MEM_(oo), MEM_MASK
+
+/* Memory/Func Code format instructions */
+#define MFC_(oo,ffff) (OP(oo) | ((ffff) & 0xFFFF))
+#define MFC_MASK (OP_MASK | 0xFFFF)
+#define MFC(oo,ffff) MFC_(oo,ffff), MFC_MASK
+
+/* Memory/Branch format instructions */
+#define MBR_(oo,h) (OP(oo) | (((h) & 3) << 14))
+#define MBR_MASK (OP_MASK | 0xC000)
+#define MBR(oo,h) MBR_(oo,h), MBR_MASK
+
+/* Operate format instructions. The OPRL variant specifies a
+ literal second argument. */
+#define OPR_(oo,ff) (OP(oo) | (((ff) & 0x7F) << 5))
+#define OPRL_(oo,ff) (OPR_((oo),(ff)) | 0x1000)
+#define OPR_MASK (OP_MASK | 0x1FE0)
+#define OPR(oo,ff) OPR_(oo,ff), OPR_MASK
+#define OPRL(oo,ff) OPRL_(oo,ff), OPR_MASK
+
+/* Generic PALcode format instructions */
+#define PCD_(oo) OP(oo)
+#define PCD_MASK OP_MASK
+#define PCD(oo) PCD_(oo), PCD_MASK
+
+/* Specific PALcode instructions */
+#define SPCD_(oo,ffff) (OP(oo) | ((ffff) & 0x3FFFFFF))
+#define SPCD_MASK 0xFFFFFFFF
+#define SPCD(oo,ffff) SPCD_(oo,ffff), SPCD_MASK
+
+/* Hardware memory (hw_{ld,st}) instructions */
+#define EV4HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12))
+#define EV4HWMEM_MASK (OP_MASK | 0xF000)
+#define EV4HWMEM(oo,f) EV4HWMEM_(oo,f), EV4HWMEM_MASK
+
+#define EV5HWMEM_(oo,f) (OP(oo) | (((f) & 0x3F) << 10))
+#define EV5HWMEM_MASK (OP_MASK | 0xF800)
+#define EV5HWMEM(oo,f) EV5HWMEM_(oo,f), EV5HWMEM_MASK
+
+#define EV6HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12))
+#define EV6HWMEM_MASK (OP_MASK | 0xF000)
+#define EV6HWMEM(oo,f) EV6HWMEM_(oo,f), EV6HWMEM_MASK
+
+#define EV6HWMBR_(oo,h) (OP(oo) | (((h) & 7) << 13))
+#define EV6HWMBR_MASK (OP_MASK | 0xE000)
+#define EV6HWMBR(oo,h) EV6HWMBR_(oo,h), EV6HWMBR_MASK
+
+/* Abbreviations for instruction subsets. */
+#define BASE AXP_OPCODE_BASE
+#define EV4 AXP_OPCODE_EV4
+#define EV5 AXP_OPCODE_EV5
+#define EV6 AXP_OPCODE_EV6
+#define BWX AXP_OPCODE_BWX
+#define CIX AXP_OPCODE_CIX
+#define MAX AXP_OPCODE_MAX
+
+/* Common combinations of arguments */
+#define ARG_NONE { 0 }
+#define ARG_BRA { RA, BDISP }
+#define ARG_FBRA { FA, BDISP }
+#define ARG_FP { FA, FB, DFC1 }
+#define ARG_FPZ1 { ZA, FB, DFC1 }
+#define ARG_MEM { RA, MDISP, PRB }
+#define ARG_FMEM { FA, MDISP, PRB }
+#define ARG_OPR { RA, RB, DRC1 }
+#define ARG_OPRL { RA, LIT, DRC1 }
+#define ARG_OPRZ1 { ZA, RB, DRC1 }
+#define ARG_OPRLZ1 { ZA, LIT, RC }
+#define ARG_PCD { PALFN }
+#define ARG_EV4HWMEM { RA, EV4HWDISP, PRB }
+#define ARG_EV4HWMPR { RA, RBA, EV4HWINDEX }
+#define ARG_EV5HWMEM { RA, EV5HWDISP, PRB }
+#define ARG_EV6HWMEM { RA, EV6HWDISP, PRB }
+
+/* The opcode table.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK { OPERANDS }
+
+ NAME is the name of the instruction.
+
+ OPCODE is the instruction opcode.
+
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+
+ OPERANDS is the list of operands.
+
+ The preceding macros merge the text of the OPCODE and MASK fields.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches, so this table is sorted to put more
+ specific instructions before more general instructions.
+
+ Otherwise, it is sorted by major opcode and minor function code.
+
+ There are three classes of not-really-instructions in this table:
+
+ ALIAS is another name for another instruction. Some of
+ these come from the Architecture Handbook, some
+ come from the original gas opcode tables. In all
+ cases, the functionality of the opcode is unchanged.
+
+ PSEUDO a stylized code form endorsed by Chapter A.4 of the
+ Architecture Handbook.
+
+ EXTRA a stylized code form found in the original gas tables.
+
+ And two annotations:
+
+ EV56 BUT opcodes that are officially introduced as of the ev56,
+ but with defined results on previous implementations.
+
+ EV56 UNA opcodes that were introduced as of the ev56 with
+ presumably undefined results on previous implementations
+ that were not assigned to a particular extension.
+*/
+
+const struct alpha_opcode alpha_opcodes[] = {
+ { "halt", SPCD(0x00,0x0000), BASE, ARG_NONE },
+ { "draina", SPCD(0x00,0x0002), BASE, ARG_NONE },
+ { "bpt", SPCD(0x00,0x0080), BASE, ARG_NONE },
+ { "bugchk", SPCD(0x00,0x0081), BASE, ARG_NONE },
+ { "callsys", SPCD(0x00,0x0083), BASE, ARG_NONE },
+ { "chmk", SPCD(0x00,0x0083), BASE, ARG_NONE },
+ { "imb", SPCD(0x00,0x0086), BASE, ARG_NONE },
+ { "rduniq", SPCD(0x00,0x009e), BASE, ARG_NONE },
+ { "wruniq", SPCD(0x00,0x009f), BASE, ARG_NONE },
+ { "gentrap", SPCD(0x00,0x00aa), BASE, ARG_NONE },
+ { "call_pal", PCD(0x00), BASE, ARG_PCD },
+ { "pal", PCD(0x00), BASE, ARG_PCD }, /* alias */
+
+ { "lda", MEM(0x08), BASE, { RA, MDISP, ZB } }, /* pseudo */
+ { "lda", MEM(0x08), BASE, ARG_MEM },
+ { "ldah", MEM(0x09), BASE, { RA, MDISP, ZB } }, /* pseudo */
+ { "ldah", MEM(0x09), BASE, ARG_MEM },
+ { "ldbu", MEM(0x0A), BWX, ARG_MEM },
+ { "unop", MEM_(0x0B) | (30 << 16),
+ MEM_MASK, BASE, { ZA } }, /* pseudo */
+ { "ldq_u", MEM(0x0B), BASE, ARG_MEM },
+ { "ldwu", MEM(0x0C), BWX, ARG_MEM },
+ { "stw", MEM(0x0D), BWX, ARG_MEM },
+ { "stb", MEM(0x0E), BWX, ARG_MEM },
+ { "stq_u", MEM(0x0F), BASE, ARG_MEM },
+
+ { "sextl", OPR(0x10,0x00), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "sextl", OPRL(0x10,0x00), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "addl", OPR(0x10,0x00), BASE, ARG_OPR },
+ { "addl", OPRL(0x10,0x00), BASE, ARG_OPRL },
+ { "s4addl", OPR(0x10,0x02), BASE, ARG_OPR },
+ { "s4addl", OPRL(0x10,0x02), BASE, ARG_OPRL },
+ { "negl", OPR(0x10,0x09), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negl", OPRL(0x10,0x09), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subl", OPR(0x10,0x09), BASE, ARG_OPR },
+ { "subl", OPRL(0x10,0x09), BASE, ARG_OPRL },
+ { "s4subl", OPR(0x10,0x0B), BASE, ARG_OPR },
+ { "s4subl", OPRL(0x10,0x0B), BASE, ARG_OPRL },
+ { "cmpbge", OPR(0x10,0x0F), BASE, ARG_OPR },
+ { "cmpbge", OPRL(0x10,0x0F), BASE, ARG_OPRL },
+ { "s8addl", OPR(0x10,0x12), BASE, ARG_OPR },
+ { "s8addl", OPRL(0x10,0x12), BASE, ARG_OPRL },
+ { "s8subl", OPR(0x10,0x1B), BASE, ARG_OPR },
+ { "s8subl", OPRL(0x10,0x1B), BASE, ARG_OPRL },
+ { "cmpult", OPR(0x10,0x1D), BASE, ARG_OPR },
+ { "cmpult", OPRL(0x10,0x1D), BASE, ARG_OPRL },
+ { "addq", OPR(0x10,0x20), BASE, ARG_OPR },
+ { "addq", OPRL(0x10,0x20), BASE, ARG_OPRL },
+ { "s4addq", OPR(0x10,0x22), BASE, ARG_OPR },
+ { "s4addq", OPRL(0x10,0x22), BASE, ARG_OPRL },
+ { "negq", OPR(0x10,0x29), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negq", OPRL(0x10,0x29), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subq", OPR(0x10,0x29), BASE, ARG_OPR },
+ { "subq", OPRL(0x10,0x29), BASE, ARG_OPRL },
+ { "s4subq", OPR(0x10,0x2B), BASE, ARG_OPR },
+ { "s4subq", OPRL(0x10,0x2B), BASE, ARG_OPRL },
+ { "cmpeq", OPR(0x10,0x2D), BASE, ARG_OPR },
+ { "cmpeq", OPRL(0x10,0x2D), BASE, ARG_OPRL },
+ { "s8addq", OPR(0x10,0x32), BASE, ARG_OPR },
+ { "s8addq", OPRL(0x10,0x32), BASE, ARG_OPRL },
+ { "s8subq", OPR(0x10,0x3B), BASE, ARG_OPR },
+ { "s8subq", OPRL(0x10,0x3B), BASE, ARG_OPRL },
+ { "cmpule", OPR(0x10,0x3D), BASE, ARG_OPR },
+ { "cmpule", OPRL(0x10,0x3D), BASE, ARG_OPRL },
+ { "addl/v", OPR(0x10,0x40), BASE, ARG_OPR },
+ { "addl/v", OPRL(0x10,0x40), BASE, ARG_OPRL },
+ { "negl/v", OPR(0x10,0x49), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negl/v", OPRL(0x10,0x49), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subl/v", OPR(0x10,0x49), BASE, ARG_OPR },
+ { "subl/v", OPRL(0x10,0x49), BASE, ARG_OPRL },
+ { "cmplt", OPR(0x10,0x4D), BASE, ARG_OPR },
+ { "cmplt", OPRL(0x10,0x4D), BASE, ARG_OPRL },
+ { "addq/v", OPR(0x10,0x60), BASE, ARG_OPR },
+ { "addq/v", OPRL(0x10,0x60), BASE, ARG_OPRL },
+ { "negq/v", OPR(0x10,0x69), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negq/v", OPRL(0x10,0x69), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subq/v", OPR(0x10,0x69), BASE, ARG_OPR },
+ { "subq/v", OPRL(0x10,0x69), BASE, ARG_OPRL },
+ { "cmple", OPR(0x10,0x6D), BASE, ARG_OPR },
+ { "cmple", OPRL(0x10,0x6D), BASE, ARG_OPRL },
+
+ { "and", OPR(0x11,0x00), BASE, ARG_OPR },
+ { "and", OPRL(0x11,0x00), BASE, ARG_OPRL },
+ { "andnot", OPR(0x11,0x08), BASE, ARG_OPR }, /* alias */
+ { "andnot", OPRL(0x11,0x08), BASE, ARG_OPRL }, /* alias */
+ { "bic", OPR(0x11,0x08), BASE, ARG_OPR },
+ { "bic", OPRL(0x11,0x08), BASE, ARG_OPRL },
+ { "cmovlbs", OPR(0x11,0x14), BASE, ARG_OPR },
+ { "cmovlbs", OPRL(0x11,0x14), BASE, ARG_OPRL },
+ { "cmovlbc", OPR(0x11,0x16), BASE, ARG_OPR },
+ { "cmovlbc", OPRL(0x11,0x16), BASE, ARG_OPRL },
+ { "nop", OPR(0x11,0x20), BASE, { ZA, ZB, ZC } }, /* pseudo */
+ { "clr", OPR(0x11,0x20), BASE, { ZA, ZB, RC } }, /* pseudo */
+ { "mov", OPR(0x11,0x20), BASE, { ZA, RB, RC } }, /* pseudo */
+ { "mov", OPR(0x11,0x20), BASE, { RA, RBA, RC } }, /* pseudo */
+ { "mov", OPRL(0x11,0x20), BASE, { ZA, LIT, RC } }, /* pseudo */
+ { "or", OPR(0x11,0x20), BASE, ARG_OPR }, /* alias */
+ { "or", OPRL(0x11,0x20), BASE, ARG_OPRL }, /* alias */
+ { "bis", OPR(0x11,0x20), BASE, ARG_OPR },
+ { "bis", OPRL(0x11,0x20), BASE, ARG_OPRL },
+ { "cmoveq", OPR(0x11,0x24), BASE, ARG_OPR },
+ { "cmoveq", OPRL(0x11,0x24), BASE, ARG_OPRL },
+ { "cmovne", OPR(0x11,0x26), BASE, ARG_OPR },
+ { "cmovne", OPRL(0x11,0x26), BASE, ARG_OPRL },
+ { "not", OPR(0x11,0x28), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "not", OPRL(0x11,0x28), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "ornot", OPR(0x11,0x28), BASE, ARG_OPR },
+ { "ornot", OPRL(0x11,0x28), BASE, ARG_OPRL },
+ { "xor", OPR(0x11,0x40), BASE, ARG_OPR },
+ { "xor", OPRL(0x11,0x40), BASE, ARG_OPRL },
+ { "cmovlt", OPR(0x11,0x44), BASE, ARG_OPR },
+ { "cmovlt", OPRL(0x11,0x44), BASE, ARG_OPRL },
+ { "cmovge", OPR(0x11,0x46), BASE, ARG_OPR },
+ { "cmovge", OPRL(0x11,0x46), BASE, ARG_OPRL },
+ { "eqv", OPR(0x11,0x48), BASE, ARG_OPR },
+ { "eqv", OPRL(0x11,0x48), BASE, ARG_OPRL },
+ { "xornot", OPR(0x11,0x48), BASE, ARG_OPR }, /* alias */
+ { "xornot", OPRL(0x11,0x48), BASE, ARG_OPRL }, /* alias */
+ { "amask", OPR(0x11,0x61), BASE, ARG_OPRZ1 }, /* ev56 but */
+ { "amask", OPRL(0x11,0x61), BASE, ARG_OPRLZ1 }, /* ev56 but */
+ { "cmovle", OPR(0x11,0x64), BASE, ARG_OPR },
+ { "cmovle", OPRL(0x11,0x64), BASE, ARG_OPRL },
+ { "cmovgt", OPR(0x11,0x66), BASE, ARG_OPR },
+ { "cmovgt", OPRL(0x11,0x66), BASE, ARG_OPRL },
+ { "implver", OPRL_(0x11,0x6C)|(31<<21)|(1<<13),
+ 0xFFFFFFE0, BASE, { RC } }, /* ev56 but */
+
+ { "mskbl", OPR(0x12,0x02), BASE, ARG_OPR },
+ { "mskbl", OPRL(0x12,0x02), BASE, ARG_OPRL },
+ { "extbl", OPR(0x12,0x06), BASE, ARG_OPR },
+ { "extbl", OPRL(0x12,0x06), BASE, ARG_OPRL },
+ { "insbl", OPR(0x12,0x0B), BASE, ARG_OPR },
+ { "insbl", OPRL(0x12,0x0B), BASE, ARG_OPRL },
+ { "mskwl", OPR(0x12,0x12), BASE, ARG_OPR },
+ { "mskwl", OPRL(0x12,0x12), BASE, ARG_OPRL },
+ { "extwl", OPR(0x12,0x16), BASE, ARG_OPR },
+ { "extwl", OPRL(0x12,0x16), BASE, ARG_OPRL },
+ { "inswl", OPR(0x12,0x1B), BASE, ARG_OPR },
+ { "inswl", OPRL(0x12,0x1B), BASE, ARG_OPRL },
+ { "mskll", OPR(0x12,0x22), BASE, ARG_OPR },
+ { "mskll", OPRL(0x12,0x22), BASE, ARG_OPRL },
+ { "extll", OPR(0x12,0x26), BASE, ARG_OPR },
+ { "extll", OPRL(0x12,0x26), BASE, ARG_OPRL },
+ { "insll", OPR(0x12,0x2B), BASE, ARG_OPR },
+ { "insll", OPRL(0x12,0x2B), BASE, ARG_OPRL },
+ { "zap", OPR(0x12,0x30), BASE, ARG_OPR },
+ { "zap", OPRL(0x12,0x30), BASE, ARG_OPRL },
+ { "zapnot", OPR(0x12,0x31), BASE, ARG_OPR },
+ { "zapnot", OPRL(0x12,0x31), BASE, ARG_OPRL },
+ { "mskql", OPR(0x12,0x32), BASE, ARG_OPR },
+ { "mskql", OPRL(0x12,0x32), BASE, ARG_OPRL },
+ { "srl", OPR(0x12,0x34), BASE, ARG_OPR },
+ { "srl", OPRL(0x12,0x34), BASE, ARG_OPRL },
+ { "extql", OPR(0x12,0x36), BASE, ARG_OPR },
+ { "extql", OPRL(0x12,0x36), BASE, ARG_OPRL },
+ { "sll", OPR(0x12,0x39), BASE, ARG_OPR },
+ { "sll", OPRL(0x12,0x39), BASE, ARG_OPRL },
+ { "insql", OPR(0x12,0x3B), BASE, ARG_OPR },
+ { "insql", OPRL(0x12,0x3B), BASE, ARG_OPRL },
+ { "sra", OPR(0x12,0x3C), BASE, ARG_OPR },
+ { "sra", OPRL(0x12,0x3C), BASE, ARG_OPRL },
+ { "mskwh", OPR(0x12,0x52), BASE, ARG_OPR },
+ { "mskwh", OPRL(0x12,0x52), BASE, ARG_OPRL },
+ { "inswh", OPR(0x12,0x57), BASE, ARG_OPR },
+ { "inswh", OPRL(0x12,0x57), BASE, ARG_OPRL },
+ { "extwh", OPR(0x12,0x5A), BASE, ARG_OPR },
+ { "extwh", OPRL(0x12,0x5A), BASE, ARG_OPRL },
+ { "msklh", OPR(0x12,0x62), BASE, ARG_OPR },
+ { "msklh", OPRL(0x12,0x62), BASE, ARG_OPRL },
+ { "inslh", OPR(0x12,0x67), BASE, ARG_OPR },
+ { "inslh", OPRL(0x12,0x67), BASE, ARG_OPRL },
+ { "extlh", OPR(0x12,0x6A), BASE, ARG_OPR },
+ { "extlh", OPRL(0x12,0x6A), BASE, ARG_OPRL },
+ { "mskqh", OPR(0x12,0x72), BASE, ARG_OPR },
+ { "mskqh", OPRL(0x12,0x72), BASE, ARG_OPRL },
+ { "insqh", OPR(0x12,0x77), BASE, ARG_OPR },
+ { "insqh", OPRL(0x12,0x77), BASE, ARG_OPRL },
+ { "extqh", OPR(0x12,0x7A), BASE, ARG_OPR },
+ { "extqh", OPRL(0x12,0x7A), BASE, ARG_OPRL },
+
+ { "mull", OPR(0x13,0x00), BASE, ARG_OPR },
+ { "mull", OPRL(0x13,0x00), BASE, ARG_OPRL },
+ { "mulq", OPR(0x13,0x20), BASE, ARG_OPR },
+ { "mulq", OPRL(0x13,0x20), BASE, ARG_OPRL },
+ { "umulh", OPR(0x13,0x30), BASE, ARG_OPR },
+ { "umulh", OPRL(0x13,0x30), BASE, ARG_OPRL },
+ { "mull/v", OPR(0x13,0x40), BASE, ARG_OPR },
+ { "mull/v", OPRL(0x13,0x40), BASE, ARG_OPRL },
+ { "mulq/v", OPR(0x13,0x60), BASE, ARG_OPR },
+ { "mulq/v", OPRL(0x13,0x60), BASE, ARG_OPRL },
+
+ { "itofs", FP(0x14,0x004), CIX, { RA, ZB, FC } },
+ { "sqrtf/c", FP(0x14,0x00A), CIX, ARG_FPZ1 },
+ { "sqrts/c", FP(0x14,0x00B), CIX, ARG_FPZ1 },
+ { "itoff", FP(0x14,0x014), CIX, { RA, ZB, FC } },
+ { "itoft", FP(0x14,0x024), CIX, { RA, ZB, FC } },
+ { "sqrtg/c", FP(0x14,0x02A), CIX, ARG_FPZ1 },
+ { "sqrtt/c", FP(0x14,0x02B), CIX, ARG_FPZ1 },
+ { "sqrts/m", FP(0x14,0x04B), CIX, ARG_FPZ1 },
+ { "sqrtt/m", FP(0x14,0x06B), CIX, ARG_FPZ1 },
+ { "sqrtf", FP(0x14,0x08A), CIX, ARG_FPZ1 },
+ { "sqrts", FP(0x14,0x08B), CIX, ARG_FPZ1 },
+ { "sqrtg", FP(0x14,0x0AA), CIX, ARG_FPZ1 },
+ { "sqrtt", FP(0x14,0x0AB), CIX, ARG_FPZ1 },
+ { "sqrts/d", FP(0x14,0x0CB), CIX, ARG_FPZ1 },
+ { "sqrtt/d", FP(0x14,0x0EB), CIX, ARG_FPZ1 },
+ { "sqrtf/uc", FP(0x14,0x10A), CIX, ARG_FPZ1 },
+ { "sqrts/uc", FP(0x14,0x10B), CIX, ARG_FPZ1 },
+ { "sqrtg/uc", FP(0x14,0x12A), CIX, ARG_FPZ1 },
+ { "sqrtt/uc", FP(0x14,0x12B), CIX, ARG_FPZ1 },
+ { "sqrts/um", FP(0x14,0x14B), CIX, ARG_FPZ1 },
+ { "sqrtt/um", FP(0x14,0x16B), CIX, ARG_FPZ1 },
+ { "sqrtf/u", FP(0x14,0x18A), CIX, ARG_FPZ1 },
+ { "sqrts/u", FP(0x14,0x18B), CIX, ARG_FPZ1 },
+ { "sqrtg/u", FP(0x14,0x1AA), CIX, ARG_FPZ1 },
+ { "sqrtt/u", FP(0x14,0x1AB), CIX, ARG_FPZ1 },
+ { "sqrts/ud", FP(0x14,0x1CB), CIX, ARG_FPZ1 },
+ { "sqrtt/ud", FP(0x14,0x1EB), CIX, ARG_FPZ1 },
+ { "sqrtf/sc", FP(0x14,0x40A), CIX, ARG_FPZ1 },
+ { "sqrtg/sc", FP(0x14,0x42A), CIX, ARG_FPZ1 },
+ { "sqrtf/s", FP(0x14,0x48A), CIX, ARG_FPZ1 },
+ { "sqrtg/s", FP(0x14,0x4AA), CIX, ARG_FPZ1 },
+ { "sqrtf/suc", FP(0x14,0x50A), CIX, ARG_FPZ1 },
+ { "sqrts/suc", FP(0x14,0x50B), CIX, ARG_FPZ1 },
+ { "sqrtg/suc", FP(0x14,0x52A), CIX, ARG_FPZ1 },
+ { "sqrtt/suc", FP(0x14,0x52B), CIX, ARG_FPZ1 },
+ { "sqrts/sum", FP(0x14,0x54B), CIX, ARG_FPZ1 },
+ { "sqrtt/sum", FP(0x14,0x56B), CIX, ARG_FPZ1 },
+ { "sqrtf/su", FP(0x14,0x58A), CIX, ARG_FPZ1 },
+ { "sqrts/su", FP(0x14,0x58B), CIX, ARG_FPZ1 },
+ { "sqrtg/su", FP(0x14,0x5AA), CIX, ARG_FPZ1 },
+ { "sqrtt/su", FP(0x14,0x5AB), CIX, ARG_FPZ1 },
+ { "sqrts/sud", FP(0x14,0x5CB), CIX, ARG_FPZ1 },
+ { "sqrtt/sud", FP(0x14,0x5EB), CIX, ARG_FPZ1 },
+ { "sqrts/suic", FP(0x14,0x70B), CIX, ARG_FPZ1 },
+ { "sqrtt/suic", FP(0x14,0x72B), CIX, ARG_FPZ1 },
+ { "sqrts/suim", FP(0x14,0x74B), CIX, ARG_FPZ1 },
+ { "sqrtt/suim", FP(0x14,0x76B), CIX, ARG_FPZ1 },
+ { "sqrts/sui", FP(0x14,0x78B), CIX, ARG_FPZ1 },
+ { "sqrtt/sui", FP(0x14,0x7AB), CIX, ARG_FPZ1 },
+ { "sqrts/suid", FP(0x14,0x7CB), CIX, ARG_FPZ1 },
+ { "sqrtt/suid", FP(0x14,0x7EB), CIX, ARG_FPZ1 },
+
+ { "addf/c", FP(0x15,0x000), BASE, ARG_FP },
+ { "subf/c", FP(0x15,0x001), BASE, ARG_FP },
+ { "mulf/c", FP(0x15,0x002), BASE, ARG_FP },
+ { "divf/c", FP(0x15,0x003), BASE, ARG_FP },
+ { "cvtdg/c", FP(0x15,0x01E), BASE, ARG_FPZ1 },
+ { "addg/c", FP(0x15,0x020), BASE, ARG_FP },
+ { "subg/c", FP(0x15,0x021), BASE, ARG_FP },
+ { "mulg/c", FP(0x15,0x022), BASE, ARG_FP },
+ { "divg/c", FP(0x15,0x023), BASE, ARG_FP },
+ { "cvtgf/c", FP(0x15,0x02C), BASE, ARG_FPZ1 },
+ { "cvtgd/c", FP(0x15,0x02D), BASE, ARG_FPZ1 },
+ { "cvtgq/c", FP(0x15,0x02F), BASE, ARG_FPZ1 },
+ { "cvtqf/c", FP(0x15,0x03C), BASE, ARG_FPZ1 },
+ { "cvtqg/c", FP(0x15,0x03E), BASE, ARG_FPZ1 },
+ { "addf", FP(0x15,0x080), BASE, ARG_FP },
+ { "negf", FP(0x15,0x081), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subf", FP(0x15,0x081), BASE, ARG_FP },
+ { "mulf", FP(0x15,0x082), BASE, ARG_FP },
+ { "divf", FP(0x15,0x083), BASE, ARG_FP },
+ { "cvtdg", FP(0x15,0x09E), BASE, ARG_FPZ1 },
+ { "addg", FP(0x15,0x0A0), BASE, ARG_FP },
+ { "negg", FP(0x15,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subg", FP(0x15,0x0A1), BASE, ARG_FP },
+ { "mulg", FP(0x15,0x0A2), BASE, ARG_FP },
+ { "divg", FP(0x15,0x0A3), BASE, ARG_FP },
+ { "cmpgeq", FP(0x15,0x0A5), BASE, ARG_FP },
+ { "cmpglt", FP(0x15,0x0A6), BASE, ARG_FP },
+ { "cmpgle", FP(0x15,0x0A7), BASE, ARG_FP },
+ { "cvtgf", FP(0x15,0x0AC), BASE, ARG_FPZ1 },
+ { "cvtgd", FP(0x15,0x0AD), BASE, ARG_FPZ1 },
+ { "cvtgq", FP(0x15,0x0AF), BASE, ARG_FPZ1 },
+ { "cvtqf", FP(0x15,0x0BC), BASE, ARG_FPZ1 },
+ { "cvtqg", FP(0x15,0x0BE), BASE, ARG_FPZ1 },
+ { "addf/uc", FP(0x15,0x100), BASE, ARG_FP },
+ { "subf/uc", FP(0x15,0x101), BASE, ARG_FP },
+ { "mulf/uc", FP(0x15,0x102), BASE, ARG_FP },
+ { "divf/uc", FP(0x15,0x103), BASE, ARG_FP },
+ { "cvtdg/uc", FP(0x15,0x11E), BASE, ARG_FPZ1 },
+ { "addg/uc", FP(0x15,0x120), BASE, ARG_FP },
+ { "subg/uc", FP(0x15,0x121), BASE, ARG_FP },
+ { "mulg/uc", FP(0x15,0x122), BASE, ARG_FP },
+ { "divg/uc", FP(0x15,0x123), BASE, ARG_FP },
+ { "cvtgf/uc", FP(0x15,0x12C), BASE, ARG_FPZ1 },
+ { "cvtgd/uc", FP(0x15,0x12D), BASE, ARG_FPZ1 },
+ { "cvtgq/vc", FP(0x15,0x12F), BASE, ARG_FPZ1 },
+ { "addf/u", FP(0x15,0x180), BASE, ARG_FP },
+ { "subf/u", FP(0x15,0x181), BASE, ARG_FP },
+ { "mulf/u", FP(0x15,0x182), BASE, ARG_FP },
+ { "divf/u", FP(0x15,0x183), BASE, ARG_FP },
+ { "cvtdg/u", FP(0x15,0x19E), BASE, ARG_FPZ1 },
+ { "addg/u", FP(0x15,0x1A0), BASE, ARG_FP },
+ { "subg/u", FP(0x15,0x1A1), BASE, ARG_FP },
+ { "mulg/u", FP(0x15,0x1A2), BASE, ARG_FP },
+ { "divg/u", FP(0x15,0x1A3), BASE, ARG_FP },
+ { "cvtgf/u", FP(0x15,0x1AC), BASE, ARG_FPZ1 },
+ { "cvtgd/u", FP(0x15,0x1AD), BASE, ARG_FPZ1 },
+ { "cvtgq/v", FP(0x15,0x1AF), BASE, ARG_FPZ1 },
+ { "addf/sc", FP(0x15,0x400), BASE, ARG_FP },
+ { "subf/sc", FP(0x15,0x401), BASE, ARG_FP },
+ { "mulf/sc", FP(0x15,0x402), BASE, ARG_FP },
+ { "divf/sc", FP(0x15,0x403), BASE, ARG_FP },
+ { "cvtdg/sc", FP(0x15,0x41E), BASE, ARG_FPZ1 },
+ { "addg/sc", FP(0x15,0x420), BASE, ARG_FP },
+ { "subg/sc", FP(0x15,0x421), BASE, ARG_FP },
+ { "mulg/sc", FP(0x15,0x422), BASE, ARG_FP },
+ { "divg/sc", FP(0x15,0x423), BASE, ARG_FP },
+ { "cvtgf/sc", FP(0x15,0x42C), BASE, ARG_FPZ1 },
+ { "cvtgd/sc", FP(0x15,0x42D), BASE, ARG_FPZ1 },
+ { "cvtgq/sc", FP(0x15,0x42F), BASE, ARG_FPZ1 },
+ { "addf/s", FP(0x15,0x480), BASE, ARG_FP },
+ { "negf/s", FP(0x15,0x481), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subf/s", FP(0x15,0x481), BASE, ARG_FP },
+ { "mulf/s", FP(0x15,0x482), BASE, ARG_FP },
+ { "divf/s", FP(0x15,0x483), BASE, ARG_FP },
+ { "cvtdg/s", FP(0x15,0x49E), BASE, ARG_FPZ1 },
+ { "addg/s", FP(0x15,0x4A0), BASE, ARG_FP },
+ { "negg/s", FP(0x15,0x4A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subg/s", FP(0x15,0x4A1), BASE, ARG_FP },
+ { "mulg/s", FP(0x15,0x4A2), BASE, ARG_FP },
+ { "divg/s", FP(0x15,0x4A3), BASE, ARG_FP },
+ { "cmpgeq/s", FP(0x15,0x4A5), BASE, ARG_FP },
+ { "cmpglt/s", FP(0x15,0x4A6), BASE, ARG_FP },
+ { "cmpgle/s", FP(0x15,0x4A7), BASE, ARG_FP },
+ { "cvtgf/s", FP(0x15,0x4AC), BASE, ARG_FPZ1 },
+ { "cvtgd/s", FP(0x15,0x4AD), BASE, ARG_FPZ1 },
+ { "cvtgq/s", FP(0x15,0x4AF), BASE, ARG_FPZ1 },
+ { "addf/suc", FP(0x15,0x500), BASE, ARG_FP },
+ { "subf/suc", FP(0x15,0x501), BASE, ARG_FP },
+ { "mulf/suc", FP(0x15,0x502), BASE, ARG_FP },
+ { "divf/suc", FP(0x15,0x503), BASE, ARG_FP },
+ { "cvtdg/suc", FP(0x15,0x51E), BASE, ARG_FPZ1 },
+ { "addg/suc", FP(0x15,0x520), BASE, ARG_FP },
+ { "subg/suc", FP(0x15,0x521), BASE, ARG_FP },
+ { "mulg/suc", FP(0x15,0x522), BASE, ARG_FP },
+ { "divg/suc", FP(0x15,0x523), BASE, ARG_FP },
+ { "cvtgf/suc", FP(0x15,0x52C), BASE, ARG_FPZ1 },
+ { "cvtgd/suc", FP(0x15,0x52D), BASE, ARG_FPZ1 },
+ { "cvtgq/svc", FP(0x15,0x52F), BASE, ARG_FPZ1 },
+ { "addf/su", FP(0x15,0x580), BASE, ARG_FP },
+ { "subf/su", FP(0x15,0x581), BASE, ARG_FP },
+ { "mulf/su", FP(0x15,0x582), BASE, ARG_FP },
+ { "divf/su", FP(0x15,0x583), BASE, ARG_FP },
+ { "cvtdg/su", FP(0x15,0x59E), BASE, ARG_FPZ1 },
+ { "addg/su", FP(0x15,0x5A0), BASE, ARG_FP },
+ { "subg/su", FP(0x15,0x5A1), BASE, ARG_FP },
+ { "mulg/su", FP(0x15,0x5A2), BASE, ARG_FP },
+ { "divg/su", FP(0x15,0x5A3), BASE, ARG_FP },
+ { "cvtgf/su", FP(0x15,0x5AC), BASE, ARG_FPZ1 },
+ { "cvtgd/su", FP(0x15,0x5AD), BASE, ARG_FPZ1 },
+ { "cvtgq/sv", FP(0x15,0x5AF), BASE, ARG_FPZ1 },
+
+ { "adds/c", FP(0x16,0x000), BASE, ARG_FP },
+ { "subs/c", FP(0x16,0x001), BASE, ARG_FP },
+ { "muls/c", FP(0x16,0x002), BASE, ARG_FP },
+ { "divs/c", FP(0x16,0x003), BASE, ARG_FP },
+ { "addt/c", FP(0x16,0x020), BASE, ARG_FP },
+ { "subt/c", FP(0x16,0x021), BASE, ARG_FP },
+ { "mult/c", FP(0x16,0x022), BASE, ARG_FP },
+ { "divt/c", FP(0x16,0x023), BASE, ARG_FP },
+ { "cvtts/c", FP(0x16,0x02C), BASE, ARG_FPZ1 },
+ { "cvttq/c", FP(0x16,0x02F), BASE, ARG_FPZ1 },
+ { "cvtqs/c", FP(0x16,0x03C), BASE, ARG_FPZ1 },
+ { "cvtqt/c", FP(0x16,0x03E), BASE, ARG_FPZ1 },
+ { "adds/m", FP(0x16,0x040), BASE, ARG_FP },
+ { "subs/m", FP(0x16,0x041), BASE, ARG_FP },
+ { "muls/m", FP(0x16,0x042), BASE, ARG_FP },
+ { "divs/m", FP(0x16,0x043), BASE, ARG_FP },
+ { "addt/m", FP(0x16,0x060), BASE, ARG_FP },
+ { "subt/m", FP(0x16,0x061), BASE, ARG_FP },
+ { "mult/m", FP(0x16,0x062), BASE, ARG_FP },
+ { "divt/m", FP(0x16,0x063), BASE, ARG_FP },
+ { "cvtts/m", FP(0x16,0x06C), BASE, ARG_FPZ1 },
+ { "cvttq/m", FP(0x16,0x06F), BASE, ARG_FPZ1 },
+ { "cvtqs/m", FP(0x16,0x07C), BASE, ARG_FPZ1 },
+ { "cvtqt/m", FP(0x16,0x07E), BASE, ARG_FPZ1 },
+ { "adds", FP(0x16,0x080), BASE, ARG_FP },
+ { "negs", FP(0x16,0x081), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs", FP(0x16,0x081), BASE, ARG_FP },
+ { "muls", FP(0x16,0x082), BASE, ARG_FP },
+ { "divs", FP(0x16,0x083), BASE, ARG_FP },
+ { "addt", FP(0x16,0x0A0), BASE, ARG_FP },
+ { "negt", FP(0x16,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt", FP(0x16,0x0A1), BASE, ARG_FP },
+ { "mult", FP(0x16,0x0A2), BASE, ARG_FP },
+ { "divt", FP(0x16,0x0A3), BASE, ARG_FP },
+ { "cmptun", FP(0x16,0x0A4), BASE, ARG_FP },
+ { "cmpteq", FP(0x16,0x0A5), BASE, ARG_FP },
+ { "cmptlt", FP(0x16,0x0A6), BASE, ARG_FP },
+ { "cmptle", FP(0x16,0x0A7), BASE, ARG_FP },
+ { "cvtts", FP(0x16,0x0AC), BASE, ARG_FPZ1 },
+ { "cvttq", FP(0x16,0x0AF), BASE, ARG_FPZ1 },
+ { "cvtqs", FP(0x16,0x0BC), BASE, ARG_FPZ1 },
+ { "cvtqt", FP(0x16,0x0BE), BASE, ARG_FPZ1 },
+ { "adds/d", FP(0x16,0x0C0), BASE, ARG_FP },
+ { "subs/d", FP(0x16,0x0C1), BASE, ARG_FP },
+ { "muls/d", FP(0x16,0x0C2), BASE, ARG_FP },
+ { "divs/d", FP(0x16,0x0C3), BASE, ARG_FP },
+ { "addt/d", FP(0x16,0x0E0), BASE, ARG_FP },
+ { "subt/d", FP(0x16,0x0E1), BASE, ARG_FP },
+ { "mult/d", FP(0x16,0x0E2), BASE, ARG_FP },
+ { "divt/d", FP(0x16,0x0E3), BASE, ARG_FP },
+ { "cvtts/d", FP(0x16,0x0EC), BASE, ARG_FPZ1 },
+ { "cvttq/d", FP(0x16,0x0EF), BASE, ARG_FPZ1 },
+ { "cvtqs/d", FP(0x16,0x0FC), BASE, ARG_FPZ1 },
+ { "cvtqt/d", FP(0x16,0x0FE), BASE, ARG_FPZ1 },
+ { "adds/uc", FP(0x16,0x100), BASE, ARG_FP },
+ { "subs/uc", FP(0x16,0x101), BASE, ARG_FP },
+ { "muls/uc", FP(0x16,0x102), BASE, ARG_FP },
+ { "divs/uc", FP(0x16,0x103), BASE, ARG_FP },
+ { "addt/uc", FP(0x16,0x120), BASE, ARG_FP },
+ { "subt/uc", FP(0x16,0x121), BASE, ARG_FP },
+ { "mult/uc", FP(0x16,0x122), BASE, ARG_FP },
+ { "divt/uc", FP(0x16,0x123), BASE, ARG_FP },
+ { "cvtts/uc", FP(0x16,0x12C), BASE, ARG_FPZ1 },
+ { "cvttq/vc", FP(0x16,0x12F), BASE, ARG_FPZ1 },
+ { "adds/um", FP(0x16,0x140), BASE, ARG_FP },
+ { "subs/um", FP(0x16,0x141), BASE, ARG_FP },
+ { "muls/um", FP(0x16,0x142), BASE, ARG_FP },
+ { "divs/um", FP(0x16,0x143), BASE, ARG_FP },
+ { "addt/um", FP(0x16,0x160), BASE, ARG_FP },
+ { "subt/um", FP(0x16,0x161), BASE, ARG_FP },
+ { "mult/um", FP(0x16,0x162), BASE, ARG_FP },
+ { "divt/um", FP(0x16,0x163), BASE, ARG_FP },
+ { "cvtts/um", FP(0x16,0x16C), BASE, ARG_FPZ1 },
+ { "cvttq/vm", FP(0x16,0x16F), BASE, ARG_FPZ1 },
+ { "adds/u", FP(0x16,0x180), BASE, ARG_FP },
+ { "subs/u", FP(0x16,0x181), BASE, ARG_FP },
+ { "muls/u", FP(0x16,0x182), BASE, ARG_FP },
+ { "divs/u", FP(0x16,0x183), BASE, ARG_FP },
+ { "addt/u", FP(0x16,0x1A0), BASE, ARG_FP },
+ { "subt/u", FP(0x16,0x1A1), BASE, ARG_FP },
+ { "mult/u", FP(0x16,0x1A2), BASE, ARG_FP },
+ { "divt/u", FP(0x16,0x1A3), BASE, ARG_FP },
+ { "cvtts/u", FP(0x16,0x1AC), BASE, ARG_FPZ1 },
+ { "cvttq/v", FP(0x16,0x1AF), BASE, ARG_FPZ1 },
+ { "adds/ud", FP(0x16,0x1C0), BASE, ARG_FP },
+ { "subs/ud", FP(0x16,0x1C1), BASE, ARG_FP },
+ { "muls/ud", FP(0x16,0x1C2), BASE, ARG_FP },
+ { "divs/ud", FP(0x16,0x1C3), BASE, ARG_FP },
+ { "addt/ud", FP(0x16,0x1E0), BASE, ARG_FP },
+ { "subt/ud", FP(0x16,0x1E1), BASE, ARG_FP },
+ { "mult/ud", FP(0x16,0x1E2), BASE, ARG_FP },
+ { "divt/ud", FP(0x16,0x1E3), BASE, ARG_FP },
+ { "cvtts/ud", FP(0x16,0x1EC), BASE, ARG_FPZ1 },
+ { "cvttq/vd", FP(0x16,0x1EF), BASE, ARG_FPZ1 },
+ { "cvtst", FP(0x16,0x2AC), BASE, ARG_FPZ1 },
+ { "adds/suc", FP(0x16,0x500), BASE, ARG_FP },
+ { "subs/suc", FP(0x16,0x501), BASE, ARG_FP },
+ { "muls/suc", FP(0x16,0x502), BASE, ARG_FP },
+ { "divs/suc", FP(0x16,0x503), BASE, ARG_FP },
+ { "addt/suc", FP(0x16,0x520), BASE, ARG_FP },
+ { "subt/suc", FP(0x16,0x521), BASE, ARG_FP },
+ { "mult/suc", FP(0x16,0x522), BASE, ARG_FP },
+ { "divt/suc", FP(0x16,0x523), BASE, ARG_FP },
+ { "cvtts/suc", FP(0x16,0x52C), BASE, ARG_FPZ1 },
+ { "cvttq/svc", FP(0x16,0x52F), BASE, ARG_FPZ1 },
+ { "adds/sum", FP(0x16,0x540), BASE, ARG_FP },
+ { "subs/sum", FP(0x16,0x541), BASE, ARG_FP },
+ { "muls/sum", FP(0x16,0x542), BASE, ARG_FP },
+ { "divs/sum", FP(0x16,0x543), BASE, ARG_FP },
+ { "addt/sum", FP(0x16,0x560), BASE, ARG_FP },
+ { "subt/sum", FP(0x16,0x561), BASE, ARG_FP },
+ { "mult/sum", FP(0x16,0x562), BASE, ARG_FP },
+ { "divt/sum", FP(0x16,0x563), BASE, ARG_FP },
+ { "cvtts/sum", FP(0x16,0x56C), BASE, ARG_FPZ1 },
+ { "cvttq/svm", FP(0x16,0x56F), BASE, ARG_FPZ1 },
+ { "adds/su", FP(0x16,0x580), BASE, ARG_FP },
+ { "negs/su", FP(0x16,0x581), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs/su", FP(0x16,0x581), BASE, ARG_FP },
+ { "muls/su", FP(0x16,0x582), BASE, ARG_FP },
+ { "divs/su", FP(0x16,0x583), BASE, ARG_FP },
+ { "addt/su", FP(0x16,0x5A0), BASE, ARG_FP },
+ { "negt/su", FP(0x16,0x5A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt/su", FP(0x16,0x5A1), BASE, ARG_FP },
+ { "mult/su", FP(0x16,0x5A2), BASE, ARG_FP },
+ { "divt/su", FP(0x16,0x5A3), BASE, ARG_FP },
+ { "cmptun/su", FP(0x16,0x5A4), BASE, ARG_FP },
+ { "cmpteq/su", FP(0x16,0x5A5), BASE, ARG_FP },
+ { "cmptlt/su", FP(0x16,0x5A6), BASE, ARG_FP },
+ { "cmptle/su", FP(0x16,0x5A7), BASE, ARG_FP },
+ { "cvtts/su", FP(0x16,0x5AC), BASE, ARG_FPZ1 },
+ { "cvttq/sv", FP(0x16,0x5AF), BASE, ARG_FPZ1 },
+ { "adds/sud", FP(0x16,0x5C0), BASE, ARG_FP },
+ { "subs/sud", FP(0x16,0x5C1), BASE, ARG_FP },
+ { "muls/sud", FP(0x16,0x5C2), BASE, ARG_FP },
+ { "divs/sud", FP(0x16,0x5C3), BASE, ARG_FP },
+ { "addt/sud", FP(0x16,0x5E0), BASE, ARG_FP },
+ { "subt/sud", FP(0x16,0x5E1), BASE, ARG_FP },
+ { "mult/sud", FP(0x16,0x5E2), BASE, ARG_FP },
+ { "divt/sud", FP(0x16,0x5E3), BASE, ARG_FP },
+ { "cvtts/sud", FP(0x16,0x5EC), BASE, ARG_FPZ1 },
+ { "cvttq/svd", FP(0x16,0x5EF), BASE, ARG_FPZ1 },
+ { "cvtst/s", FP(0x16,0x6AC), BASE, ARG_FPZ1 },
+ { "adds/suic", FP(0x16,0x700), BASE, ARG_FP },
+ { "subs/suic", FP(0x16,0x701), BASE, ARG_FP },
+ { "muls/suic", FP(0x16,0x702), BASE, ARG_FP },
+ { "divs/suic", FP(0x16,0x703), BASE, ARG_FP },
+ { "addt/suic", FP(0x16,0x720), BASE, ARG_FP },
+ { "subt/suic", FP(0x16,0x721), BASE, ARG_FP },
+ { "mult/suic", FP(0x16,0x722), BASE, ARG_FP },
+ { "divt/suic", FP(0x16,0x723), BASE, ARG_FP },
+ { "cvtts/suic", FP(0x16,0x72C), BASE, ARG_FPZ1 },
+ { "cvttq/svic", FP(0x16,0x72F), BASE, ARG_FPZ1 },
+ { "cvtqs/suic", FP(0x16,0x73C), BASE, ARG_FPZ1 },
+ { "cvtqt/suic", FP(0x16,0x73E), BASE, ARG_FPZ1 },
+ { "adds/suim", FP(0x16,0x740), BASE, ARG_FP },
+ { "subs/suim", FP(0x16,0x741), BASE, ARG_FP },
+ { "muls/suim", FP(0x16,0x742), BASE, ARG_FP },
+ { "divs/suim", FP(0x16,0x743), BASE, ARG_FP },
+ { "addt/suim", FP(0x16,0x760), BASE, ARG_FP },
+ { "subt/suim", FP(0x16,0x761), BASE, ARG_FP },
+ { "mult/suim", FP(0x16,0x762), BASE, ARG_FP },
+ { "divt/suim", FP(0x16,0x763), BASE, ARG_FP },
+ { "cvtts/suim", FP(0x16,0x76C), BASE, ARG_FPZ1 },
+ { "cvttq/svim", FP(0x16,0x76F), BASE, ARG_FPZ1 },
+ { "cvtqs/suim", FP(0x16,0x77C), BASE, ARG_FPZ1 },
+ { "cvtqt/suim", FP(0x16,0x77E), BASE, ARG_FPZ1 },
+ { "adds/sui", FP(0x16,0x780), BASE, ARG_FP },
+ { "negs/sui", FP(0x16,0x781), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs/sui", FP(0x16,0x781), BASE, ARG_FP },
+ { "muls/sui", FP(0x16,0x782), BASE, ARG_FP },
+ { "divs/sui", FP(0x16,0x783), BASE, ARG_FP },
+ { "addt/sui", FP(0x16,0x7A0), BASE, ARG_FP },
+ { "negt/sui", FP(0x16,0x7A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt/sui", FP(0x16,0x7A1), BASE, ARG_FP },
+ { "mult/sui", FP(0x16,0x7A2), BASE, ARG_FP },
+ { "divt/sui", FP(0x16,0x7A3), BASE, ARG_FP },
+ { "cvtts/sui", FP(0x16,0x7AC), BASE, ARG_FPZ1 },
+ { "cvttq/svi", FP(0x16,0x7AF), BASE, ARG_FPZ1 },
+ { "cvtqs/sui", FP(0x16,0x7BC), BASE, ARG_FPZ1 },
+ { "cvtqt/sui", FP(0x16,0x7BE), BASE, ARG_FPZ1 },
+ { "adds/suid", FP(0x16,0x7C0), BASE, ARG_FP },
+ { "subs/suid", FP(0x16,0x7C1), BASE, ARG_FP },
+ { "muls/suid", FP(0x16,0x7C2), BASE, ARG_FP },
+ { "divs/suid", FP(0x16,0x7C3), BASE, ARG_FP },
+ { "addt/suid", FP(0x16,0x7E0), BASE, ARG_FP },
+ { "subt/suid", FP(0x16,0x7E1), BASE, ARG_FP },
+ { "mult/suid", FP(0x16,0x7E2), BASE, ARG_FP },
+ { "divt/suid", FP(0x16,0x7E3), BASE, ARG_FP },
+ { "cvtts/suid", FP(0x16,0x7EC), BASE, ARG_FPZ1 },
+ { "cvttq/svid", FP(0x16,0x7EF), BASE, ARG_FPZ1 },
+ { "cvtqs/suid", FP(0x16,0x7FC), BASE, ARG_FPZ1 },
+ { "cvtqt/suid", FP(0x16,0x7FE), BASE, ARG_FPZ1 },
+
+ { "cvtlq", FP(0x17,0x010), BASE, ARG_FPZ1 },
+ { "fnop", FP(0x17,0x020), BASE, { ZA, ZB, ZC } }, /* pseudo */
+ { "fclr", FP(0x17,0x020), BASE, { ZA, ZB, FC } }, /* pseudo */
+ { "fabs", FP(0x17,0x020), BASE, ARG_FPZ1 }, /* pseudo */
+ { "fmov", FP(0x17,0x020), BASE, { FA, RBA, FC } }, /* pseudo */
+ { "cpys", FP(0x17,0x020), BASE, ARG_FP },
+ { "fneg", FP(0x17,0x021), BASE, { FA, RBA, FC } }, /* pseudo */
+ { "cpysn", FP(0x17,0x021), BASE, ARG_FP },
+ { "cpyse", FP(0x17,0x022), BASE, ARG_FP },
+ { "mt_fpcr", FP(0x17,0x024), BASE, { FA, RBA, RCA } },
+ { "mf_fpcr", FP(0x17,0x025), BASE, { FA, RBA, RCA } },
+ { "fcmoveq", FP(0x17,0x02A), BASE, ARG_FP },
+ { "fcmovne", FP(0x17,0x02B), BASE, ARG_FP },
+ { "fcmovlt", FP(0x17,0x02C), BASE, ARG_FP },
+ { "fcmovge", FP(0x17,0x02D), BASE, ARG_FP },
+ { "fcmovle", FP(0x17,0x02E), BASE, ARG_FP },
+ { "fcmovgt", FP(0x17,0x02F), BASE, ARG_FP },
+ { "cvtql", FP(0x17,0x030), BASE, ARG_FPZ1 },
+ { "cvtql/v", FP(0x17,0x130), BASE, ARG_FPZ1 },
+ { "cvtql/sv", FP(0x17,0x530), BASE, ARG_FPZ1 },
+
+ { "trapb", MFC(0x18,0x0000), BASE, ARG_NONE },
+ { "draint", MFC(0x18,0x0000), BASE, ARG_NONE }, /* alias */
+ { "excb", MFC(0x18,0x0400), BASE, ARG_NONE },
+ { "mb", MFC(0x18,0x4000), BASE, ARG_NONE },
+ { "wmb", MFC(0x18,0x4400), BASE, ARG_NONE },
+ { "fetch", MFC(0x18,0x8000), BASE, { ZA, PRB } },
+ { "fetch_m", MFC(0x18,0xA000), BASE, { ZA, PRB } },
+ { "rpcc", MFC(0x18,0xC000), BASE, { RA } },
+ { "rc", MFC(0x18,0xE000), BASE, { RA } },
+ { "ecb", MFC(0x18,0xE800), BASE, { ZA, PRB } }, /* ev56 una */
+ { "rs", MFC(0x18,0xF000), BASE, { RA } },
+ { "wh64", MFC(0x18,0xF800), BASE, { ZA, PRB } }, /* ev56 una */
+ { "wh64en", MFC(0x18,0xFC00), BASE, { ZA, PRB } }, /* ev7 una */
+
+ { "hw_mfpr", OPR(0x19,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } },
+ { "hw_mfpr", OP(0x19), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } },
+ { "hw_mfpr", OP(0x19), OP_MASK, EV6, { RA, ZB, EV6HWINDEX } },
+ { "hw_mfpr/i", OPR(0x19,0x01), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/a", OPR(0x19,0x02), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/ai", OPR(0x19,0x03), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/p", OPR(0x19,0x04), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pi", OPR(0x19,0x05), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pa", OPR(0x19,0x06), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pai", OPR(0x19,0x07), EV4, ARG_EV4HWMPR },
+ { "pal19", PCD(0x19), BASE, ARG_PCD },
+
+ { "jmp", MBR_(0x1A,0), MBR_MASK | 0x3FFF, /* pseudo */
+ BASE, { ZA, CPRB } },
+ { "jmp", MBR(0x1A,0), BASE, { RA, CPRB, JMPHINT } },
+ { "jsr", MBR(0x1A,1), BASE, { RA, CPRB, JMPHINT } },
+ { "ret", MBR_(0x1A,2) | (31 << 21) | (26 << 16) | 1,/* pseudo */
+ 0xFFFFFFFF, BASE, { 0 } },
+ { "ret", MBR(0x1A,2), BASE, { RA, CPRB, RETHINT } },
+ { "jcr", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } }, /* alias */
+ { "jsr_coroutine", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } },
+
+ { "hw_ldl", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_ldl", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_ldl", EV6HWMEM(0x1B,0x8), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/a", EV6HWMEM(0x1B,0xC), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/p", EV6HWMEM(0x1B,0x0), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/v", EV6HWMEM(0x1B,0x4), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/w", EV6HWMEM(0x1B,0xA), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/wa", EV6HWMEM(0x1B,0xE), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/a", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/av", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/aw", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/awv", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/p", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/p", EV6HWMEM(0x1B,0x2), EV6, ARG_EV6HWMEM },
+ { "hw_ldl_l/pa", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pav", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/paw", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pawv", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pv", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pw", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pwv", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/v", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/w", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/wv", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "hw_ldq", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_ldq", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_ldq", EV6HWMEM(0x1B,0x9), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/a", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/a", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/a", EV6HWMEM(0x1B,0xD), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/al", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/ar", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/av", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/avl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/aw", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awl", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/p", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/p", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/p", EV6HWMEM(0x1B,0x1), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/pa", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pa", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pal", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/par", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pav", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pavl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/paw", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawl", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pl", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pr", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pw", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwl", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/r", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/v", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/v", EV6HWMEM(0x1B,0x5), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/vl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/w", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/w", EV6HWMEM(0x1B,0xB), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/wa", EV6HWMEM(0x1B,0xF), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/wl", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/wv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/wvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/a", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/av", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/aw", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/awv", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/p", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/p", EV6HWMEM(0x1B,0x3), EV6, ARG_EV6HWMEM },
+ { "hw_ldq_l/pa", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pav", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/paw", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pawv", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pv", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pw", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pwv", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/v", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/w", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/wv", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ld", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_ld", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_ld/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_ld/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_ld/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aq", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_ld/aq", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aql", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aqv", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aqvl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ld/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_ld/arq", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_ld/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_ld/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awq", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awql", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awqv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awqvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ld/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_ld/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paq", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_ld/paq", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paql", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paqv", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paqvl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ld/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_ld/parq", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawq", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawql", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawqv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawqvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pq", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pq", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pql", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pqv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pqvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_ld/prq", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwq", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwql", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwqv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwqvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/q", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_ld/q", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_ld/ql", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ld/qv", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_ld/qvl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ld/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_ld/rq", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_ld/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_ld/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ld/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wq", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wql", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wqv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wqvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "pal1b", PCD(0x1B), BASE, ARG_PCD },
+
+ { "sextb", OPR(0x1C, 0x00), BWX, ARG_OPRZ1 },
+ { "sextw", OPR(0x1C, 0x01), BWX, ARG_OPRZ1 },
+ { "ctpop", OPR(0x1C, 0x30), CIX, ARG_OPRZ1 },
+ { "perr", OPR(0x1C, 0x31), MAX, ARG_OPR },
+ { "ctlz", OPR(0x1C, 0x32), CIX, ARG_OPRZ1 },
+ { "cttz", OPR(0x1C, 0x33), CIX, ARG_OPRZ1 },
+ { "unpkbw", OPR(0x1C, 0x34), MAX, ARG_OPRZ1 },
+ { "unpkbl", OPR(0x1C, 0x35), MAX, ARG_OPRZ1 },
+ { "pkwb", OPR(0x1C, 0x36), MAX, ARG_OPRZ1 },
+ { "pklb", OPR(0x1C, 0x37), MAX, ARG_OPRZ1 },
+ { "minsb8", OPR(0x1C, 0x38), MAX, ARG_OPR },
+ { "minsb8", OPRL(0x1C, 0x38), MAX, ARG_OPRL },
+ { "minsw4", OPR(0x1C, 0x39), MAX, ARG_OPR },
+ { "minsw4", OPRL(0x1C, 0x39), MAX, ARG_OPRL },
+ { "minub8", OPR(0x1C, 0x3A), MAX, ARG_OPR },
+ { "minub8", OPRL(0x1C, 0x3A), MAX, ARG_OPRL },
+ { "minuw4", OPR(0x1C, 0x3B), MAX, ARG_OPR },
+ { "minuw4", OPRL(0x1C, 0x3B), MAX, ARG_OPRL },
+ { "maxub8", OPR(0x1C, 0x3C), MAX, ARG_OPR },
+ { "maxub8", OPRL(0x1C, 0x3C), MAX, ARG_OPRL },
+ { "maxuw4", OPR(0x1C, 0x3D), MAX, ARG_OPR },
+ { "maxuw4", OPRL(0x1C, 0x3D), MAX, ARG_OPRL },
+ { "maxsb8", OPR(0x1C, 0x3E), MAX, ARG_OPR },
+ { "maxsb8", OPRL(0x1C, 0x3E), MAX, ARG_OPRL },
+ { "maxsw4", OPR(0x1C, 0x3F), MAX, ARG_OPR },
+ { "maxsw4", OPRL(0x1C, 0x3F), MAX, ARG_OPRL },
+ { "ftoit", FP(0x1C, 0x70), CIX, { FA, ZB, RC } },
+ { "ftois", FP(0x1C, 0x78), CIX, { FA, ZB, RC } },
+
+ { "hw_mtpr", OPR(0x1D,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } },
+ { "hw_mtpr", OP(0x1D), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } },
+ { "hw_mtpr", OP(0x1D), OP_MASK, EV6, { ZA, RB, EV6HWINDEX } },
+ { "hw_mtpr/i", OPR(0x1D,0x01), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/a", OPR(0x1D,0x02), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/ai", OPR(0x1D,0x03), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/p", OPR(0x1D,0x04), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pi", OPR(0x1D,0x05), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pa", OPR(0x1D,0x06), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pai", OPR(0x1D,0x07), EV4, ARG_EV4HWMPR },
+ { "pal1d", PCD(0x1D), BASE, ARG_PCD },
+
+ { "hw_rei", SPCD(0x1E,0x3FF8000), EV4|EV5, ARG_NONE },
+ { "hw_rei_stall", SPCD(0x1E,0x3FFC000), EV5, ARG_NONE },
+ { "hw_jmp", EV6HWMBR(0x1E,0x0), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_jsr", EV6HWMBR(0x1E,0x2), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_ret", EV6HWMBR(0x1E,0x4), EV6, { ZA, PRB } },
+ { "hw_jcr", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } },
+ { "hw_coroutine", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } }, /* alias */
+ { "hw_jmp/stall", EV6HWMBR(0x1E,0x1), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_jsr/stall", EV6HWMBR(0x1E,0x3), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_ret/stall", EV6HWMBR(0x1E,0x5), EV6, { ZA, PRB } },
+ { "hw_jcr/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } },
+ { "hw_coroutine/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } }, /* alias */
+ { "pal1e", PCD(0x1E), BASE, ARG_PCD },
+
+ { "hw_stl", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_stl", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_stl", EV6HWMEM(0x1F,0x4), EV6, ARG_EV6HWMEM }, /* ??? 8 */
+ { "hw_stl/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_stl/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_stl/a", EV6HWMEM(0x1F,0xC), EV6, ARG_EV6HWMEM },
+ { "hw_stl/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_stl/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_stl/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_stl/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_stl/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_stl/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_stl/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_stl/p", EV6HWMEM(0x1F,0x0), EV6, ARG_EV6HWMEM },
+ { "hw_stl/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_stl/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_stl/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_stl/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_stl/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_stl/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/a", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/av", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/p", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/p", EV6HWMEM(0x1F,0x2), EV6, ARG_EV6HWMEM },
+ { "hw_stl_c/pa", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/pav", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/pv", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/v", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_stq", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_stq", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_stq", EV6HWMEM(0x1F,0x5), EV6, ARG_EV6HWMEM }, /* ??? 9 */
+ { "hw_stq/a", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_stq/a", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_stq/a", EV6HWMEM(0x1F,0xD), EV6, ARG_EV6HWMEM },
+ { "hw_stq/ac", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_stq/ar", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_stq/av", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_stq/avc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_stq/c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_stq/p", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_stq/p", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_stq/p", EV6HWMEM(0x1F,0x1), EV6, ARG_EV6HWMEM },
+ { "hw_stq/pa", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pa", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pac", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_stq/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_stq/par", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pav", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pavc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pr", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_stq/r", EV4HWMEM(0x1F,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_stq/v", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_stq/vc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/a", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/av", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/p", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/p", EV6HWMEM(0x1F,0x3), EV6, ARG_EV6HWMEM },
+ { "hw_stq_c/pa", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/pav", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/pv", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/v", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_st", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_st", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_st/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_st/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_st/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_st/aq", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_st/aq", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqc", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqv", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqvc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_st/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_st/arq", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_st/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_st/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_st/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_st/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_st/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_st/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_st/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_st/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_st/paq", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_st/paq", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqc", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqv", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqvc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_st/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_st/parq", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_st/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_st/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_st/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_st/pq", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_st/pq", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_st/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_st/prq", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_st/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_st/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_st/q", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_st/q", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_st/qc", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_st/qv", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_st/qvc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_st/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_st/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_st/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "pal1f", PCD(0x1F), BASE, ARG_PCD },
+
+ { "ldf", MEM(0x20), BASE, ARG_FMEM },
+ { "ldg", MEM(0x21), BASE, ARG_FMEM },
+ { "lds", MEM(0x22), BASE, ARG_FMEM },
+ { "ldt", MEM(0x23), BASE, ARG_FMEM },
+ { "stf", MEM(0x24), BASE, ARG_FMEM },
+ { "stg", MEM(0x25), BASE, ARG_FMEM },
+ { "sts", MEM(0x26), BASE, ARG_FMEM },
+ { "stt", MEM(0x27), BASE, ARG_FMEM },
+
+ { "ldl", MEM(0x28), BASE, ARG_MEM },
+ { "ldq", MEM(0x29), BASE, ARG_MEM },
+ { "ldl_l", MEM(0x2A), BASE, ARG_MEM },
+ { "ldq_l", MEM(0x2B), BASE, ARG_MEM },
+ { "stl", MEM(0x2C), BASE, ARG_MEM },
+ { "stq", MEM(0x2D), BASE, ARG_MEM },
+ { "stl_c", MEM(0x2E), BASE, ARG_MEM },
+ { "stq_c", MEM(0x2F), BASE, ARG_MEM },
+
+ { "br", BRA(0x30), BASE, { ZA, BDISP } }, /* pseudo */
+ { "br", BRA(0x30), BASE, ARG_BRA },
+ { "fbeq", BRA(0x31), BASE, ARG_FBRA },
+ { "fblt", BRA(0x32), BASE, ARG_FBRA },
+ { "fble", BRA(0x33), BASE, ARG_FBRA },
+ { "bsr", BRA(0x34), BASE, ARG_BRA },
+ { "fbne", BRA(0x35), BASE, ARG_FBRA },
+ { "fbge", BRA(0x36), BASE, ARG_FBRA },
+ { "fbgt", BRA(0x37), BASE, ARG_FBRA },
+ { "blbc", BRA(0x38), BASE, ARG_BRA },
+ { "beq", BRA(0x39), BASE, ARG_BRA },
+ { "blt", BRA(0x3A), BASE, ARG_BRA },
+ { "ble", BRA(0x3B), BASE, ARG_BRA },
+ { "blbs", BRA(0x3C), BASE, ARG_BRA },
+ { "bne", BRA(0x3D), BASE, ARG_BRA },
+ { "bge", BRA(0x3E), BASE, ARG_BRA },
+ { "bgt", BRA(0x3F), BASE, ARG_BRA },
+};
+
+const unsigned alpha_num_opcodes = sizeof(alpha_opcodes)/sizeof(*alpha_opcodes);
+
+/* OSF register names. */
+
+static const char * const osf_regnames[64] = {
+ "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
+ "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp",
+ "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9",
+ "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero",
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
+};
+
+/* VMS register names. */
+
+static const char * const vms_regnames[64] = {
+ "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
+ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
+ "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
+ "R24", "AI", "RA", "PV", "AT", "FP", "SP", "RZ",
+ "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
+ "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
+ "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23",
+ "F24", "F25", "F26", "F27", "F28", "F29", "F30", "FZ"
+};
+
+/* Disassemble Alpha instructions. */
+
+int
+print_insn_alpha (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ static const struct alpha_opcode *opcode_index[AXP_NOPS+1];
+ const char * const * regnames;
+ const struct alpha_opcode *opcode, *opcode_end;
+ const unsigned char *opindex;
+ unsigned insn, op, isa_mask;
+ int need_comma;
+
+ /* Initialize the majorop table the first time through */
+ if (!opcode_index[0])
+ {
+ opcode = alpha_opcodes;
+ opcode_end = opcode + alpha_num_opcodes;
+
+ for (op = 0; op < AXP_NOPS; ++op)
+ {
+ opcode_index[op] = opcode;
+ while (opcode < opcode_end && op == AXP_OP (opcode->opcode))
+ ++opcode;
+ }
+ opcode_index[op] = opcode;
+ }
+
+ if (info->flavour == bfd_target_evax_flavour)
+ regnames = vms_regnames;
+ else
+ regnames = osf_regnames;
+
+ isa_mask = AXP_OPCODE_NOPAL;
+ switch (info->mach)
+ {
+ case bfd_mach_alpha_ev4:
+ isa_mask |= AXP_OPCODE_EV4;
+ break;
+ case bfd_mach_alpha_ev5:
+ isa_mask |= AXP_OPCODE_EV5;
+ break;
+ case bfd_mach_alpha_ev6:
+ isa_mask |= AXP_OPCODE_EV6;
+ break;
+ }
+
+ /* Read the insn into a host word */
+ {
+ bfd_byte buffer[4];
+ int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ insn = bfd_getl32 (buffer);
+ }
+
+ /* Get the major opcode of the instruction. */
+ op = AXP_OP (insn);
+
+ /* Find the first match in the opcode table. */
+ opcode_end = opcode_index[op + 1];
+ for (opcode = opcode_index[op]; opcode < opcode_end; ++opcode)
+ {
+ if ((insn ^ opcode->opcode) & opcode->mask)
+ continue;
+
+ if (!(opcode->flags & isa_mask))
+ continue;
+
+ /* Make two passes over the operands. First see if any of them
+ have extraction functions, and, if they do, make sure the
+ instruction is valid. */
+ {
+ int invalid = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ const struct alpha_operand *operand = alpha_operands + *opindex;
+ if (operand->extract)
+ (*operand->extract) (insn, &invalid);
+ }
+ if (invalid)
+ continue;
+ }
+
+ /* The instruction is valid. */
+ goto found;
+ }
+
+ /* No instruction found */
+ (*info->fprintf_func) (info->stream, ".long %#08x", insn);
+
+ return 4;
+
+found:
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+ if (opcode->operands[0] != 0)
+ (*info->fprintf_func) (info->stream, "\t");
+
+ /* Now extract and print the operands. */
+ need_comma = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ const struct alpha_operand *operand = alpha_operands + *opindex;
+ int value;
+
+ /* Operands that are marked FAKE are simply ignored. We
+ already made sure that the extract function considered
+ the instruction to be valid. */
+ if ((operand->flags & AXP_OPERAND_FAKE) != 0)
+ continue;
+
+ /* Extract the value from the instruction. */
+ if (operand->extract)
+ value = (*operand->extract) (insn, (int *) NULL);
+ else
+ {
+ value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+ if (operand->flags & AXP_OPERAND_SIGNED)
+ {
+ int signbit = 1 << (operand->bits - 1);
+ value = (value ^ signbit) - signbit;
+ }
+ }
+
+ if (need_comma &&
+ ((operand->flags & (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA))
+ != AXP_OPERAND_PARENS))
+ {
+ (*info->fprintf_func) (info->stream, ",");
+ }
+ if (operand->flags & AXP_OPERAND_PARENS)
+ (*info->fprintf_func) (info->stream, "(");
+
+ /* Print the operand as directed by the flags. */
+ if (operand->flags & AXP_OPERAND_IR)
+ (*info->fprintf_func) (info->stream, "%s", regnames[value]);
+ else if (operand->flags & AXP_OPERAND_FPR)
+ (*info->fprintf_func) (info->stream, "%s", regnames[value + 32]);
+ else if (operand->flags & AXP_OPERAND_RELATIVE)
+ (*info->print_address_func) (memaddr + 4 + value, info);
+ else if (operand->flags & AXP_OPERAND_SIGNED)
+ (*info->fprintf_func) (info->stream, "%d", value);
+ else
+ (*info->fprintf_func) (info->stream, "%#x", value);
+
+ if (operand->flags & AXP_OPERAND_PARENS)
+ (*info->fprintf_func) (info->stream, ")");
+ need_comma = 1;
+ }
+
+ return 4;
+}
diff --git a/alpha.ld b/alpha.ld
new file mode 100644
index 0000000..0975443
--- /dev/null
+++ b/alpha.ld
@@ -0,0 +1,128 @@
+OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
+ "elf64-alpha")
+OUTPUT_ARCH(alpha)
+ENTRY(__start)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/arm-dis.c b/arm-dis.c
new file mode 100644
index 0000000..1e027ef
--- /dev/null
+++ b/arm-dis.c
@@ -0,0 +1,1680 @@
+/* Instruction printing code for the ARM
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+ Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modification by James G. Smith (jsmith@cygnus.co.uk)
+
+This file is part of libopcodes.
+
+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. */
+
+#include "dis-asm.h"
+
+struct arm_opcode {
+ unsigned long value, mask; /* recognise instruction if (op&mask)==value */
+ char *assembler; /* how to disassemble this instruction */
+};
+
+struct thumb_opcode
+{
+ unsigned short value, mask; /* recognise instruction if (op&mask)==value */
+ char * assembler; /* how to disassemble this instruction */
+};
+
+/* format of the assembler string :
+
+ %% %
+ %<bitfield>d print the bitfield in decimal
+ %<bitfield>x print the bitfield in hex
+ %<bitfield>X print the bitfield as 1 hex digit without leading "0x"
+ %<bitfield>r print as an ARM register
+ %<bitfield>f print a floating point constant if >7 else a
+ floating point register
+ %<code>y print a single precision VFP reg.
+ Codes: 0=>Sm, 1=>Sd, 2=>Sn, 3=>multi-list, 4=>Sm pair
+ %<code>z print a double precision VFP reg
+ Codes: 0=>Dm, 1=>Dd, 2=>Dn, 3=>multi-list
+ %c print condition code (always bits 28-31)
+ %P print floating point precision in arithmetic insn
+ %Q print floating point precision in ldf/stf insn
+ %R print floating point rounding mode
+ %<bitnum>'c print specified char iff bit is one
+ %<bitnum>`c print specified char iff bit is zero
+ %<bitnum>?ab print a if bit is one else print b
+ %p print 'p' iff bits 12-15 are 15
+ %t print 't' iff bit 21 set and bit 24 clear
+ %o print operand2 (immediate or register + shift)
+ %a print address for ldr/str instruction
+ %s print address for ldr/str halfword/signextend instruction
+ %b print branch destination
+ %B print arm BLX(1) destination
+ %A print address for ldc/stc/ldf/stf instruction
+ %m print register mask for ldm/stm instruction
+ %C print the PSR sub type.
+ %F print the COUNT field of a LFM/SFM instruction.
+Thumb specific format options:
+ %D print Thumb register (bits 0..2 as high number if bit 7 set)
+ %S print Thumb register (bits 3..5 as high number if bit 6 set)
+ %<bitfield>I print bitfield as a signed decimal
+ (top bit of range being the sign bit)
+ %M print Thumb register mask
+ %N print Thumb register mask (with LR)
+ %O print Thumb register mask (with PC)
+ %T print Thumb condition code (always bits 8-11)
+ %I print cirrus signed shift immediate: bits 0..3|4..6
+ %<bitfield>B print Thumb branch destination (signed displacement)
+ %<bitfield>W print (bitfield * 4) as a decimal
+ %<bitfield>H print (bitfield * 2) as a decimal
+ %<bitfield>a print (bitfield * 4) as a pc-rel offset + decoded symbol
+*/
+
+/* Note: There is a partial ordering in this table - it must be searched from
+ the top to obtain a correct match. */
+
+static struct arm_opcode arm_opcodes[] =
+{
+ /* ARM instructions. */
+ {0xe1a00000, 0xffffffff, "nop\t\t\t(mov r0,r0)"},
+ {0x012FFF10, 0x0ffffff0, "bx%c\t%0-3r"},
+ {0x00000090, 0x0fe000f0, "mul%c%20's\t%16-19r, %0-3r, %8-11r"},
+ {0x00200090, 0x0fe000f0, "mla%c%20's\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {0x01000090, 0x0fb00ff0, "swp%c%22'b\t%12-15r, %0-3r, [%16-19r]"},
+ {0x00800090, 0x0fa000f0, "%22?sumull%c%20's\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {0x00a00090, 0x0fa000f0, "%22?sumlal%c%20's\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+ /* V5J instruction. */
+ {0x012fff20, 0x0ffffff0, "bxj%c\t%0-3r"},
+
+ /* XScale instructions. */
+ {0x0e200010, 0x0fff0ff0, "mia%c\tacc0, %0-3r, %12-15r"},
+ {0x0e280010, 0x0fff0ff0, "miaph%c\tacc0, %0-3r, %12-15r"},
+ {0x0e2c0010, 0x0ffc0ff0, "mia%17'T%17`B%16'T%16`B%c\tacc0, %0-3r, %12-15r"},
+ {0x0c400000, 0x0ff00fff, "mar%c\tacc0, %12-15r, %16-19r"},
+ {0x0c500000, 0x0ff00fff, "mra%c\t%12-15r, %16-19r, acc0"},
+ {0xf450f000, 0xfc70f000, "pld\t%a"},
+
+ /* V5 Instructions. */
+ {0xe1200070, 0xfff000f0, "bkpt\t0x%16-19X%12-15X%8-11X%0-3X"},
+ {0xfa000000, 0xfe000000, "blx\t%B"},
+ {0x012fff30, 0x0ffffff0, "blx%c\t%0-3r"},
+ {0x016f0f10, 0x0fff0ff0, "clz%c\t%12-15r, %0-3r"},
+ {0xfc100000, 0xfe100000, "ldc2%22'l\t%8-11d, cr%12-15d, %A"},
+ {0xfc000000, 0xfe100000, "stc2%22'l\t%8-11d, cr%12-15d, %A"},
+ {0xfe000000, 0xff000010, "cdp2\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {0xfe000010, 0xff100010, "mcr2\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {0xfe100010, 0xff100010, "mrc2\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+
+ /* V5E "El Segundo" Instructions. */
+ {0x000000d0, 0x0e1000f0, "ldr%cd\t%12-15r, %s"},
+ {0x000000f0, 0x0e1000f0, "str%cd\t%12-15r, %s"},
+ {0x01000080, 0x0ff000f0, "smlabb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {0x010000a0, 0x0ff000f0, "smlatb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {0x010000c0, 0x0ff000f0, "smlabt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {0x010000e0, 0x0ff000f0, "smlatt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+ {0x01200080, 0x0ff000f0, "smlawb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {0x012000c0, 0x0ff000f0, "smlawt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+ {0x01400080, 0x0ff000f0, "smlalbb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {0x014000a0, 0x0ff000f0, "smlaltb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {0x014000c0, 0x0ff000f0, "smlalbt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {0x014000e0, 0x0ff000f0, "smlaltt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+ {0x01600080, 0x0ff0f0f0, "smulbb%c\t%16-19r, %0-3r, %8-11r"},
+ {0x016000a0, 0x0ff0f0f0, "smultb%c\t%16-19r, %0-3r, %8-11r"},
+ {0x016000c0, 0x0ff0f0f0, "smulbt%c\t%16-19r, %0-3r, %8-11r"},
+ {0x016000e0, 0x0ff0f0f0, "smultt%c\t%16-19r, %0-3r, %8-11r"},
+
+ {0x012000a0, 0x0ff0f0f0, "smulwb%c\t%16-19r, %0-3r, %8-11r"},
+ {0x012000e0, 0x0ff0f0f0, "smulwt%c\t%16-19r, %0-3r, %8-11r"},
+
+ {0x01000050, 0x0ff00ff0, "qadd%c\t%12-15r, %0-3r, %16-19r"},
+ {0x01400050, 0x0ff00ff0, "qdadd%c\t%12-15r, %0-3r, %16-19r"},
+ {0x01200050, 0x0ff00ff0, "qsub%c\t%12-15r, %0-3r, %16-19r"},
+ {0x01600050, 0x0ff00ff0, "qdsub%c\t%12-15r, %0-3r, %16-19r"},
+
+ {0x0c400000, 0x0ff00000, "mcrr%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+ {0x0c500000, 0x0ff00000, "mrrc%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+
+ /* ARM Instructions. */
+ {0x00000090, 0x0e100090, "str%c%6's%5?hb\t%12-15r, %s"},
+ {0x00100090, 0x0e100090, "ldr%c%6's%5?hb\t%12-15r, %s"},
+ {0x00000000, 0x0de00000, "and%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00200000, 0x0de00000, "eor%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00400000, 0x0de00000, "sub%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00600000, 0x0de00000, "rsb%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00800000, 0x0de00000, "add%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00a00000, 0x0de00000, "adc%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00c00000, 0x0de00000, "sbc%c%20's\t%12-15r, %16-19r, %o"},
+ {0x00e00000, 0x0de00000, "rsc%c%20's\t%12-15r, %16-19r, %o"},
+ {0x0120f000, 0x0db0f000, "msr%c\t%22?SCPSR%C, %o"},
+ {0x010f0000, 0x0fbf0fff, "mrs%c\t%12-15r, %22?SCPSR"},
+ {0x01000000, 0x0de00000, "tst%c%p\t%16-19r, %o"},
+ {0x01200000, 0x0de00000, "teq%c%p\t%16-19r, %o"},
+ {0x01400000, 0x0de00000, "cmp%c%p\t%16-19r, %o"},
+ {0x01600000, 0x0de00000, "cmn%c%p\t%16-19r, %o"},
+ {0x01800000, 0x0de00000, "orr%c%20's\t%12-15r, %16-19r, %o"},
+ {0x01a00000, 0x0de00000, "mov%c%20's\t%12-15r, %o"},
+ {0x01c00000, 0x0de00000, "bic%c%20's\t%12-15r, %16-19r, %o"},
+ {0x01e00000, 0x0de00000, "mvn%c%20's\t%12-15r, %o"},
+ {0x04000000, 0x0e100000, "str%c%22'b%t\t%12-15r, %a"},
+ {0x06000000, 0x0e100ff0, "str%c%22'b%t\t%12-15r, %a"},
+ {0x04000000, 0x0c100010, "str%c%22'b%t\t%12-15r, %a"},
+ {0x06000010, 0x0e000010, "undefined"},
+ {0x04100000, 0x0c100000, "ldr%c%22'b%t\t%12-15r, %a"},
+ {0x08000000, 0x0e100000, "stm%c%23?id%24?ba\t%16-19r%21'!, %m%22'^"},
+ {0x08100000, 0x0e100000, "ldm%c%23?id%24?ba\t%16-19r%21'!, %m%22'^"},
+ {0x0a000000, 0x0e000000, "b%24'l%c\t%b"},
+ {0x0f000000, 0x0f000000, "swi%c\t%0-23x"},
+
+ /* Floating point coprocessor (FPA) instructions */
+ {0x0e000100, 0x0ff08f10, "adf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e100100, 0x0ff08f10, "muf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e200100, 0x0ff08f10, "suf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e300100, 0x0ff08f10, "rsf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e400100, 0x0ff08f10, "dvf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e500100, 0x0ff08f10, "rdf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e600100, 0x0ff08f10, "pow%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e700100, 0x0ff08f10, "rpw%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e800100, 0x0ff08f10, "rmf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e900100, 0x0ff08f10, "fml%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0ea00100, 0x0ff08f10, "fdv%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0eb00100, 0x0ff08f10, "frd%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0ec00100, 0x0ff08f10, "pol%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {0x0e008100, 0x0ff08f10, "mvf%c%P%R\t%12-14f, %0-3f"},
+ {0x0e108100, 0x0ff08f10, "mnf%c%P%R\t%12-14f, %0-3f"},
+ {0x0e208100, 0x0ff08f10, "abs%c%P%R\t%12-14f, %0-3f"},
+ {0x0e308100, 0x0ff08f10, "rnd%c%P%R\t%12-14f, %0-3f"},
+ {0x0e408100, 0x0ff08f10, "sqt%c%P%R\t%12-14f, %0-3f"},
+ {0x0e508100, 0x0ff08f10, "log%c%P%R\t%12-14f, %0-3f"},
+ {0x0e608100, 0x0ff08f10, "lgn%c%P%R\t%12-14f, %0-3f"},
+ {0x0e708100, 0x0ff08f10, "exp%c%P%R\t%12-14f, %0-3f"},
+ {0x0e808100, 0x0ff08f10, "sin%c%P%R\t%12-14f, %0-3f"},
+ {0x0e908100, 0x0ff08f10, "cos%c%P%R\t%12-14f, %0-3f"},
+ {0x0ea08100, 0x0ff08f10, "tan%c%P%R\t%12-14f, %0-3f"},
+ {0x0eb08100, 0x0ff08f10, "asn%c%P%R\t%12-14f, %0-3f"},
+ {0x0ec08100, 0x0ff08f10, "acs%c%P%R\t%12-14f, %0-3f"},
+ {0x0ed08100, 0x0ff08f10, "atn%c%P%R\t%12-14f, %0-3f"},
+ {0x0ee08100, 0x0ff08f10, "urd%c%P%R\t%12-14f, %0-3f"},
+ {0x0ef08100, 0x0ff08f10, "nrm%c%P%R\t%12-14f, %0-3f"},
+ {0x0e000110, 0x0ff00f1f, "flt%c%P%R\t%16-18f, %12-15r"},
+ {0x0e100110, 0x0fff0f98, "fix%c%R\t%12-15r, %0-2f"},
+ {0x0e200110, 0x0fff0fff, "wfs%c\t%12-15r"},
+ {0x0e300110, 0x0fff0fff, "rfs%c\t%12-15r"},
+ {0x0e400110, 0x0fff0fff, "wfc%c\t%12-15r"},
+ {0x0e500110, 0x0fff0fff, "rfc%c\t%12-15r"},
+ {0x0e90f110, 0x0ff8fff0, "cmf%c\t%16-18f, %0-3f"},
+ {0x0eb0f110, 0x0ff8fff0, "cnf%c\t%16-18f, %0-3f"},
+ {0x0ed0f110, 0x0ff8fff0, "cmfe%c\t%16-18f, %0-3f"},
+ {0x0ef0f110, 0x0ff8fff0, "cnfe%c\t%16-18f, %0-3f"},
+ {0x0c000100, 0x0e100f00, "stf%c%Q\t%12-14f, %A"},
+ {0x0c100100, 0x0e100f00, "ldf%c%Q\t%12-14f, %A"},
+ {0x0c000200, 0x0e100f00, "sfm%c\t%12-14f, %F, %A"},
+ {0x0c100200, 0x0e100f00, "lfm%c\t%12-14f, %F, %A"},
+
+ /* Floating point coprocessor (VFP) instructions */
+ {0x0eb00bc0, 0x0fff0ff0, "fabsd%c\t%1z, %0z"},
+ {0x0eb00ac0, 0x0fbf0fd0, "fabss%c\t%1y, %0y"},
+ {0x0e300b00, 0x0ff00ff0, "faddd%c\t%1z, %2z, %0z"},
+ {0x0e300a00, 0x0fb00f50, "fadds%c\t%1y, %2y, %1y"},
+ {0x0eb40b40, 0x0fff0f70, "fcmp%7'ed%c\t%1z, %0z"},
+ {0x0eb40a40, 0x0fbf0f50, "fcmp%7'es%c\t%1y, %0y"},
+ {0x0eb50b40, 0x0fff0f70, "fcmp%7'ezd%c\t%1z"},
+ {0x0eb50a40, 0x0fbf0f70, "fcmp%7'ezs%c\t%1y"},
+ {0x0eb00b40, 0x0fff0ff0, "fcpyd%c\t%1z, %0z"},
+ {0x0eb00a40, 0x0fbf0fd0, "fcpys%c\t%1y, %0y"},
+ {0x0eb70ac0, 0x0fff0fd0, "fcvtds%c\t%1z, %0y"},
+ {0x0eb70bc0, 0x0fbf0ff0, "fcvtsd%c\t%1y, %0z"},
+ {0x0e800b00, 0x0ff00ff0, "fdivd%c\t%1z, %2z, %0z"},
+ {0x0e800a00, 0x0fb00f50, "fdivs%c\t%1y, %2y, %0y"},
+ {0x0d100b00, 0x0f700f00, "fldd%c\t%1z, %A"},
+ {0x0c900b00, 0x0fd00f00, "fldmia%0?xd%c\t%16-19r%21'!, %3z"},
+ {0x0d300b00, 0x0ff00f00, "fldmdb%0?xd%c\t%16-19r!, %3z"},
+ {0x0d100a00, 0x0f300f00, "flds%c\t%1y, %A"},
+ {0x0c900a00, 0x0f900f00, "fldmias%c\t%16-19r%21'!, %3y"},
+ {0x0d300a00, 0x0fb00f00, "fldmdbs%c\t%16-19r!, %3y"},
+ {0x0e000b00, 0x0ff00ff0, "fmacd%c\t%1z, %2z, %0z"},
+ {0x0e000a00, 0x0fb00f50, "fmacs%c\t%1y, %2y, %0y"},
+ {0x0e200b10, 0x0ff00fff, "fmdhr%c\t%2z, %12-15r"},
+ {0x0e000b10, 0x0ff00fff, "fmdlr%c\t%2z, %12-15r"},
+ {0x0c400b10, 0x0ff00ff0, "fmdrr%c\t%0z, %12-15r, %16-19r"},
+ {0x0e300b10, 0x0ff00fff, "fmrdh%c\t%12-15r, %2z"},
+ {0x0e100b10, 0x0ff00fff, "fmrdl%c\t%12-15r, %2z"},
+ {0x0c500b10, 0x0ff00ff0, "fmrrd%c\t%12-15r, %16-19r, %0z"},
+ {0x0c500a10, 0x0ff00fd0, "fmrrs%c\t%12-15r, %16-19r, %4y"},
+ {0x0e100a10, 0x0ff00f7f, "fmrs%c\t%12-15r, %2y"},
+ {0x0ef1fa10, 0x0fffffff, "fmstat%c"},
+ {0x0ef00a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpsid"},
+ {0x0ef10a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpscr"},
+ {0x0ef80a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpexc"},
+ {0x0ef90a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst\t@ Impl def"},
+ {0x0efa0a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst2\t@ Impl def"},
+ {0x0ef00a10, 0x0ff00fff, "fmrx%c\t%12-15r, <impl def 0x%16-19x>"},
+ {0x0e100b00, 0x0ff00ff0, "fmscd%c\t%1z, %2z, %0z"},
+ {0x0e100a00, 0x0fb00f50, "fmscs%c\t%1y, %2y, %0y"},
+ {0x0e000a10, 0x0ff00f7f, "fmsr%c\t%2y, %12-15r"},
+ {0x0c400a10, 0x0ff00fd0, "fmsrr%c\t%12-15r, %16-19r, %4y"},
+ {0x0e200b00, 0x0ff00ff0, "fmuld%c\t%1z, %2z, %0z"},
+ {0x0e200a00, 0x0fb00f50, "fmuls%c\t%1y, %2y, %0y"},
+ {0x0ee00a10, 0x0fff0fff, "fmxr%c\tfpsid, %12-15r"},
+ {0x0ee10a10, 0x0fff0fff, "fmxr%c\tfpscr, %12-15r"},
+ {0x0ee80a10, 0x0fff0fff, "fmxr%c\tfpexc, %12-15r"},
+ {0x0ee90a10, 0x0fff0fff, "fmxr%c\tfpinst, %12-15r\t@ Impl def"},
+ {0x0eea0a10, 0x0fff0fff, "fmxr%c\tfpinst2, %12-15r\t@ Impl def"},
+ {0x0ee00a10, 0x0ff00fff, "fmxr%c\t<impl def 0x%16-19x>, %12-15r"},
+ {0x0eb10b40, 0x0fff0ff0, "fnegd%c\t%1z, %0z"},
+ {0x0eb10a40, 0x0fbf0fd0, "fnegs%c\t%1y, %0y"},
+ {0x0e000b40, 0x0ff00ff0, "fnmacd%c\t%1z, %2z, %0z"},
+ {0x0e000a40, 0x0fb00f50, "fnmacs%c\t%1y, %2y, %0y"},
+ {0x0e100b40, 0x0ff00ff0, "fnmscd%c\t%1z, %2z, %0z"},
+ {0x0e100a40, 0x0fb00f50, "fnmscs%c\t%1y, %2y, %0y"},
+ {0x0e200b40, 0x0ff00ff0, "fnmuld%c\t%1z, %2z, %0z"},
+ {0x0e200a40, 0x0fb00f50, "fnmuls%c\t%1y, %2y, %0y"},
+ {0x0eb80bc0, 0x0fff0fd0, "fsitod%c\t%1z, %0y"},
+ {0x0eb80ac0, 0x0fbf0fd0, "fsitos%c\t%1y, %0y"},
+ {0x0eb10bc0, 0x0fff0ff0, "fsqrtd%c\t%1z, %0z"},
+ {0x0eb10ac0, 0x0fbf0fd0, "fsqrts%c\t%1y, %0y"},
+ {0x0d000b00, 0x0f700f00, "fstd%c\t%1z, %A"},
+ {0x0c800b00, 0x0fd00f00, "fstmia%0?xd%c\t%16-19r%21'!, %3z"},
+ {0x0d200b00, 0x0ff00f00, "fstmdb%0?xd%c\t%16-19r!, %3z"},
+ {0x0d000a00, 0x0f300f00, "fsts%c\t%1y, %A"},
+ {0x0c800a00, 0x0f900f00, "fstmias%c\t%16-19r%21'!, %3y"},
+ {0x0d200a00, 0x0fb00f00, "fstmdbs%c\t%16-19r!, %3y"},
+ {0x0e300b40, 0x0ff00ff0, "fsubd%c\t%1z, %2z, %0z"},
+ {0x0e300a40, 0x0fb00f50, "fsubs%c\t%1y, %2y, %0y"},
+ {0x0ebc0b40, 0x0fbe0f70, "fto%16?sui%7'zd%c\t%1y, %0z"},
+ {0x0ebc0a40, 0x0fbe0f50, "fto%16?sui%7'zs%c\t%1y, %0y"},
+ {0x0eb80b40, 0x0fff0fd0, "fuitod%c\t%1z, %0y"},
+ {0x0eb80a40, 0x0fbf0fd0, "fuitos%c\t%1y, %0y"},
+
+ /* Cirrus coprocessor instructions. */
+ {0x0d100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+ {0x0c100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+ {0x0d500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+ {0x0c500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+ {0x0d100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+ {0x0c100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+ {0x0d500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+ {0x0c500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+ {0x0d000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+ {0x0c000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+ {0x0d400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+ {0x0c400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+ {0x0d000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+ {0x0c000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+ {0x0d400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+ {0x0c400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+ {0x0e000450, 0x0ff00ff0, "cfmvsr%c\tmvf%16-19d, %12-15r"},
+ {0x0e100450, 0x0ff00ff0, "cfmvrs%c\t%12-15r, mvf%16-19d"},
+ {0x0e000410, 0x0ff00ff0, "cfmvdlr%c\tmvd%16-19d, %12-15r"},
+ {0x0e100410, 0x0ff00ff0, "cfmvrdl%c\t%12-15r, mvd%16-19d"},
+ {0x0e000430, 0x0ff00ff0, "cfmvdhr%c\tmvd%16-19d, %12-15r"},
+ {0x0e100430, 0x0ff00fff, "cfmvrdh%c\t%12-15r, mvd%16-19d"},
+ {0x0e000510, 0x0ff00fff, "cfmv64lr%c\tmvdx%16-19d, %12-15r"},
+ {0x0e100510, 0x0ff00fff, "cfmvr64l%c\t%12-15r, mvdx%16-19d"},
+ {0x0e000530, 0x0ff00fff, "cfmv64hr%c\tmvdx%16-19d, %12-15r"},
+ {0x0e100530, 0x0ff00fff, "cfmvr64h%c\t%12-15r, mvdx%16-19d"},
+ {0x0e100610, 0x0ff0fff0, "cfmval32%c\tmvax%0-3d, mvfx%16-19d"},
+ {0x0e000610, 0x0ff0fff0, "cfmv32al%c\tmvfx%0-3d, mvax%16-19d"},
+ {0x0e100630, 0x0ff0fff0, "cfmvam32%c\tmvax%0-3d, mvfx%16-19d"},
+ {0x0e000630, 0x0ff0fff0, "cfmv32am%c\tmvfx%0-3d, mvax%16-19d"},
+ {0x0e100650, 0x0ff0fff0, "cfmvah32%c\tmvax%0-3d, mvfx%16-19d"},
+ {0x0e000650, 0x0ff0fff0, "cfmv32ah%c\tmvfx%0-3d, mvax%16-19d"},
+ {0x0e000670, 0x0ff0fff0, "cfmv32a%c\tmvfx%0-3d, mvax%16-19d"},
+ {0x0e100670, 0x0ff0fff0, "cfmva32%c\tmvax%0-3d, mvfx%16-19d"},
+ {0x0e000690, 0x0ff0fff0, "cfmv64a%c\tmvdx%0-3d, mvax%16-19d"},
+ {0x0e100690, 0x0ff0fff0, "cfmva64%c\tmvax%0-3d, mvdx%16-19d"},
+ {0x0e1006b0, 0x0ff0fff0, "cfmvsc32%c\tdspsc, mvfx%16-19d"},
+ {0x0e0006b0, 0x0ff0fff0, "cfmv32sc%c\tmvfx%0-3d, dspsc"},
+ {0x0e000400, 0x0ff00fff, "cfcpys%c\tmvf%12-15d, mvf%16-19d"},
+ {0x0e000420, 0x0ff00fff, "cfcpyd%c\tmvd%12-15d, mvd%16-19d"},
+ {0x0e000460, 0x0ff00fff, "cfcvtsd%c\tmvd%12-15d, mvf%16-19d"},
+ {0x0e000440, 0x0ff00fff, "cfcvtds%c\tmvf%12-15d, mvd%16-19d"},
+ {0x0e000480, 0x0ff00fff, "cfcvt32s%c\tmvf%12-15d, mvfx%16-19d"},
+ {0x0e0004a0, 0x0ff00fff, "cfcvt32d%c\tmvd%12-15d, mvfx%16-19d"},
+ {0x0e0004c0, 0x0ff00fff, "cfcvt64s%c\tmvf%12-15d, mvdx%16-19d"},
+ {0x0e0004e0, 0x0ff00fff, "cfcvt64d%c\tmvd%12-15d, mvdx%16-19d"},
+ {0x0e100580, 0x0ff00fff, "cfcvts32%c\tmvfx%12-15d, mvf%16-19d"},
+ {0x0e1005a0, 0x0ff00fff, "cfcvtd32%c\tmvfx%12-15d, mvd%16-19d"},
+ {0x0e1005c0, 0x0ff00fff, "cftruncs32%c\tmvfx%12-15d, mvf%16-19d"},
+ {0x0e1005e0, 0x0ff00fff, "cftruncd32%c\tmvfx%12-15d, mvd%16-19d"},
+ {0x0e000550, 0x0ff00ff0, "cfrshl32%c\tmvfx%16-19d, mvfx%0-3d, %12-15r"},
+ {0x0e000570, 0x0ff00ff0, "cfrshl64%c\tmvdx%16-19d, mvdx%0-3d, %12-15r"},
+ {0x0e000500, 0x0ff00f00, "cfsh32%c\tmvfx%12-15d, mvfx%16-19d, #%I"},
+ {0x0e200500, 0x0ff00f00, "cfsh64%c\tmvdx%12-15d, mvdx%16-19d, #%I"},
+ {0x0e100490, 0x0ff00ff0, "cfcmps%c\t%12-15r, mvf%16-19d, mvf%0-3d"},
+ {0x0e1004b0, 0x0ff00ff0, "cfcmpd%c\t%12-15r, mvd%16-19d, mvd%0-3d"},
+ {0x0e100590, 0x0ff00ff0, "cfcmp32%c\t%12-15r, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e1005b0, 0x0ff00ff0, "cfcmp64%c\t%12-15r, mvdx%16-19d, mvdx%0-3d"},
+ {0x0e300400, 0x0ff00fff, "cfabss%c\tmvf%12-15d, mvf%16-19d"},
+ {0x0e300420, 0x0ff00fff, "cfabsd%c\tmvd%12-15d, mvd%16-19d"},
+ {0x0e300440, 0x0ff00fff, "cfnegs%c\tmvf%12-15d, mvf%16-19d"},
+ {0x0e300460, 0x0ff00fff, "cfnegd%c\tmvd%12-15d, mvd%16-19d"},
+ {0x0e300480, 0x0ff00ff0, "cfadds%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {0x0e3004a0, 0x0ff00ff0, "cfaddd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {0x0e3004c0, 0x0ff00ff0, "cfsubs%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {0x0e3004e0, 0x0ff00ff0, "cfsubd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {0x0e100400, 0x0ff00ff0, "cfmuls%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {0x0e100420, 0x0ff00ff0, "cfmuld%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {0x0e300500, 0x0ff00fff, "cfabs32%c\tmvfx%12-15d, mvfx%16-19d"},
+ {0x0e300520, 0x0ff00fff, "cfabs64%c\tmvdx%12-15d, mvdx%16-19d"},
+ {0x0e300540, 0x0ff00fff, "cfneg32%c\tmvfx%12-15d, mvfx%16-19d"},
+ {0x0e300560, 0x0ff00fff, "cfneg64%c\tmvdx%12-15d, mvdx%16-19d"},
+ {0x0e300580, 0x0ff00ff0, "cfadd32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e3005a0, 0x0ff00ff0, "cfadd64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {0x0e3005c0, 0x0ff00ff0, "cfsub32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e3005e0, 0x0ff00ff0, "cfsub64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {0x0e100500, 0x0ff00ff0, "cfmul32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e100520, 0x0ff00ff0, "cfmul64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {0x0e100540, 0x0ff00ff0, "cfmac32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e100560, 0x0ff00ff0, "cfmsc32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e000600, 0x0ff00f00, "cfmadd32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e100600, 0x0ff00f00, "cfmsub32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e200600, 0x0ff00f00, "cfmadda32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {0x0e300600, 0x0ff00f00, "cfmsuba32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+
+ /* Generic coprocessor instructions */
+ {0x0e000000, 0x0f000010, "cdp%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {0x0e100010, 0x0f100010, "mrc%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {0x0e000010, 0x0f100010, "mcr%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {0x0c000000, 0x0e100000, "stc%c%22'l\t%8-11d, cr%12-15d, %A"},
+ {0x0c100000, 0x0e100000, "ldc%c%22'l\t%8-11d, cr%12-15d, %A"},
+
+ /* The rest. */
+ {0x00000000, 0x00000000, "undefined instruction %0-31x"},
+ {0x00000000, 0x00000000, 0}
+};
+
+#define BDISP(x) ((((x) & 0xffffff) ^ 0x800000) - 0x800000) /* 26 bit */
+
+static struct thumb_opcode thumb_opcodes[] =
+{
+ /* Thumb instructions. */
+
+ /* ARM V5 ISA extends Thumb. */
+ {0xbe00, 0xff00, "bkpt\t%0-7x"},
+ {0x4780, 0xff87, "blx\t%3-6r"}, /* note: 4 bit register number. */
+ /* Note: this is BLX(2). BLX(1) is done in arm-dis.c/print_insn_thumb()
+ as an extension of the special processing there for Thumb BL.
+ BL and BLX(1) involve 2 successive 16-bit instructions, which must
+ always appear together in the correct order. So, the empty
+ string is put in this table, and the string interpreter takes <empty>
+ to mean it has a pair of BL-ish instructions. */
+ {0x46C0, 0xFFFF, "nop\t\t\t(mov r8, r8)"},
+ /* Format 5 instructions do not update the PSR. */
+ {0x1C00, 0xFFC0, "mov\t%0-2r, %3-5r\t\t(add %0-2r, %3-5r, #%6-8d)"},
+ /* Format 4. */
+ {0x4000, 0xFFC0, "and\t%0-2r, %3-5r"},
+ {0x4040, 0xFFC0, "eor\t%0-2r, %3-5r"},
+ {0x4080, 0xFFC0, "lsl\t%0-2r, %3-5r"},
+ {0x40C0, 0xFFC0, "lsr\t%0-2r, %3-5r"},
+ {0x4100, 0xFFC0, "asr\t%0-2r, %3-5r"},
+ {0x4140, 0xFFC0, "adc\t%0-2r, %3-5r"},
+ {0x4180, 0xFFC0, "sbc\t%0-2r, %3-5r"},
+ {0x41C0, 0xFFC0, "ror\t%0-2r, %3-5r"},
+ {0x4200, 0xFFC0, "tst\t%0-2r, %3-5r"},
+ {0x4240, 0xFFC0, "neg\t%0-2r, %3-5r"},
+ {0x4280, 0xFFC0, "cmp\t%0-2r, %3-5r"},
+ {0x42C0, 0xFFC0, "cmn\t%0-2r, %3-5r"},
+ {0x4300, 0xFFC0, "orr\t%0-2r, %3-5r"},
+ {0x4340, 0xFFC0, "mul\t%0-2r, %3-5r"},
+ {0x4380, 0xFFC0, "bic\t%0-2r, %3-5r"},
+ {0x43C0, 0xFFC0, "mvn\t%0-2r, %3-5r"},
+ /* format 13 */
+ {0xB000, 0xFF80, "add\tsp, #%0-6W"},
+ {0xB080, 0xFF80, "sub\tsp, #%0-6W"},
+ /* format 5 */
+ {0x4700, 0xFF80, "bx\t%S"},
+ {0x4400, 0xFF00, "add\t%D, %S"},
+ {0x4500, 0xFF00, "cmp\t%D, %S"},
+ {0x4600, 0xFF00, "mov\t%D, %S"},
+ /* format 14 */
+ {0xB400, 0xFE00, "push\t%N"},
+ {0xBC00, 0xFE00, "pop\t%O"},
+ /* format 2 */
+ {0x1800, 0xFE00, "add\t%0-2r, %3-5r, %6-8r"},
+ {0x1A00, 0xFE00, "sub\t%0-2r, %3-5r, %6-8r"},
+ {0x1C00, 0xFE00, "add\t%0-2r, %3-5r, #%6-8d"},
+ {0x1E00, 0xFE00, "sub\t%0-2r, %3-5r, #%6-8d"},
+ /* format 8 */
+ {0x5200, 0xFE00, "strh\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5A00, 0xFE00, "ldrh\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5600, 0xF600, "ldrs%11?hb\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 7 */
+ {0x5000, 0xFA00, "str%10'b\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5800, 0xFA00, "ldr%10'b\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 1 */
+ {0x0000, 0xF800, "lsl\t%0-2r, %3-5r, #%6-10d"},
+ {0x0800, 0xF800, "lsr\t%0-2r, %3-5r, #%6-10d"},
+ {0x1000, 0xF800, "asr\t%0-2r, %3-5r, #%6-10d"},
+ /* format 3 */
+ {0x2000, 0xF800, "mov\t%8-10r, #%0-7d"},
+ {0x2800, 0xF800, "cmp\t%8-10r, #%0-7d"},
+ {0x3000, 0xF800, "add\t%8-10r, #%0-7d"},
+ {0x3800, 0xF800, "sub\t%8-10r, #%0-7d"},
+ /* format 6 */
+ {0x4800, 0xF800, "ldr\t%8-10r, [pc, #%0-7W]\t(%0-7a)"}, /* TODO: Disassemble PC relative "LDR rD,=<symbolic>" */
+ /* format 9 */
+ {0x6000, 0xF800, "str\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x6800, 0xF800, "ldr\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x7000, 0xF800, "strb\t%0-2r, [%3-5r, #%6-10d]"},
+ {0x7800, 0xF800, "ldrb\t%0-2r, [%3-5r, #%6-10d]"},
+ /* format 10 */
+ {0x8000, 0xF800, "strh\t%0-2r, [%3-5r, #%6-10H]"},
+ {0x8800, 0xF800, "ldrh\t%0-2r, [%3-5r, #%6-10H]"},
+ /* format 11 */
+ {0x9000, 0xF800, "str\t%8-10r, [sp, #%0-7W]"},
+ {0x9800, 0xF800, "ldr\t%8-10r, [sp, #%0-7W]"},
+ /* format 12 */
+ {0xA000, 0xF800, "add\t%8-10r, pc, #%0-7W\t(adr %8-10r,%0-7a)"},
+ {0xA800, 0xF800, "add\t%8-10r, sp, #%0-7W"},
+ /* format 15 */
+ {0xC000, 0xF800, "stmia\t%8-10r!,%M"},
+ {0xC800, 0xF800, "ldmia\t%8-10r!,%M"},
+ /* format 18 */
+ {0xE000, 0xF800, "b\t%0-10B"},
+ {0xE800, 0xF800, "undefined"},
+ /* format 19 */
+ {0xF000, 0xF800, ""}, /* special processing required in disassembler */
+ {0xF800, 0xF800, "second half of BL instruction %0-15x"},
+ /* format 16 */
+ {0xD000, 0xFF00, "beq\t%0-7B"},
+ {0xD100, 0xFF00, "bne\t%0-7B"},
+ {0xD200, 0xFF00, "bcs\t%0-7B"},
+ {0xD300, 0xFF00, "bcc\t%0-7B"},
+ {0xD400, 0xFF00, "bmi\t%0-7B"},
+ {0xD500, 0xFF00, "bpl\t%0-7B"},
+ {0xD600, 0xFF00, "bvs\t%0-7B"},
+ {0xD700, 0xFF00, "bvc\t%0-7B"},
+ {0xD800, 0xFF00, "bhi\t%0-7B"},
+ {0xD900, 0xFF00, "bls\t%0-7B"},
+ {0xDA00, 0xFF00, "bge\t%0-7B"},
+ {0xDB00, 0xFF00, "blt\t%0-7B"},
+ {0xDC00, 0xFF00, "bgt\t%0-7B"},
+ {0xDD00, 0xFF00, "ble\t%0-7B"},
+ /* format 17 */
+ {0xDE00, 0xFF00, "bal\t%0-7B"},
+ {0xDF00, 0xFF00, "swi\t%0-7d"},
+ /* format 9 */
+ {0x6000, 0xF800, "str\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x6800, 0xF800, "ldr\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x7000, 0xF800, "strb\t%0-2r, [%3-5r, #%6-10d]"},
+ {0x7800, 0xF800, "ldrb\t%0-2r, [%3-5r, #%6-10d]"},
+ /* the rest */
+ {0x0000, 0x0000, "undefined instruction %0-15x"},
+ {0x0000, 0x0000, 0}
+};
+
+#define BDISP23(x) ((((((x) & 0x07ff) << 11) | (((x) & 0x07ff0000) >> 16)) \
+ ^ 0x200000) - 0x200000) /* 23bit */
+
+#ifndef streq
+#define streq(a,b) (strcmp ((a), (b)) == 0)
+#endif
+
+#ifndef strneq
+#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0)
+#endif
+
+#ifndef NUM_ELEM
+#define NUM_ELEM(a) (sizeof (a) / sizeof (a)[0])
+#endif
+
+static char * arm_conditional[] =
+{"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "", "nv"};
+
+typedef struct
+{
+ const char * name;
+ const char * description;
+ const char * reg_names[16];
+}
+arm_regname;
+
+static arm_regname regnames[] =
+{
+ { "raw" , "Select raw register names",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}},
+ { "gcc", "Select register names used by GCC",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "std", "Select register names used in ARM's ISA documentation",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" }},
+ { "apcs", "Select register names used in the APCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "atpcs", "Select register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "IP", "SP", "LR", "PC" }},
+ { "special-atpcs", "Select special register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL", "FP", "IP", "SP", "LR", "PC" }}
+};
+
+/* Default to STD register name set. */
+static unsigned int regname_selected = 2;
+
+#define NUM_ARM_REGNAMES NUM_ELEM (regnames)
+#define arm_regnames regnames[regname_selected].reg_names
+
+static boolean force_thumb = false;
+
+static char * arm_fp_const[] =
+{"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"};
+
+static char * arm_shift[] =
+{"lsl", "lsr", "asr", "ror"};
+
+/* Forward declarations. */
+static void arm_decode_shift PARAMS ((long, fprintf_ftype, void *));
+static int print_insn_arm1 PARAMS ((bfd_vma, struct disassemble_info *, long));
+static int print_insn_thumb PARAMS ((bfd_vma, struct disassemble_info *, long));
+static void parse_disassembler_options PARAMS ((char *));
+int get_arm_regname_num_options (void);
+int set_arm_regname_option (int option);
+int get_arm_regnames (int option, const char **setname,
+ const char **setdescription,
+ const char ***register_names);
+
+/* Functions. */
+int
+get_arm_regname_num_options ()
+{
+ return NUM_ARM_REGNAMES;
+}
+
+int
+set_arm_regname_option (option)
+ int option;
+{
+ int old = regname_selected;
+ regname_selected = option;
+ return old;
+}
+
+int
+get_arm_regnames (option, setname, setdescription, register_names)
+ int option;
+ const char **setname;
+ const char **setdescription;
+ const char ***register_names;
+{
+ *setname = regnames[option].name;
+ *setdescription = regnames[option].description;
+ *register_names = regnames[option].reg_names;
+ return 16;
+}
+
+static void
+arm_decode_shift (given, func, stream)
+ long given;
+ fprintf_ftype func;
+ void * stream;
+{
+ func (stream, "%s", arm_regnames[given & 0xf]);
+
+ if ((given & 0xff0) != 0)
+ {
+ if ((given & 0x10) == 0)
+ {
+ int amount = (given & 0xf80) >> 7;
+ int shift = (given & 0x60) >> 5;
+
+ if (amount == 0)
+ {
+ if (shift == 3)
+ {
+ func (stream, ", rrx");
+ return;
+ }
+
+ amount = 32;
+ }
+
+ func (stream, ", %s #%d", arm_shift[shift], amount);
+ }
+ else
+ func (stream, ", %s %s", arm_shift[(given & 0x60) >> 5],
+ arm_regnames[(given & 0xf00) >> 8]);
+ }
+}
+
+/* Print one instruction from PC on INFO->STREAM.
+ Return the size of the instruction (always 4 on ARM). */
+
+static int
+print_insn_arm1 (pc, info, given)
+ bfd_vma pc;
+ struct disassemble_info * info;
+ long given;
+{
+ struct arm_opcode * insn;
+ void * stream = info->stream;
+ fprintf_ftype func = info->fprintf_func;
+
+ for (insn = arm_opcodes; insn->assembler; insn++)
+ {
+ if ((given & insn->mask) == insn->value)
+ {
+ char * c;
+
+ for (c = insn->assembler; *c; c++)
+ {
+ if (*c == '%')
+ {
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'a':
+ if (((given & 0x000f0000) == 0x000f0000)
+ && ((given & 0x02000000) == 0))
+ {
+ int offset = given & 0xfff;
+
+ func (stream, "[pc");
+
+ if (given & 0x01000000)
+ {
+ if ((given & 0x00800000) == 0)
+ offset = - offset;
+
+ /* Pre-indexed. */
+ func (stream, ", #%d]", offset);
+
+ offset += pc + 8;
+
+ /* Cope with the possibility of write-back
+ being used. Probably a very dangerous thing
+ for the programmer to do, but who are we to
+ argue ? */
+ if (given & 0x00200000)
+ func (stream, "!");
+ }
+ else
+ {
+ /* Post indexed. */
+ func (stream, "], #%d", offset);
+
+ /* ie ignore the offset. */
+ offset = pc + 8;
+ }
+
+ func (stream, "\t; ");
+ info->print_address_func (offset, info);
+ }
+ else
+ {
+ func (stream, "[%s",
+ arm_regnames[(given >> 16) & 0xf]);
+ if ((given & 0x01000000) != 0)
+ {
+ if ((given & 0x02000000) == 0)
+ {
+ int offset = given & 0xfff;
+ if (offset)
+ func (stream, ", %s#%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ }
+ else
+ {
+ func (stream, ", %s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""));
+ arm_decode_shift (given, func, stream);
+ }
+
+ func (stream, "]%s",
+ ((given & 0x00200000) != 0) ? "!" : "");
+ }
+ else
+ {
+ if ((given & 0x02000000) == 0)
+ {
+ int offset = given & 0xfff;
+ if (offset)
+ func (stream, "], %s#%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ func (stream, "], %s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""));
+ arm_decode_shift (given, func, stream);
+ }
+ }
+ }
+ break;
+
+ case 's':
+ if ((given & 0x004f0000) == 0x004f0000)
+ {
+ /* PC relative with immediate offset. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+
+ if ((given & 0x00800000) == 0)
+ offset = -offset;
+
+ func (stream, "[pc, #%d]\t; ", offset);
+
+ (*info->print_address_func)
+ (offset + pc + 8, info);
+ }
+ else
+ {
+ func (stream, "[%s",
+ arm_regnames[(given >> 16) & 0xf]);
+ if ((given & 0x01000000) != 0)
+ {
+ /* Pre-indexed. */
+ if ((given & 0x00400000) == 0x00400000)
+ {
+ /* Immediate. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+ if (offset)
+ func (stream, ", %s#%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ }
+ else
+ {
+ /* Register. */
+ func (stream, ", %s%s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""),
+ arm_regnames[given & 0xf]);
+ }
+
+ func (stream, "]%s",
+ ((given & 0x00200000) != 0) ? "!" : "");
+ }
+ else
+ {
+ /* Post-indexed. */
+ if ((given & 0x00400000) == 0x00400000)
+ {
+ /* Immediate. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+ if (offset)
+ func (stream, "], %s#%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ /* Register. */
+ func (stream, "], %s%s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""),
+ arm_regnames[given & 0xf]);
+ }
+ }
+ }
+ break;
+
+ case 'b':
+ (*info->print_address_func)
+ (BDISP (given) * 4 + pc + 8, info);
+ break;
+
+ case 'c':
+ func (stream, "%s",
+ arm_conditional [(given >> 28) & 0xf]);
+ break;
+
+ case 'm':
+ {
+ int started = 0;
+ int reg;
+
+ func (stream, "{");
+ for (reg = 0; reg < 16; reg++)
+ if ((given & (1 << reg)) != 0)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ func (stream, "}");
+ }
+ break;
+
+ case 'o':
+ if ((given & 0x02000000) != 0)
+ {
+ int rotate = (given & 0xf00) >> 7;
+ int immed = (given & 0xff);
+ immed = (((immed << (32 - rotate))
+ | (immed >> rotate)) & 0xffffffff);
+ func (stream, "#%d\t; 0x%x", immed, immed);
+ }
+ else
+ arm_decode_shift (given, func, stream);
+ break;
+
+ case 'p':
+ if ((given & 0x0000f000) == 0x0000f000)
+ func (stream, "p");
+ break;
+
+ case 't':
+ if ((given & 0x01200000) == 0x00200000)
+ func (stream, "t");
+ break;
+
+ case 'A':
+ func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+ if ((given & 0x01000000) != 0)
+ {
+ int offset = given & 0xff;
+ if (offset)
+ func (stream, ", %s#%d]%s",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4,
+ ((given & 0x00200000) != 0 ? "!" : ""));
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ int offset = given & 0xff;
+ if (offset)
+ func (stream, "], %s#%d",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4);
+ else
+ func (stream, "]");
+ }
+ break;
+
+ case 'B':
+ /* Print ARM V5 BLX(1) address: pc+25 bits. */
+ {
+ bfd_vma address;
+ bfd_vma offset = 0;
+
+ if (given & 0x00800000)
+ /* Is signed, hi bits should be ones. */
+ offset = (-1) ^ 0x00ffffff;
+
+ /* Offset is (SignExtend(offset field)<<2). */
+ offset += given & 0x00ffffff;
+ offset <<= 2;
+ address = offset + pc + 8;
+
+ if (given & 0x01000000)
+ /* H bit allows addressing to 2-byte boundaries. */
+ address += 2;
+
+ info->print_address_func (address, info);
+ }
+ break;
+
+ case 'I':
+ /* Print a Cirrus/DSP shift immediate. */
+ /* Immediates are 7bit signed ints with bits 0..3 in
+ bits 0..3 of opcode and bits 4..6 in bits 5..7
+ of opcode. */
+ {
+ int imm;
+
+ imm = (given & 0xf) | ((given & 0xe0) >> 1);
+
+ /* Is ``imm'' a negative number? */
+ if (imm & 0x40)
+ imm |= (-1 << 7);
+
+ func (stream, "%d", imm);
+ }
+
+ break;
+
+ case 'C':
+ func (stream, "_");
+ if (given & 0x80000)
+ func (stream, "f");
+ if (given & 0x40000)
+ func (stream, "s");
+ if (given & 0x20000)
+ func (stream, "x");
+ if (given & 0x10000)
+ func (stream, "c");
+ break;
+
+ case 'F':
+ switch (given & 0x00408000)
+ {
+ case 0:
+ func (stream, "4");
+ break;
+ case 0x8000:
+ func (stream, "1");
+ break;
+ case 0x00400000:
+ func (stream, "2");
+ break;
+ default:
+ func (stream, "3");
+ }
+ break;
+
+ case 'P':
+ switch (given & 0x00080080)
+ {
+ case 0:
+ func (stream, "s");
+ break;
+ case 0x80:
+ func (stream, "d");
+ break;
+ case 0x00080000:
+ func (stream, "e");
+ break;
+ default:
+ func (stream, _("<illegal precision>"));
+ break;
+ }
+ break;
+ case 'Q':
+ switch (given & 0x00408000)
+ {
+ case 0:
+ func (stream, "s");
+ break;
+ case 0x8000:
+ func (stream, "d");
+ break;
+ case 0x00400000:
+ func (stream, "e");
+ break;
+ default:
+ func (stream, "p");
+ break;
+ }
+ break;
+ case 'R':
+ switch (given & 0x60)
+ {
+ case 0:
+ break;
+ case 0x20:
+ func (stream, "p");
+ break;
+ case 0x40:
+ func (stream, "m");
+ break;
+ default:
+ func (stream, "z");
+ break;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int bitstart = *c++ - '0';
+ int bitend = 0;
+ while (*c >= '0' && *c <= '9')
+ bitstart = (bitstart * 10) + *c++ - '0';
+
+ switch (*c)
+ {
+ case '-':
+ c++;
+
+ while (*c >= '0' && *c <= '9')
+ bitend = (bitend * 10) + *c++ - '0';
+
+ if (!bitend)
+ abort ();
+
+ switch (*c)
+ {
+ case 'r':
+ {
+ long reg;
+
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ break;
+ case 'd':
+ {
+ long reg;
+
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+
+ func (stream, "%d", reg);
+ }
+ break;
+ case 'x':
+ {
+ long reg;
+
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+
+ func (stream, "0x%08x", reg);
+
+ /* Some SWI instructions have special
+ meanings. */
+ if ((given & 0x0fffffff) == 0x0FF00000)
+ func (stream, "\t; IMB");
+ else if ((given & 0x0fffffff) == 0x0FF00001)
+ func (stream, "\t; IMBRange");
+ }
+ break;
+ case 'X':
+ {
+ long reg;
+
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+
+ func (stream, "%01x", reg & 0xf);
+ }
+ break;
+ case 'f':
+ {
+ long reg;
+
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+
+ if (reg > 7)
+ func (stream, "#%s",
+ arm_fp_const[reg & 7]);
+ else
+ func (stream, "f%d", reg);
+ }
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case 'y':
+ case 'z':
+ {
+ int single = *c == 'y';
+ int regno;
+
+ switch (bitstart)
+ {
+ case 4: /* Sm pair */
+ func (stream, "{");
+ /* Fall through. */
+ case 0: /* Sm, Dm */
+ regno = given & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 5) & 1;
+ }
+ break;
+
+ case 1: /* Sd, Dd */
+ regno = (given >> 12) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 22) & 1;
+ }
+ break;
+
+ case 2: /* Sn, Dn */
+ regno = (given >> 16) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 7) & 1;
+ }
+ break;
+
+ case 3: /* List */
+ func (stream, "{");
+ regno = (given >> 12) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 22) & 1;
+ }
+ break;
+
+
+ default:
+ abort ();
+ }
+
+ func (stream, "%c%d", single ? 's' : 'd', regno);
+
+ if (bitstart == 3)
+ {
+ int count = given & 0xff;
+
+ if (single == 0)
+ count >>= 1;
+
+ if (--count)
+ {
+ func (stream, "-%c%d",
+ single ? 's' : 'd',
+ regno + count);
+ }
+
+ func (stream, "}");
+ }
+ else if (bitstart == 4)
+ func (stream, ", %c%d}", single ? 's' : 'd',
+ regno + 1);
+
+ break;
+ }
+
+ case '`':
+ c++;
+ if ((given & (1 << bitstart)) == 0)
+ func (stream, "%c", *c);
+ break;
+ case '\'':
+ c++;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c);
+ break;
+ case '?':
+ ++c;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c++);
+ else
+ func (stream, "%c", *++c);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ else
+ func (stream, "%c", *c);
+ }
+ return 4;
+ }
+ }
+ abort ();
+}
+
+/* Print one instruction from PC on INFO->STREAM.
+ Return the size of the instruction. */
+
+static int
+print_insn_thumb (pc, info, given)
+ bfd_vma pc;
+ struct disassemble_info * info;
+ long given;
+{
+ struct thumb_opcode * insn;
+ void * stream = info->stream;
+ fprintf_ftype func = info->fprintf_func;
+
+ for (insn = thumb_opcodes; insn->assembler; insn++)
+ {
+ if ((given & insn->mask) == insn->value)
+ {
+ char * c = insn->assembler;
+
+ /* Special processing for Thumb 2 instruction BL sequence: */
+ if (!*c) /* Check for empty (not NULL) assembler string. */
+ {
+ long offset;
+
+ info->bytes_per_chunk = 4;
+ info->bytes_per_line = 4;
+
+ offset = BDISP23 (given);
+ offset = offset * 2 + pc + 4;
+
+ if ((given & 0x10000000) == 0)
+ {
+ func (stream, "blx\t");
+ offset &= 0xfffffffc;
+ }
+ else
+ func (stream, "bl\t");
+
+ info->print_address_func (offset, info);
+ return 4;
+ }
+ else
+ {
+ info->bytes_per_chunk = 2;
+ info->bytes_per_line = 4;
+
+ given &= 0xffff;
+
+ for (; *c; c++)
+ {
+ if (*c == '%')
+ {
+ int domaskpc = 0;
+ int domasklr = 0;
+
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'S':
+ {
+ long reg;
+
+ reg = (given >> 3) & 0x7;
+ if (given & (1 << 6))
+ reg += 8;
+
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ break;
+
+ case 'D':
+ {
+ long reg;
+
+ reg = given & 0x7;
+ if (given & (1 << 7))
+ reg += 8;
+
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ break;
+
+ case 'T':
+ func (stream, "%s",
+ arm_conditional [(given >> 8) & 0xf]);
+ break;
+
+ case 'N':
+ if (given & (1 << 8))
+ domasklr = 1;
+ /* Fall through. */
+ case 'O':
+ if (*c == 'O' && (given & (1 << 8)))
+ domaskpc = 1;
+ /* Fall through. */
+ case 'M':
+ {
+ int started = 0;
+ int reg;
+
+ func (stream, "{");
+
+ /* It would be nice if we could spot
+ ranges, and generate the rS-rE format: */
+ for (reg = 0; (reg < 8); reg++)
+ if ((given & (1 << reg)) != 0)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[reg]);
+ }
+
+ if (domasklr)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, arm_regnames[14] /* "lr" */);
+ }
+
+ if (domaskpc)
+ {
+ if (started)
+ func (stream, ", ");
+ func (stream, arm_regnames[15] /* "pc" */);
+ }
+
+ func (stream, "}");
+ }
+ break;
+
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int bitstart = *c++ - '0';
+ int bitend = 0;
+
+ while (*c >= '0' && *c <= '9')
+ bitstart = (bitstart * 10) + *c++ - '0';
+
+ switch (*c)
+ {
+ case '-':
+ {
+ long reg;
+
+ c++;
+ while (*c >= '0' && *c <= '9')
+ bitend = (bitend * 10) + *c++ - '0';
+ if (!bitend)
+ abort ();
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+ switch (*c)
+ {
+ case 'r':
+ func (stream, "%s", arm_regnames[reg]);
+ break;
+
+ case 'd':
+ func (stream, "%d", reg);
+ break;
+
+ case 'H':
+ func (stream, "%d", reg << 1);
+ break;
+
+ case 'W':
+ func (stream, "%d", reg << 2);
+ break;
+
+ case 'a':
+ /* PC-relative address -- the bottom two
+ bits of the address are dropped
+ before the calculation. */
+ info->print_address_func
+ (((pc + 4) & ~3) + (reg << 2), info);
+ break;
+
+ case 'x':
+ func (stream, "0x%04x", reg);
+ break;
+
+ case 'I':
+ reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+ func (stream, "%d", reg);
+ break;
+
+ case 'B':
+ reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+ (*info->print_address_func)
+ (reg * 2 + pc + 4, info);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ case '\'':
+ c++;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c);
+ break;
+
+ case '?':
+ ++c;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c++);
+ else
+ func (stream, "%c", *++c);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ else
+ func (stream, "%c", *c);
+ }
+ }
+ return 2;
+ }
+ }
+
+ /* No match. */
+ abort ();
+}
+
+/* Parse an individual disassembler option. */
+
+void
+parse_arm_disassembler_option (option)
+ char * option;
+{
+ if (option == NULL)
+ return;
+
+ if (strneq (option, "reg-names-", 10))
+ {
+ int i;
+
+ option += 10;
+
+ for (i = NUM_ARM_REGNAMES; i--;)
+ if (streq (option, regnames[i].name))
+ {
+ regname_selected = i;
+ break;
+ }
+
+ if (i < 0)
+ fprintf (stderr, _("Unrecognised register name set: %s\n"), option);
+ }
+ else if (streq (option, "force-thumb"))
+ force_thumb = 1;
+ else if (streq (option, "no-force-thumb"))
+ force_thumb = 0;
+ else
+ fprintf (stderr, _("Unrecognised disassembler option: %s\n"), option);
+
+ return;
+}
+
+/* Parse the string of disassembler options, spliting it at whitespaces. */
+
+static void
+parse_disassembler_options (options)
+ char * options;
+{
+ char * space;
+
+ if (options == NULL)
+ return;
+
+ do
+ {
+ space = strchr (options, ' ');
+
+ if (space)
+ {
+ * space = '\0';
+ parse_arm_disassembler_option (options);
+ * space = ' ';
+ options = space + 1;
+ }
+ else
+ parse_arm_disassembler_option (options);
+ }
+ while (space);
+}
+
+/* NOTE: There are no checks in these routines that
+ the relevant number of data bytes exist. */
+
+int
+print_insn_arm (pc, info)
+ bfd_vma pc;
+ struct disassemble_info * info;
+{
+ unsigned char b[4];
+ long given;
+ int status;
+ int is_thumb;
+ int little;
+
+ if (info->disassembler_options)
+ {
+ parse_disassembler_options (info->disassembler_options);
+
+ /* To avoid repeated parsing of these options, we remove them here. */
+ info->disassembler_options = NULL;
+ }
+
+ is_thumb = force_thumb;
+ if (pc & 1)
+ {
+ is_thumb = 1;
+ pc &= ~(bfd_vma) 1;
+ }
+
+#if 0
+ if (!is_thumb && info->symbols != NULL)
+ {
+ if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour)
+ {
+ coff_symbol_type * cs;
+
+ cs = coffsymbol (*info->symbols);
+ is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT
+ || cs->native->u.syment.n_sclass == C_THUMBSTAT
+ || cs->native->u.syment.n_sclass == C_THUMBLABEL
+ || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC
+ || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC);
+ }
+ else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour)
+ {
+ elf_symbol_type * es;
+ unsigned int type;
+
+ es = *(elf_symbol_type **)(info->symbols);
+ type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
+
+ is_thumb = (type == STT_ARM_TFUNC) || (type == STT_ARM_16BIT);
+ }
+ }
+#endif
+
+ little = (info->endian == BFD_ENDIAN_LITTLE);
+ info->bytes_per_chunk = 4;
+ info->display_endian = little ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG;
+
+ if (little)
+ {
+ status = info->read_memory_func (pc, (bfd_byte *) &b[0], 4, info);
+ if (status != 0 && is_thumb)
+ {
+ info->bytes_per_chunk = 2;
+
+ status = info->read_memory_func (pc, (bfd_byte *) b, 2, info);
+ b[3] = b[2] = 0;
+ }
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, pc, info);
+ return -1;
+ }
+
+ given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
+ }
+ else
+ {
+ status = info->read_memory_func
+ (pc & ~ 0x3, (bfd_byte *) &b[0], 4, info);
+ if (status != 0)
+ {
+ info->memory_error_func (status, pc, info);
+ return -1;
+ }
+
+ if (is_thumb)
+ {
+ if (pc & 0x2)
+ {
+ given = (b[2] << 8) | b[3];
+
+ status = info->read_memory_func
+ ((pc + 4) & ~ 0x3, (bfd_byte *) b, 4, info);
+ if (status != 0)
+ {
+ info->memory_error_func (status, pc + 4, info);
+ return -1;
+ }
+
+ given |= (b[0] << 24) | (b[1] << 16);
+ }
+ else
+ given = (b[0] << 8) | b[1] | (b[2] << 24) | (b[3] << 16);
+ }
+ else
+ given = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]);
+ }
+
+ if (info->flags & INSN_HAS_RELOC)
+ /* If the instruction has a reloc associated with it, then
+ the offset field in the instruction will actually be the
+ addend for the reloc. (We are using REL type relocs).
+ In such cases, we can ignore the pc when computing
+ addresses, since the addend is not currently pc-relative. */
+ pc = 0;
+ if (is_thumb)
+ status = print_insn_thumb (pc, info, given);
+ else
+ status = print_insn_arm1 (pc, info, given);
+
+ return status;
+}
+
+void
+print_arm_disassembler_options (FILE * stream)
+{
+ int i;
+
+ fprintf (stream, _("\n\
+The following ARM specific disassembler options are supported for use with\n\
+the -M switch:\n"));
+
+ for (i = NUM_ARM_REGNAMES; i--;)
+ fprintf (stream, " reg-names-%s %*c%s\n",
+ regnames[i].name,
+ (int)(14 - strlen (regnames[i].name)), ' ',
+ regnames[i].description);
+
+ fprintf (stream, " force-thumb Assume all insns are Thumb insns\n");
+ fprintf (stream, " no-force-thumb Examine preceeding label to determine an insn's type\n\n");
+}
diff --git a/arm.ld b/arm.ld
new file mode 100644
index 0000000..61f4c34
--- /dev/null
+++ b/arm.ld
@@ -0,0 +1,128 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",
+ "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
new file mode 100644
index 0000000..71e5235
--- /dev/null
+++ b/audio/alsaaudio.c
@@ -0,0 +1,974 @@
+/*
+ * QEMU ALSA audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <alsa/asoundlib.h>
+#include "vl.h"
+
+#define AUDIO_CAP "alsa"
+#include "audio_int.h"
+
+typedef struct ALSAVoiceOut {
+ HWVoiceOut hw;
+ void *pcm_buf;
+ snd_pcm_t *handle;
+} ALSAVoiceOut;
+
+typedef struct ALSAVoiceIn {
+ HWVoiceIn hw;
+ snd_pcm_t *handle;
+ void *pcm_buf;
+} ALSAVoiceIn;
+
+static struct {
+ int size_in_usec_in;
+ int size_in_usec_out;
+ const char *pcm_name_in;
+ const char *pcm_name_out;
+ unsigned int buffer_size_in;
+ unsigned int period_size_in;
+ unsigned int buffer_size_out;
+ unsigned int period_size_out;
+ unsigned int threshold;
+
+ int buffer_size_in_overriden;
+ int period_size_in_overriden;
+
+ int buffer_size_out_overriden;
+ int period_size_out_overriden;
+ int verbose;
+} conf = {
+#ifdef HIGH_LATENCY
+ .size_in_usec_in = 1,
+ .size_in_usec_out = 1,
+#endif
+ .pcm_name_out = "default",
+ .pcm_name_in = "default",
+#ifdef HIGH_LATENCY
+ .buffer_size_in = 400000,
+ .period_size_in = 400000 / 4,
+ .buffer_size_out = 400000,
+ .period_size_out = 400000 / 4,
+#else
+#define DEFAULT_BUFFER_SIZE 1024
+#define DEFAULT_PERIOD_SIZE 256
+ .buffer_size_in = DEFAULT_BUFFER_SIZE * 4,
+ .period_size_in = DEFAULT_PERIOD_SIZE * 4,
+ .buffer_size_out = DEFAULT_BUFFER_SIZE,
+ .period_size_out = DEFAULT_PERIOD_SIZE,
+ .buffer_size_in_overriden = 0,
+ .buffer_size_out_overriden = 0,
+ .period_size_in_overriden = 0,
+ .period_size_out_overriden = 0,
+#endif
+ .threshold = 0,
+ .verbose = 0
+};
+
+struct alsa_params_req {
+ int freq;
+ audfmt_e fmt;
+ int nchannels;
+ unsigned int buffer_size;
+ unsigned int period_size;
+};
+
+struct alsa_params_obt {
+ int freq;
+ audfmt_e fmt;
+ int nchannels;
+ snd_pcm_uframes_t samples;
+};
+
+static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
+ int err,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+}
+
+static void alsa_anal_close (snd_pcm_t **handlep)
+{
+ int err = snd_pcm_close (*handlep);
+ if (err) {
+ alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
+ }
+ *handlep = NULL;
+}
+
+static int alsa_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int aud_to_alsafmt (audfmt_e fmt)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ return SND_PCM_FORMAT_S8;
+
+ case AUD_FMT_U8:
+ return SND_PCM_FORMAT_U8;
+
+ case AUD_FMT_S16:
+ return SND_PCM_FORMAT_S16_LE;
+
+ case AUD_FMT_U16:
+ return SND_PCM_FORMAT_U16_LE;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return SND_PCM_FORMAT_U8;
+ }
+}
+
+static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
+{
+ switch (alsafmt) {
+ case SND_PCM_FORMAT_S8:
+ *endianness = 0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case SND_PCM_FORMAT_U8:
+ *endianness = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case SND_PCM_FORMAT_U16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case SND_PCM_FORMAT_U16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ default:
+ dolog ("Unrecognized audio format %d\n", alsafmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined DEBUG_MISMATCHES || defined DEBUG
+static void alsa_dump_info (struct alsa_params_req *req,
+ struct alsa_params_obt *obt)
+{
+ dolog ("parameter | requested value | obtained value\n");
+ dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
+ dolog ("channels | %10d | %10d\n",
+ req->nchannels, obt->nchannels);
+ dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
+ dolog ("============================================\n");
+ dolog ("requested: buffer size %d period size %d\n",
+ req->buffer_size, req->period_size);
+ dolog ("obtained: samples %ld\n", obt->samples);
+}
+#endif
+
+static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
+{
+ int err;
+ snd_pcm_sw_params_t *sw_params;
+
+ snd_pcm_sw_params_alloca (&sw_params);
+
+ err = snd_pcm_sw_params_current (handle, sw_params);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to get current software parameters\n");
+ return;
+ }
+
+ err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to set software threshold to %ld\n",
+ threshold);
+ return;
+ }
+
+ err = snd_pcm_sw_params (handle, sw_params);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to set software parameters\n");
+ return;
+ }
+}
+
+static int alsa_open (int in, struct alsa_params_req *req,
+ struct alsa_params_obt *obt, snd_pcm_t **handlep)
+{
+ snd_pcm_t *handle;
+ snd_pcm_hw_params_t *hw_params;
+ int err, freq, nchannels;
+ const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
+ unsigned int period_size, buffer_size;
+ snd_pcm_uframes_t obt_buffer_size;
+ const char *typ = in ? "ADC" : "DAC";
+
+ freq = req->freq;
+ period_size = req->period_size;
+ buffer_size = req->buffer_size;
+ nchannels = req->nchannels;
+
+ snd_pcm_hw_params_alloca (&hw_params);
+
+ err = snd_pcm_open (
+ &handle,
+ pcm_name,
+ in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
+ return -1;
+ }
+
+ err = snd_pcm_hw_params_any (handle, hw_params);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_access (
+ handle,
+ hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set access type\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_channels_near (
+ handle,
+ hw_params,
+ &nchannels
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
+ req->nchannels);
+ goto err;
+ }
+
+ if (nchannels != 1 && nchannels != 2) {
+ alsa_logerr2 (err, typ,
+ "Can not handle obtained number of channels %d\n",
+ nchannels);
+ goto err;
+ }
+
+ if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
+ if (!buffer_size) {
+ buffer_size = DEFAULT_BUFFER_SIZE;
+ period_size= DEFAULT_PERIOD_SIZE;
+ }
+ }
+
+ if (buffer_size) {
+ if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
+ if (period_size) {
+ err = snd_pcm_hw_params_set_period_time_near (
+ handle,
+ hw_params,
+ &period_size,
+ 0
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ,
+ "Failed to set period time %d\n",
+ req->period_size);
+ goto err;
+ }
+ }
+
+ err = snd_pcm_hw_params_set_buffer_time_near (
+ handle,
+ hw_params,
+ &buffer_size,
+ 0
+ );
+
+ if (err < 0) {
+ alsa_logerr2 (err, typ,
+ "Failed to set buffer time %d\n",
+ req->buffer_size);
+ goto err;
+ }
+ }
+ else {
+ int dir;
+ snd_pcm_uframes_t minval;
+
+ if (period_size) {
+ minval = period_size;
+ dir = 0;
+
+ err = snd_pcm_hw_params_get_period_size_min (
+ hw_params,
+ &minval,
+ &dir
+ );
+ if (err < 0) {
+ alsa_logerr (
+ err,
+ "Could not get minmal period size for %s\n",
+ typ
+ );
+ }
+ else {
+ if (period_size < minval) {
+ if ((in && conf.period_size_in_overriden)
+ || (!in && conf.period_size_out_overriden)) {
+ dolog ("%s period size(%d) is less "
+ "than minmal period size(%ld)\n",
+ typ,
+ period_size,
+ minval);
+ }
+ period_size = minval;
+ }
+ }
+
+ err = snd_pcm_hw_params_set_period_size (
+ handle,
+ hw_params,
+ period_size,
+ 0
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set period size %d\n",
+ req->period_size);
+ goto err;
+ }
+ }
+
+ minval = buffer_size;
+ err = snd_pcm_hw_params_get_buffer_size_min (
+ hw_params,
+ &minval
+ );
+ if (err < 0) {
+ alsa_logerr (err, "Could not get minmal buffer size for %s\n",
+ typ);
+ }
+ else {
+ if (buffer_size < minval) {
+ if ((in && conf.buffer_size_in_overriden)
+ || (!in && conf.buffer_size_out_overriden)) {
+ dolog (
+ "%s buffer size(%d) is less "
+ "than minimal buffer size(%ld)\n",
+ typ,
+ buffer_size,
+ minval
+ );
+ }
+ buffer_size = minval;
+ }
+ }
+
+ err = snd_pcm_hw_params_set_buffer_size (
+ handle,
+ hw_params,
+ buffer_size
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
+ req->buffer_size);
+ goto err;
+ }
+ }
+ }
+ else {
+ dolog ("warning: Buffer size is not set\n");
+ }
+
+ err = snd_pcm_hw_params (handle, hw_params);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to get buffer size\n");
+ goto err;
+ }
+
+ err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
+ goto err;
+ }
+
+ if (!in && conf.threshold) {
+ snd_pcm_uframes_t threshold;
+ int bytes_per_sec;
+
+ bytes_per_sec = freq
+ << (nchannels == 2)
+ << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
+
+ threshold = (conf.threshold * bytes_per_sec) / 1000;
+ alsa_set_threshold (handle, threshold);
+ }
+
+ obt->fmt = req->fmt;
+ obt->nchannels = nchannels;
+ obt->freq = freq;
+ obt->samples = obt_buffer_size;
+ *handlep = handle;
+
+#if defined DEBUG_MISMATCHES || defined DEBUG
+ if (obt->fmt != req->fmt ||
+ obt->nchannels != req->nchannels ||
+ obt->freq != req->freq) {
+ dolog ("Audio paramters mismatch for %s\n", typ);
+ alsa_dump_info (req, obt);
+ }
+#endif
+
+#ifdef DEBUG
+ alsa_dump_info (req, obt);
+#endif
+ return 0;
+
+ err:
+ alsa_anal_close (&handle);
+ return -1;
+}
+
+static int alsa_recover (snd_pcm_t *handle)
+{
+ int err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Failed to prepare handle %p\n", handle);
+ return -1;
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
+{
+ snd_pcm_sframes_t avail;
+
+ avail = snd_pcm_avail_update (handle);
+ if (avail < 0) {
+ if (avail == -EPIPE) {
+ if (!alsa_recover (handle)) {
+ avail = snd_pcm_avail_update (handle);
+ }
+ }
+
+ if (avail < 0) {
+ alsa_logerr (avail,
+ "Could not obtain number of available frames\n");
+ return -1;
+ }
+ }
+
+ return avail;
+}
+
+static int alsa_run_out (HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ int rpos, live, decr;
+ int samples;
+ uint8_t *dst;
+ st_sample_t *src;
+ snd_pcm_sframes_t avail;
+
+ live = audio_pcm_hw_get_live_out (hw);
+ if (!live) {
+ return 0;
+ }
+
+ avail = alsa_get_avail (alsa->handle);
+ if (avail < 0) {
+ dolog ("Could not get number of available playback frames\n");
+ return 0;
+ }
+
+ decr = audio_MIN (live, avail);
+ samples = decr;
+ rpos = hw->rpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
+ int len = audio_MIN (samples, left_till_end_samples);
+ snd_pcm_sframes_t written;
+
+ src = hw->mix_buf + rpos;
+ dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
+
+ hw->clip (dst, src, len);
+
+ while (len) {
+ written = snd_pcm_writei (alsa->handle, dst, len);
+
+ if (written <= 0) {
+ switch (written) {
+ case 0:
+ if (conf.verbose) {
+ dolog ("Failed to write %d frames (wrote zero)\n", len);
+ }
+ goto exit;
+
+ case -EPIPE:
+ if (alsa_recover (alsa->handle)) {
+ alsa_logerr (written, "Failed to write %d frames\n",
+ len);
+ goto exit;
+ }
+ if (conf.verbose) {
+ dolog ("Recovering from playback xrun\n");
+ }
+ continue;
+
+ case -EAGAIN:
+ goto exit;
+
+ default:
+ alsa_logerr (written, "Failed to write %d frames to %p\n",
+ len, dst);
+ goto exit;
+ }
+ }
+
+ rpos = (rpos + written) % hw->samples;
+ samples -= written;
+ len -= written;
+ dst = advance (dst, written << hw->info.shift);
+ src += written;
+ }
+ }
+
+ exit:
+ hw->rpos = rpos;
+ return decr;
+}
+
+static void alsa_fini_out (HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ ldebug ("alsa_fini\n");
+ alsa_anal_close (&alsa->handle);
+
+ if (alsa->pcm_buf) {
+ qemu_free (alsa->pcm_buf);
+ alsa->pcm_buf = NULL;
+ }
+}
+
+static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ struct alsa_params_req req;
+ struct alsa_params_obt obt;
+ audfmt_e effective_fmt;
+ int endianness;
+ int err;
+ snd_pcm_t *handle;
+ audsettings_t obt_as;
+
+ req.fmt = aud_to_alsafmt (as->fmt);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.period_size = conf.period_size_out;
+ req.buffer_size = conf.buffer_size_out;
+
+ if (alsa_open (0, &req, &obt, &handle)) {
+ return -1;
+ }
+
+ err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ alsa_anal_close (&handle);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
+ if (!alsa->pcm_buf) {
+ dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ alsa_anal_close (&handle);
+ return -1;
+ }
+
+ alsa->handle = handle;
+ return 0;
+}
+
+static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
+{
+ int err;
+
+ if (pause) {
+ err = snd_pcm_drop (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not stop %s\n", typ);
+ return -1;
+ }
+ }
+ else {
+ err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not prepare handle for %s\n", typ);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ ldebug ("enabling voice\n");
+ return alsa_voice_ctl (alsa->handle, "playback", 0);
+
+ case VOICE_DISABLE:
+ ldebug ("disabling voice\n");
+ return alsa_voice_ctl (alsa->handle, "playback", 1);
+ }
+
+ return -1;
+}
+
+static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+ struct alsa_params_req req;
+ struct alsa_params_obt obt;
+ int endianness;
+ int err;
+ audfmt_e effective_fmt;
+ snd_pcm_t *handle;
+ audsettings_t obt_as;
+
+ req.fmt = aud_to_alsafmt (as->fmt);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.period_size = conf.period_size_in;
+ req.buffer_size = conf.buffer_size_in;
+
+ if (alsa_open (1, &req, &obt, &handle)) {
+ return -1;
+ }
+
+ err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ alsa_anal_close (&handle);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!alsa->pcm_buf) {
+ dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ alsa_anal_close (&handle);
+ return -1;
+ }
+
+ alsa->handle = handle;
+ return 0;
+}
+
+static void alsa_fini_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ alsa_anal_close (&alsa->handle);
+
+ if (alsa->pcm_buf) {
+ qemu_free (alsa->pcm_buf);
+ alsa->pcm_buf = NULL;
+ }
+}
+
+static int alsa_run_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int i;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ int decr;
+ struct {
+ int add;
+ int len;
+ } bufs[2] = {
+ { hw->wpos, 0 },
+ { 0, 0 }
+ };
+ snd_pcm_sframes_t avail;
+ snd_pcm_uframes_t read_samples = 0;
+
+ if (!dead) {
+ return 0;
+ }
+
+ avail = alsa_get_avail (alsa->handle);
+ if (avail < 0) {
+ dolog ("Could not get number of captured frames\n");
+ return 0;
+ }
+
+ if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
+ avail = hw->samples;
+ }
+
+ decr = audio_MIN (dead, avail);
+ if (!decr) {
+ return 0;
+ }
+
+ if (hw->wpos + decr > hw->samples) {
+ bufs[0].len = (hw->samples - hw->wpos);
+ bufs[1].len = (decr - (hw->samples - hw->wpos));
+ }
+ else {
+ bufs[0].len = decr;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ void *src;
+ st_sample_t *dst;
+ snd_pcm_sframes_t nread;
+ snd_pcm_uframes_t len;
+
+ len = bufs[i].len;
+
+ src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
+ dst = hw->conv_buf + bufs[i].add;
+
+ while (len) {
+ nread = snd_pcm_readi (alsa->handle, src, len);
+
+ if (nread <= 0) {
+ switch (nread) {
+ case 0:
+ if (conf.verbose) {
+ dolog ("Failed to read %ld frames (read zero)\n", len);
+ }
+ goto exit;
+
+ case -EPIPE:
+ if (alsa_recover (alsa->handle)) {
+ alsa_logerr (nread, "Failed to read %ld frames\n", len);
+ goto exit;
+ }
+ if (conf.verbose) {
+ dolog ("Recovering from capture xrun\n");
+ }
+ continue;
+
+ case -EAGAIN:
+ goto exit;
+
+ default:
+ alsa_logerr (
+ nread,
+ "Failed to read %ld frames from %p\n",
+ len,
+ src
+ );
+ goto exit;
+ }
+ }
+
+ hw->conv (dst, src, nread, &nominal_volume);
+
+ src = advance (src, nread << hwshift);
+ dst += nread;
+
+ read_samples += nread;
+ len -= nread;
+ }
+ }
+
+ exit:
+ hw->wpos = (hw->wpos + read_samples) % hw->samples;
+ return read_samples;
+}
+
+static int alsa_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ ldebug ("enabling voice\n");
+ return alsa_voice_ctl (alsa->handle, "capture", 0);
+
+ case VOICE_DISABLE:
+ ldebug ("disabling voice\n");
+ return alsa_voice_ctl (alsa->handle, "capture", 1);
+ }
+
+ return -1;
+}
+
+static void *alsa_audio_init (void)
+{
+ return &conf;
+}
+
+static void alsa_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option alsa_options[] = {
+ {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
+ "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+ {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
+ "DAC period size", &conf.period_size_out_overriden, 0},
+ {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
+ "DAC buffer size", &conf.buffer_size_out_overriden, 0},
+
+ {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
+ "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+ {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
+ "ADC period size", &conf.period_size_in_overriden, 0},
+ {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
+ "ADC buffer size", &conf.buffer_size_in_overriden, 0},
+
+ {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
+ "(undocumented)", NULL, 0},
+
+ {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
+ "DAC device name (for instance dmix)", NULL, 0},
+
+ {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
+ "ADC device name", NULL, 0},
+
+ {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
+ "Behave in a more verbose way", NULL, 0},
+
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops alsa_pcm_ops = {
+ alsa_init_out,
+ alsa_fini_out,
+ alsa_run_out,
+ alsa_write,
+ alsa_ctl_out,
+
+ alsa_init_in,
+ alsa_fini_in,
+ alsa_run_in,
+ alsa_read,
+ alsa_ctl_in
+};
+
+struct audio_driver alsa_audio_driver = {
+ INIT_FIELD (name = ) "alsa",
+ INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
+ INIT_FIELD (options = ) alsa_options,
+ INIT_FIELD (init = ) alsa_audio_init,
+ INIT_FIELD (fini = ) alsa_audio_fini,
+ INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
+};
diff --git a/audio/audio.c b/audio/audio.c
new file mode 100644
index 0000000..8e7af1a
--- /dev/null
+++ b/audio/audio.c
@@ -0,0 +1,1871 @@
+/*
+ * QEMU Audio subsystem
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define AUDIO_CAP "audio"
+#include "audio_int.h"
+
+/* #define DEBUG_PLIVE */
+/* #define DEBUG_LIVE */
+/* #define DEBUG_OUT */
+/* #define DEBUG_CAPTURE */
+
+#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
+
+static struct audio_driver *drvtab[] = {
+#ifdef CONFIG_OSS
+ &oss_audio_driver,
+#endif
+#ifdef CONFIG_ALSA
+ &alsa_audio_driver,
+#endif
+#ifdef CONFIG_COREAUDIO
+ &coreaudio_audio_driver,
+#endif
+#ifdef CONFIG_DSOUND
+ &dsound_audio_driver,
+#endif
+#ifdef CONFIG_FMOD
+ &fmod_audio_driver,
+#endif
+#ifdef CONFIG_SDL
+ &sdl_audio_driver,
+#endif
+ &no_audio_driver,
+ &wav_audio_driver
+};
+
+struct fixed_settings {
+ int enabled;
+ int nb_voices;
+ int greedy;
+ audsettings_t settings;
+};
+
+static struct {
+ struct fixed_settings fixed_out;
+ struct fixed_settings fixed_in;
+ union {
+ int hz;
+ int64_t ticks;
+ } period;
+ int plive;
+ int log_to_monitor;
+} conf = {
+ { /* DAC fixed settings */
+ 1, /* enabled */
+ 1, /* nb_voices */
+ 1, /* greedy */
+ {
+ 44100, /* freq */
+ 2, /* nchannels */
+ AUD_FMT_S16 /* fmt */
+ }
+ },
+
+ { /* ADC fixed settings */
+ 1, /* enabled */
+ 1, /* nb_voices */
+ 1, /* greedy */
+ {
+ 44100, /* freq */
+ 2, /* nchannels */
+ AUD_FMT_S16 /* fmt */
+ }
+ },
+
+ { 0 }, /* period */
+ 0, /* plive */
+ 0 /* log_to_monitor */
+};
+
+static AudioState glob_audio_state;
+
+volume_t nominal_volume = {
+ 0,
+#ifdef FLOAT_MIXENG
+ 1.0,
+ 1.0
+#else
+ UINT_MAX,
+ UINT_MAX
+#endif
+};
+
+/* http://www.df.lth.se/~john_e/gems/gem002d.html */
+/* http://www.multi-platforms.com/Tips/PopCount.htm */
+uint32_t popcount (uint32_t u)
+{
+ u = ((u&0x55555555) + ((u>>1)&0x55555555));
+ u = ((u&0x33333333) + ((u>>2)&0x33333333));
+ u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+ u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+ u = ( u&0x0000ffff) + (u>>16);
+ return u;
+}
+
+inline uint32_t lsbindex (uint32_t u)
+{
+ return popcount ((u&-u)-1);
+}
+
+#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
+#error No its not
+#else
+int audio_bug (const char *funcname, int cond)
+{
+ if (cond) {
+ static int shown;
+
+ AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
+ if (!shown) {
+ shown = 1;
+ AUD_log (NULL, "Save all your work and restart without audio\n");
+ AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n");
+ AUD_log (NULL, "I am sorry\n");
+ }
+ AUD_log (NULL, "Context:\n");
+
+#if defined AUDIO_BREAKPOINT_ON_BUG
+# if defined HOST_I386
+# if defined __GNUC__
+ __asm__ ("int3");
+# elif defined _MSC_VER
+ _asm _emit 0xcc;
+# else
+ abort ();
+# endif
+# else
+ abort ();
+# endif
+#endif
+ }
+
+ return cond;
+}
+#endif
+
+void *audio_calloc (const char *funcname, int nmemb, size_t size)
+{
+ int cond;
+ size_t len;
+
+ len = nmemb * size;
+ cond = !nmemb || !size;
+ cond |= nmemb < 0;
+ cond |= len < size;
+
+ if (audio_bug ("audio_calloc", cond)) {
+ AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
+ funcname);
+ AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
+ return NULL;
+ }
+
+ return qemu_mallocz (len);
+}
+
+static char *audio_alloc_prefix (const char *s)
+{
+ const char qemu_prefix[] = "QEMU_";
+ size_t len;
+ char *r;
+
+ if (!s) {
+ return NULL;
+ }
+
+ len = strlen (s);
+ r = qemu_malloc (len + sizeof (qemu_prefix));
+
+ if (r) {
+ size_t i;
+ char *u = r + sizeof (qemu_prefix) - 1;
+
+ strcpy (r, qemu_prefix);
+ strcat (r, s);
+
+ for (i = 0; i < len; ++i) {
+ u[i] = toupper (u[i]);
+ }
+ }
+ return r;
+}
+
+const char *audio_audfmt_to_string (audfmt_e fmt)
+{
+ switch (fmt) {
+ case AUD_FMT_U8:
+ return "U8";
+
+ case AUD_FMT_U16:
+ return "U16";
+
+ case AUD_FMT_S8:
+ return "S8";
+
+ case AUD_FMT_S16:
+ return "S16";
+ }
+
+ dolog ("Bogus audfmt %d returning S16\n", fmt);
+ return "S16";
+}
+
+audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, int *defaultp)
+{
+ if (!strcasecmp (s, "u8")) {
+ *defaultp = 0;
+ return AUD_FMT_U8;
+ }
+ else if (!strcasecmp (s, "u16")) {
+ *defaultp = 0;
+ return AUD_FMT_U16;
+ }
+ else if (!strcasecmp (s, "s8")) {
+ *defaultp = 0;
+ return AUD_FMT_S8;
+ }
+ else if (!strcasecmp (s, "s16")) {
+ *defaultp = 0;
+ return AUD_FMT_S16;
+ }
+ else {
+ dolog ("Bogus audio format `%s' using %s\n",
+ s, audio_audfmt_to_string (defval));
+ *defaultp = 1;
+ return defval;
+ }
+}
+
+static audfmt_e audio_get_conf_fmt (const char *envname,
+ audfmt_e defval,
+ int *defaultp)
+{
+ const char *var = getenv (envname);
+ if (!var) {
+ *defaultp = 1;
+ return defval;
+ }
+ return audio_string_to_audfmt (var, defval, defaultp);
+}
+
+static int audio_get_conf_int (const char *key, int defval, int *defaultp)
+{
+ int val;
+ char *strval;
+
+ strval = getenv (key);
+ if (strval) {
+ *defaultp = 0;
+ val = atoi (strval);
+ return val;
+ }
+ else {
+ *defaultp = 1;
+ return defval;
+ }
+}
+
+static const char *audio_get_conf_str (const char *key,
+ const char *defval,
+ int *defaultp)
+{
+ const char *val = getenv (key);
+ if (!val) {
+ *defaultp = 1;
+ return defval;
+ }
+ else {
+ *defaultp = 0;
+ return val;
+ }
+}
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap)
+{
+ if (conf.log_to_monitor) {
+ if (cap) {
+ term_printf ("%s: ", cap);
+ }
+
+ term_vprintf (fmt, ap);
+ }
+ else {
+ if (cap) {
+ fprintf (stderr, "%s: ", cap);
+ }
+
+ vfprintf (stderr, fmt, ap);
+ }
+}
+
+void AUD_log (const char *cap, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (cap, fmt, ap);
+ va_end (ap);
+}
+
+static void audio_print_options (const char *prefix,
+ struct audio_option *opt)
+{
+ char *uprefix;
+
+ if (!prefix) {
+ dolog ("No prefix specified\n");
+ return;
+ }
+
+ if (!opt) {
+ dolog ("No options\n");
+ return;
+ }
+
+ uprefix = audio_alloc_prefix (prefix);
+
+ for (; opt->name; opt++) {
+ const char *state = "default";
+ printf (" %s_%s: ", uprefix, opt->name);
+
+ if (opt->overridenp && *opt->overridenp) {
+ state = "current";
+ }
+
+ switch (opt->tag) {
+ case AUD_OPT_BOOL:
+ {
+ int *intp = opt->valp;
+ printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
+ }
+ break;
+
+ case AUD_OPT_INT:
+ {
+ int *intp = opt->valp;
+ printf ("integer, %s = %d\n", state, *intp);
+ }
+ break;
+
+ case AUD_OPT_FMT:
+ {
+ audfmt_e *fmtp = opt->valp;
+ printf (
+ "format, %s = %s, (one of: U8 S8 U16 S16)\n",
+ state,
+ audio_audfmt_to_string (*fmtp)
+ );
+ }
+ break;
+
+ case AUD_OPT_STR:
+ {
+ const char **strp = opt->valp;
+ printf ("string, %s = %s\n",
+ state,
+ *strp ? *strp : "(not set)");
+ }
+ break;
+
+ default:
+ printf ("???\n");
+ dolog ("Bad value tag for option %s_%s %d\n",
+ uprefix, opt->name, opt->tag);
+ break;
+ }
+ printf (" %s\n", opt->descr);
+ }
+
+ qemu_free (uprefix);
+}
+
+static void audio_process_options (const char *prefix,
+ struct audio_option *opt)
+{
+ char *optname;
+ const char qemu_prefix[] = "QEMU_";
+ size_t preflen;
+
+ if (audio_bug (AUDIO_FUNC, !prefix)) {
+ dolog ("prefix = NULL\n");
+ return;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !opt)) {
+ dolog ("opt = NULL\n");
+ return;
+ }
+
+ preflen = strlen (prefix);
+
+ for (; opt->name; opt++) {
+ size_t len, i;
+ int def;
+
+ if (!opt->valp) {
+ dolog ("Option value pointer for `%s' is not set\n",
+ opt->name);
+ continue;
+ }
+
+ len = strlen (opt->name);
+ /* len of opt->name + len of prefix + size of qemu_prefix
+ * (includes trailing zero) + zero + underscore (on behalf of
+ * sizeof) */
+ optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1);
+ if (!optname) {
+ dolog ("Could not allocate memory for option name `%s'\n",
+ opt->name);
+ continue;
+ }
+
+ strcpy (optname, qemu_prefix);
+
+ /* copy while upper-casing, including trailing zero */
+ for (i = 0; i <= preflen; ++i) {
+ optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]);
+ }
+ strcat (optname, "_");
+ strcat (optname, opt->name);
+
+ def = 1;
+ switch (opt->tag) {
+ case AUD_OPT_BOOL:
+ case AUD_OPT_INT:
+ {
+ int *intp = opt->valp;
+ *intp = audio_get_conf_int (optname, *intp, &def);
+ }
+ break;
+
+ case AUD_OPT_FMT:
+ {
+ audfmt_e *fmtp = opt->valp;
+ *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
+ }
+ break;
+
+ case AUD_OPT_STR:
+ {
+ const char **strp = opt->valp;
+ *strp = audio_get_conf_str (optname, *strp, &def);
+ }
+ break;
+
+ default:
+ dolog ("Bad value tag for option `%s' - %d\n",
+ optname, opt->tag);
+ break;
+ }
+
+ if (!opt->overridenp) {
+ opt->overridenp = &opt->overriden;
+ }
+ *opt->overridenp = !def;
+ qemu_free (optname);
+ }
+}
+
+static void audio_print_settings (audsettings_t *as)
+{
+ dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ AUD_log (NULL, "S8");
+ break;
+ case AUD_FMT_U8:
+ AUD_log (NULL, "U8");
+ break;
+ case AUD_FMT_S16:
+ AUD_log (NULL, "S16");
+ break;
+ case AUD_FMT_U16:
+ AUD_log (NULL, "U16");
+ break;
+ default:
+ AUD_log (NULL, "invalid(%d)", as->fmt);
+ break;
+ }
+
+ AUD_log (NULL, " endianness=");
+ switch (as->endianness) {
+ case 0:
+ AUD_log (NULL, "little");
+ break;
+ case 1:
+ AUD_log (NULL, "big");
+ break;
+ default:
+ AUD_log (NULL, "invalid");
+ break;
+ }
+ AUD_log (NULL, "\n");
+}
+
+static int audio_validate_settings (audsettings_t *as)
+{
+ int invalid;
+
+ invalid = as->nchannels != 1 && as->nchannels != 2;
+ invalid |= as->endianness != 0 && as->endianness != 1;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ break;
+ default:
+ invalid = 1;
+ break;
+ }
+
+ invalid |= as->freq <= 0;
+ return invalid ? -1 : 0;
+}
+
+static int audio_pcm_info_eq (struct audio_pcm_info *info, audsettings_t *as)
+{
+ int bits = 8, sign = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ sign = 1;
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ sign = 1;
+ case AUD_FMT_U16:
+ bits = 16;
+ break;
+ }
+ return info->freq == as->freq
+ && info->nchannels == as->nchannels
+ && info->sign == sign
+ && info->bits == bits
+ && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as)
+{
+ int bits = 8, sign = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ sign = 1;
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ sign = 1;
+ case AUD_FMT_U16:
+ bits = 16;
+ break;
+ }
+
+ info->freq = as->freq;
+ info->bits = bits;
+ info->sign = sign;
+ info->nchannels = as->nchannels;
+ info->shift = (as->nchannels == 2) + (bits == 16);
+ info->align = (1 << info->shift) - 1;
+ info->bytes_per_second = info->freq << info->shift;
+ info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
+{
+ if (!len) {
+ return;
+ }
+
+ if (info->sign) {
+ memset (buf, len << info->shift, 0x00);
+ }
+ else {
+ if (info->bits == 8) {
+ memset (buf, len << info->shift, 0x80);
+ }
+ else {
+ int i;
+ uint16_t *p = buf;
+ int shift = info->nchannels - 1;
+ short s = INT16_MAX;
+
+ if (info->swap_endianness) {
+ s = bswap16 (s);
+ }
+
+ for (i = 0; i < len << shift; i++) {
+ p[i] = s;
+ }
+ }
+ }
+}
+
+/*
+ * Capture
+ */
+static void noop_conv (st_sample_t *dst, const void *src,
+ int samples, volume_t *vol)
+{
+ (void) src;
+ (void) dst;
+ (void) samples;
+ (void) vol;
+}
+
+static CaptureVoiceOut *audio_pcm_capture_find_specific (
+ AudioState *s,
+ audsettings_t *as
+ )
+{
+ CaptureVoiceOut *cap;
+
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ if (audio_pcm_info_eq (&cap->hw.info, as)) {
+ return cap;
+ }
+ }
+ return NULL;
+}
+
+static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
+{
+ struct capture_callback *cb;
+
+#ifdef DEBUG_CAPTURE
+ dolog ("notification %d sent\n", cmd);
+#endif
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.notify (cb->opaque, cmd);
+ }
+}
+
+static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
+{
+ if (cap->hw.enabled != enabled) {
+ audcnotification_e cmd;
+ cap->hw.enabled = enabled;
+ cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
+ audio_notify_capture (cap, cmd);
+ }
+}
+
+static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
+{
+ HWVoiceOut *hw = &cap->hw;
+ SWVoiceOut *sw;
+ int enabled = 0;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ enabled = 1;
+ break;
+ }
+ }
+ audio_capture_maybe_changed (cap, enabled);
+}
+
+static void audio_detach_capture (HWVoiceOut *hw)
+{
+ SWVoiceCap *sc = hw->cap_head.lh_first;
+
+ while (sc) {
+ SWVoiceCap *sc1 = sc->entries.le_next;
+ SWVoiceOut *sw = &sc->sw;
+ CaptureVoiceOut *cap = sc->cap;
+ int was_active = sw->active;
+
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ sw->rate = NULL;
+ }
+
+ LIST_REMOVE (sw, entries);
+ LIST_REMOVE (sc, entries);
+ qemu_free (sc);
+ if (was_active) {
+ /* We have removed soft voice from the capture:
+ this might have changed the overall status of the capture
+ since this might have been the only active voice */
+ audio_recalc_and_notify_capture (cap);
+ }
+ sc = sc1;
+ }
+}
+
+static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
+{
+ CaptureVoiceOut *cap;
+
+ audio_detach_capture (hw);
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ SWVoiceCap *sc;
+ SWVoiceOut *sw;
+ HWVoiceOut *hw_cap = &cap->hw;
+
+ sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
+ if (!sc) {
+ dolog ("Could not allocate soft capture voice (%zu bytes)\n",
+ sizeof (*sc));
+ return -1;
+ }
+
+ sc->cap = cap;
+ sw = &sc->sw;
+ sw->hw = hw_cap;
+ sw->info = hw->info;
+ sw->empty = 1;
+ sw->active = hw->enabled;
+ sw->conv = noop_conv;
+ sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
+ sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
+ if (!sw->rate) {
+ dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
+ qemu_free (sw);
+ return -1;
+ }
+ LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
+ LIST_INSERT_HEAD (&hw->cap_head, sc, entries);
+#ifdef DEBUG_CAPTURE
+ asprintf (&sw->name, "for %p %d,%d,%d",
+ hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
+ dolog ("Added %s active = %d\n", sw->name, sw->active);
+#endif
+ if (sw->active) {
+ audio_capture_maybe_changed (cap, 1);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Hard voice (capture)
+ */
+static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
+{
+ SWVoiceIn *sw;
+ int m = hw->total_samples_captured;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ m = audio_MIN (m, sw->total_hw_samples_acquired);
+ }
+ }
+ return m;
+}
+
+int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
+{
+ int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+ return live;
+}
+
+/*
+ * Soft voice (capture)
+ */
+static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
+{
+ HWVoiceIn *hw = sw->hw;
+ int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+ int rpos;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+
+ rpos = hw->wpos - live;
+ if (rpos >= 0) {
+ return rpos;
+ }
+ else {
+ return hw->samples + rpos;
+ }
+}
+
+int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
+{
+ HWVoiceIn *hw = sw->hw;
+ int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
+ st_sample_t *src, *dst = sw->buf;
+
+ rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
+
+ live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+
+ samples = size >> sw->info.shift;
+ if (!live) {
+ return 0;
+ }
+
+ swlim = (live * sw->ratio) >> 32;
+ swlim = audio_MIN (swlim, samples);
+
+ while (swlim) {
+ src = hw->conv_buf + rpos;
+ isamp = hw->wpos - rpos;
+ /* XXX: <= ? */
+ if (isamp <= 0) {
+ isamp = hw->samples - rpos;
+ }
+
+ if (!isamp) {
+ break;
+ }
+ osamp = swlim;
+
+ if (audio_bug (AUDIO_FUNC, osamp < 0)) {
+ dolog ("osamp=%d\n", osamp);
+ return 0;
+ }
+
+ st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
+ swlim -= osamp;
+ rpos = (rpos + isamp) % hw->samples;
+ dst += osamp;
+ ret += osamp;
+ total += isamp;
+ }
+
+ sw->clip (buf, sw->buf, ret);
+ sw->total_hw_samples_acquired += total;
+ return ret << sw->info.shift;
+}
+
+/*
+ * Hard voice (playback)
+ */
+static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
+{
+ SWVoiceOut *sw;
+ int m = INT_MAX;
+ int nb_live = 0;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active || !sw->empty) {
+ m = audio_MIN (m, sw->total_hw_samples_mixed);
+ nb_live += 1;
+ }
+ }
+
+ *nb_livep = nb_live;
+ return m;
+}
+
+int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
+{
+ int smin;
+
+ smin = audio_pcm_hw_find_min_out (hw, nb_live);
+
+ if (!*nb_live) {
+ return 0;
+ }
+ else {
+ int live = smin;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+ return live;
+ }
+}
+
+int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
+{
+ int nb_live;
+ int live;
+
+ live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+ return live;
+}
+
+/*
+ * Soft voice (playback)
+ */
+int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
+{
+ int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+ int ret = 0, pos = 0, total = 0;
+
+ if (!sw) {
+ return size;
+ }
+
+ hwsamples = sw->hw->samples;
+
+ live = sw->total_hw_samples_mixed;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
+ dolog ("live=%d hw->samples=%d\n", live, hwsamples);
+ return 0;
+ }
+
+ if (live == hwsamples) {
+#ifdef DEBUG_OUT
+ dolog ("%s is full %d\n", sw->name, live);
+#endif
+ return 0;
+ }
+
+ wpos = (sw->hw->rpos + live) % hwsamples;
+ samples = size >> sw->info.shift;
+
+ dead = hwsamples - live;
+ swlim = ((int64_t) dead << 32) / sw->ratio;
+ swlim = audio_MIN (swlim, samples);
+ if (swlim) {
+ sw->conv (sw->buf, buf, swlim, &sw->vol);
+ }
+
+ while (swlim) {
+ dead = hwsamples - live;
+ left = hwsamples - wpos;
+ blck = audio_MIN (dead, left);
+ if (!blck) {
+ break;
+ }
+ isamp = swlim;
+ osamp = blck;
+ st_rate_flow_mix (
+ sw->rate,
+ sw->buf + pos,
+ sw->hw->mix_buf + wpos,
+ &isamp,
+ &osamp
+ );
+ ret += isamp;
+ swlim -= isamp;
+ pos += isamp;
+ live += osamp;
+ wpos = (wpos + osamp) % hwsamples;
+ total += osamp;
+ }
+
+ sw->total_hw_samples_mixed += total;
+ sw->empty = sw->total_hw_samples_mixed == 0;
+
+#ifdef DEBUG_OUT
+ dolog (
+ "%s: write size %d ret %d total sw %d\n",
+ SW_NAME (sw),
+ size >> sw->info.shift,
+ ret,
+ sw->total_hw_samples_mixed
+ );
+#endif
+
+ return ret << sw->info.shift;
+}
+
+#ifdef DEBUG_AUDIO
+static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
+{
+ dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
+ cap, info->bits, info->sign, info->freq, info->nchannels);
+}
+#endif
+
+#define DAC
+#include "audio_template.h"
+#undef DAC
+#include "audio_template.h"
+
+int AUD_write (SWVoiceOut *sw, void *buf, int size)
+{
+ int bytes;
+
+ if (!sw) {
+ /* XXX: Consider options */
+ return size;
+ }
+
+ if (!sw->hw->enabled) {
+ dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
+ return 0;
+ }
+
+ bytes = sw->hw->pcm_ops->write (sw, buf, size);
+ return bytes;
+}
+
+int AUD_read (SWVoiceIn *sw, void *buf, int size)
+{
+ int bytes;
+
+ if (!sw) {
+ /* XXX: Consider options */
+ return size;
+ }
+
+ if (!sw->hw->enabled) {
+ dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
+ return 0;
+ }
+
+ bytes = sw->hw->pcm_ops->read (sw, buf, size);
+ return bytes;
+}
+
+int AUD_get_buffer_size_out (SWVoiceOut *sw)
+{
+ return sw->hw->samples << sw->hw->info.shift;
+}
+
+void AUD_set_active_out (SWVoiceOut *sw, int on)
+{
+ HWVoiceOut *hw;
+
+ if (!sw) {
+ return;
+ }
+
+ hw = sw->hw;
+ if (sw->active != on) {
+ SWVoiceOut *temp_sw;
+ SWVoiceCap *sc;
+
+ if (on) {
+ hw->pending_disable = 0;
+ if (!hw->enabled) {
+ hw->enabled = 1;
+ hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
+ }
+ }
+ else {
+ if (hw->enabled) {
+ int nb_active = 0;
+
+ for (temp_sw = hw->sw_head.lh_first; temp_sw;
+ temp_sw = temp_sw->entries.le_next) {
+ nb_active += temp_sw->active != 0;
+ }
+
+ hw->pending_disable = nb_active == 1;
+ }
+ }
+
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ sc->sw.active = hw->enabled;
+ if (hw->enabled) {
+ audio_capture_maybe_changed (sc->cap, 1);
+ }
+ }
+ sw->active = on;
+ }
+}
+
+void AUD_set_active_in (SWVoiceIn *sw, int on)
+{
+ HWVoiceIn *hw;
+
+ if (!sw) {
+ return;
+ }
+
+ hw = sw->hw;
+ if (sw->active != on) {
+ SWVoiceIn *temp_sw;
+
+ if (on) {
+ if (!hw->enabled) {
+ hw->enabled = 1;
+ hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
+ }
+ sw->total_hw_samples_acquired = hw->total_samples_captured;
+ }
+ else {
+ if (hw->enabled) {
+ int nb_active = 0;
+
+ for (temp_sw = hw->sw_head.lh_first; temp_sw;
+ temp_sw = temp_sw->entries.le_next) {
+ nb_active += temp_sw->active != 0;
+ }
+
+ if (nb_active == 1) {
+ hw->enabled = 0;
+ hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+ }
+ }
+ }
+ sw->active = on;
+ }
+}
+
+static int audio_get_avail (SWVoiceIn *sw)
+{
+ int live;
+
+ if (!sw) {
+ return 0;
+ }
+
+ live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+ return 0;
+ }
+
+ ldebug (
+ "%s: get_avail live %d ret %" PRId64 "\n",
+ SW_NAME (sw),
+ live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
+ );
+
+ return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
+}
+
+static int audio_get_free (SWVoiceOut *sw)
+{
+ int live, dead;
+
+ if (!sw) {
+ return 0;
+ }
+
+ live = sw->total_hw_samples_mixed;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+ return 0;
+ }
+
+ dead = sw->hw->samples - live;
+
+#ifdef DEBUG_OUT
+ dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
+ SW_NAME (sw),
+ live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
+#endif
+
+ return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
+}
+
+static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
+{
+ int n;
+
+ if (hw->enabled) {
+ SWVoiceCap *sc;
+
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ SWVoiceOut *sw = &sc->sw;
+ int rpos2 = rpos;
+
+ n = samples;
+ while (n) {
+ int till_end_of_hw = hw->samples - rpos2;
+ int to_write = audio_MIN (till_end_of_hw, n);
+ int bytes = to_write << hw->info.shift;
+ int written;
+
+ sw->buf = hw->mix_buf + rpos2;
+ written = audio_pcm_sw_write (sw, NULL, bytes);
+ if (written - bytes) {
+ dolog ("Could not mix %d bytes into a capture "
+ "buffer, mixed %d\n",
+ bytes, written);
+ break;
+ }
+ n -= to_write;
+ rpos2 = (rpos2 + to_write) % hw->samples;
+ }
+ }
+ }
+
+ n = audio_MIN (samples, hw->samples - rpos);
+ mixeng_clear (hw->mix_buf + rpos, n);
+ mixeng_clear (hw->mix_buf, samples - n);
+}
+
+static void audio_run_out (AudioState *s)
+{
+ HWVoiceOut *hw = NULL;
+ SWVoiceOut *sw;
+
+ while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
+ int played;
+ int live, free, nb_live, cleanup_required, prev_rpos;
+
+ live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+ if (!nb_live) {
+ live = 0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ continue;
+ }
+
+ if (hw->pending_disable && !nb_live) {
+ SWVoiceCap *sc;
+#ifdef DEBUG_OUT
+ dolog ("Disabling voice\n");
+#endif
+ hw->enabled = 0;
+ hw->pending_disable = 0;
+ hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ sc->sw.active = 0;
+ audio_recalc_and_notify_capture (sc->cap);
+ }
+ continue;
+ }
+
+ if (!live) {
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ free = audio_get_free (sw);
+ if (free > 0) {
+ sw->callback.fn (sw->callback.opaque, free);
+ }
+ }
+ }
+ continue;
+ }
+
+ prev_rpos = hw->rpos;
+ played = hw->pcm_ops->run_out (hw);
+ if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
+ dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
+ hw->rpos, hw->samples, played);
+ hw->rpos = 0;
+ }
+
+#ifdef DEBUG_OUT
+ dolog ("played=%d\n", played);
+#endif
+
+ if (played) {
+ hw->ts_helper += played;
+ audio_capture_mix_and_clear (hw, prev_rpos, played);
+ }
+
+ cleanup_required = 0;
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (!sw->active && sw->empty) {
+ continue;
+ }
+
+ if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
+ dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
+ played, sw->total_hw_samples_mixed);
+ played = sw->total_hw_samples_mixed;
+ }
+
+ sw->total_hw_samples_mixed -= played;
+
+ if (!sw->total_hw_samples_mixed) {
+ sw->empty = 1;
+ cleanup_required |= !sw->active && !sw->callback.fn;
+ }
+
+ if (sw->active) {
+ free = audio_get_free (sw);
+ if (free > 0) {
+ sw->callback.fn (sw->callback.opaque, free);
+ }
+ }
+ }
+
+ if (cleanup_required) {
+ SWVoiceOut *sw1;
+
+ sw = hw->sw_head.lh_first;
+ while (sw) {
+ sw1 = sw->entries.le_next;
+ if (!sw->active && !sw->callback.fn) {
+#ifdef DEBUG_PLIVE
+ dolog ("Finishing with old voice\n");
+#endif
+ audio_close_out (s, sw);
+ }
+ sw = sw1;
+ }
+ }
+ }
+}
+
+static void audio_run_in (AudioState *s)
+{
+ HWVoiceIn *hw = NULL;
+
+ while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) {
+ SWVoiceIn *sw;
+ int captured, min;
+
+ captured = hw->pcm_ops->run_in (hw);
+
+ min = audio_pcm_hw_find_min_in (hw);
+ hw->total_samples_captured += captured - min;
+ hw->ts_helper += captured;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ sw->total_hw_samples_acquired -= min;
+
+ if (sw->active) {
+ int avail;
+
+ avail = audio_get_avail (sw);
+ if (avail > 0) {
+ sw->callback.fn (sw->callback.opaque, avail);
+ }
+ }
+ }
+ }
+}
+
+static void audio_run_capture (AudioState *s)
+{
+ CaptureVoiceOut *cap;
+
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ int live, rpos, captured;
+ HWVoiceOut *hw = &cap->hw;
+ SWVoiceOut *sw;
+
+ captured = live = audio_pcm_hw_get_live_out (hw);
+ rpos = hw->rpos;
+ while (live) {
+ int left = hw->samples - rpos;
+ int to_capture = audio_MIN (live, left);
+ st_sample_t *src;
+ struct capture_callback *cb;
+
+ src = hw->mix_buf + rpos;
+ hw->clip (cap->buf, src, to_capture);
+ mixeng_clear (src, to_capture);
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.capture (cb->opaque, cap->buf,
+ to_capture << hw->info.shift);
+ }
+ rpos = (rpos + to_capture) % hw->samples;
+ live -= to_capture;
+ }
+ hw->rpos = rpos;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (!sw->active && sw->empty) {
+ continue;
+ }
+
+ if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
+ dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
+ captured, sw->total_hw_samples_mixed);
+ captured = sw->total_hw_samples_mixed;
+ }
+
+ sw->total_hw_samples_mixed -= captured;
+ sw->empty = sw->total_hw_samples_mixed == 0;
+ }
+ }
+}
+
+static void audio_timer (void *opaque)
+{
+ AudioState *s = opaque;
+
+ audio_run_out (s);
+ audio_run_in (s);
+ audio_run_capture (s);
+
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+}
+
+static struct audio_option audio_options[] = {
+ /* DAC */
+ {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
+ "Use fixed settings for host DAC", NULL, 0},
+
+ {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
+ "Frequency for fixed host DAC", NULL, 0},
+
+ {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
+ "Format for fixed host DAC", NULL, 0},
+
+ {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
+ "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
+
+ {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
+ "Number of voices for DAC", NULL, 0},
+
+ /* ADC */
+ {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
+ "Use fixed settings for host ADC", NULL, 0},
+
+ {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
+ "Frequency for fixed host ADC", NULL, 0},
+
+ {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
+ "Format for fixed host ADC", NULL, 0},
+
+ {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
+ "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
+
+ {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
+ "Number of voices for ADC", NULL, 0},
+
+ /* Misc */
+ {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hz,
+ "Timer period in HZ (0 - use lowest possible)", NULL, 0},
+
+ {"PLIVE", AUD_OPT_BOOL, &conf.plive,
+ "(undocumented)", NULL, 0},
+
+ {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
+ "print logging messages to montior instead of stderr", NULL, 0},
+
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static void audio_pp_nb_voices (const char *typ, int nb)
+{
+ switch (nb) {
+ case 0:
+ printf ("Does not support %s\n", typ);
+ break;
+ case 1:
+ printf ("One %s voice\n", typ);
+ break;
+ case INT_MAX:
+ printf ("Theoretically supports many %s voices\n", typ);
+ break;
+ default:
+ printf ("Theoretically supports upto %d %s voices\n", nb, typ);
+ break;
+ }
+
+}
+
+void AUD_help (void)
+{
+ size_t i;
+
+ audio_process_options ("AUDIO", audio_options);
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ struct audio_driver *d = drvtab[i];
+ if (d->options) {
+ audio_process_options (d->name, d->options);
+ }
+ }
+
+ printf ("Audio options:\n");
+ audio_print_options ("AUDIO", audio_options);
+ printf ("\n");
+
+ printf ("Available drivers:\n");
+
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ struct audio_driver *d = drvtab[i];
+
+ printf ("Name: %s\n", d->name);
+ printf ("Description: %s\n", d->descr);
+
+ audio_pp_nb_voices ("playback", d->max_voices_out);
+ audio_pp_nb_voices ("capture", d->max_voices_in);
+
+ if (d->options) {
+ printf ("Options:\n");
+ audio_print_options (d->name, d->options);
+ }
+ else {
+ printf ("No options\n");
+ }
+ printf ("\n");
+ }
+
+ printf (
+ "Options are settable through environment variables.\n"
+ "Example:\n"
+#ifdef _WIN32
+ " set QEMU_AUDIO_DRV=wav\n"
+ " set QEMU_WAV_PATH=c:\\tune.wav\n"
+#else
+ " export QEMU_AUDIO_DRV=wav\n"
+ " export QEMU_WAV_PATH=$HOME/tune.wav\n"
+ "(for csh replace export with setenv in the above)\n"
+#endif
+ " qemu ...\n\n"
+ );
+}
+
+static int audio_driver_init (AudioState *s, struct audio_driver *drv)
+{
+ if (drv->options) {
+ audio_process_options (drv->name, drv->options);
+ }
+ s->drv_opaque = drv->init ();
+
+ if (s->drv_opaque) {
+ audio_init_nb_voices_out (s, drv);
+ audio_init_nb_voices_in (s, drv);
+ s->drv = drv;
+ return 0;
+ }
+ else {
+ dolog ("Could not init `%s' audio driver\n", drv->name);
+ return -1;
+ }
+}
+
+static void audio_vm_change_state_handler (void *opaque, int running)
+{
+ AudioState *s = opaque;
+ HWVoiceOut *hwo = NULL;
+ HWVoiceIn *hwi = NULL;
+ int op = running ? VOICE_ENABLE : VOICE_DISABLE;
+
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+ hwo->pcm_ops->ctl_out (hwo, op);
+ }
+
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, op);
+ }
+}
+
+static void audio_atexit (void)
+{
+ AudioState *s = &glob_audio_state;
+ HWVoiceOut *hwo = NULL;
+ HWVoiceIn *hwi = NULL;
+
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+ SWVoiceCap *sc;
+
+ hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
+ hwo->pcm_ops->fini_out (hwo);
+
+ for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ CaptureVoiceOut *cap = sc->cap;
+ struct capture_callback *cb;
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.destroy (cb->opaque);
+ }
+ }
+ }
+
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
+ hwi->pcm_ops->fini_in (hwi);
+ }
+
+ if (s->drv) {
+ s->drv->fini (s->drv_opaque);
+ }
+}
+
+static void audio_save (QEMUFile *f, void *opaque)
+{
+ (void) f;
+ (void) opaque;
+}
+
+static int audio_load (QEMUFile *f, void *opaque, int version_id)
+{
+ (void) f;
+ (void) opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card)
+{
+ card->audio = s;
+ card->name = qemu_strdup (name);
+ memset (&card->entries, 0, sizeof (card->entries));
+ LIST_INSERT_HEAD (&s->card_head, card, entries);
+}
+
+void AUD_remove_card (QEMUSoundCard *card)
+{
+ LIST_REMOVE (card, entries);
+ card->audio = NULL;
+ qemu_free (card->name);
+}
+
+AudioState *AUD_init (void)
+{
+ size_t i;
+ int done = 0;
+ const char *drvname;
+ AudioState *s = &glob_audio_state;
+
+ LIST_INIT (&s->hw_head_out);
+ LIST_INIT (&s->hw_head_in);
+ LIST_INIT (&s->cap_head);
+ atexit (audio_atexit);
+
+ s->ts = qemu_new_timer (vm_clock, audio_timer, s);
+ if (!s->ts) {
+ dolog ("Could not create audio timer\n");
+ return NULL;
+ }
+
+ audio_process_options ("AUDIO", audio_options);
+
+ s->nb_hw_voices_out = conf.fixed_out.nb_voices;
+ s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+
+ if (s->nb_hw_voices_out <= 0) {
+ dolog ("Bogus number of playback voices %d, setting to 1\n",
+ s->nb_hw_voices_out);
+ s->nb_hw_voices_out = 1;
+ }
+
+ if (s->nb_hw_voices_in <= 0) {
+ dolog ("Bogus number of capture voices %d, setting to 0\n",
+ s->nb_hw_voices_in);
+ s->nb_hw_voices_in = 0;
+ }
+
+ {
+ int def;
+ drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
+ }
+
+ if (drvname) {
+ int found = 0;
+
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ if (!strcmp (drvname, drvtab[i]->name)) {
+ done = !audio_driver_init (s, drvtab[i]);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dolog ("Unknown audio driver `%s'\n", drvname);
+ dolog ("Run with -audio-help to list available drivers\n");
+ }
+ }
+
+ if (!done) {
+ for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ if (drvtab[i]->can_be_default) {
+ done = !audio_driver_init (s, drvtab[i]);
+ }
+ }
+ }
+
+ if (!done) {
+ done = !audio_driver_init (s, &no_audio_driver);
+ if (!done) {
+ dolog ("Could not initialize audio subsystem\n");
+ }
+ else {
+ dolog ("warning: Using timer based audio emulation\n");
+ }
+ }
+
+ if (done) {
+ VMChangeStateEntry *e;
+
+ if (conf.period.hz <= 0) {
+ if (conf.period.hz < 0) {
+ dolog ("warning: Timer period is negative - %d "
+ "treating as zero\n",
+ conf.period.hz);
+ }
+ conf.period.ticks = 1;
+ }
+ else {
+ conf.period.ticks = ticks_per_sec / conf.period.hz;
+ }
+
+ e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
+ if (!e) {
+ dolog ("warning: Could not register change state handler\n"
+ "(Audio can continue looping even after stopping the VM)\n");
+ }
+ }
+ else {
+ qemu_del_timer (s->ts);
+ return NULL;
+ }
+
+ LIST_INIT (&s->card_head);
+ register_savevm ("audio", 0, 1, audio_save, audio_load, s);
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+ return s;
+}
+
+CaptureVoiceOut *AUD_add_capture (
+ AudioState *s,
+ audsettings_t *as,
+ struct audio_capture_ops *ops,
+ void *cb_opaque
+ )
+{
+ CaptureVoiceOut *cap;
+ struct capture_callback *cb;
+
+ if (!s) {
+ /* XXX suppress */
+ s = &glob_audio_state;
+ }
+
+ if (audio_validate_settings (as)) {
+ dolog ("Invalid settings were passed when trying to add capture\n");
+ audio_print_settings (as);
+ goto err0;
+ }
+
+ cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
+ if (!cb) {
+ dolog ("Could not allocate capture callback information, size %zu\n",
+ sizeof (*cb));
+ goto err0;
+ }
+ cb->ops = *ops;
+ cb->opaque = cb_opaque;
+
+ cap = audio_pcm_capture_find_specific (s, as);
+ if (cap) {
+ LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+ return cap;
+ }
+ else {
+ HWVoiceOut *hw;
+ CaptureVoiceOut *cap;
+
+ cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
+ if (!cap) {
+ dolog ("Could not allocate capture voice, size %zu\n",
+ sizeof (*cap));
+ goto err1;
+ }
+
+ hw = &cap->hw;
+ LIST_INIT (&hw->sw_head);
+ LIST_INIT (&cap->cb_head);
+
+ /* XXX find a more elegant way */
+ hw->samples = 4096 * 4;
+ hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
+ sizeof (st_sample_t));
+ if (!hw->mix_buf) {
+ dolog ("Could not allocate capture mix buffer (%d samples)\n",
+ hw->samples);
+ goto err2;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+
+ cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!cap->buf) {
+ dolog ("Could not allocate capture buffer "
+ "(%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ goto err3;
+ }
+
+ hw->clip = mixeng_clip
+ [hw->info.nchannels == 2]
+ [hw->info.sign]
+ [hw->info.swap_endianness]
+ [hw->info.bits == 16];
+
+ LIST_INSERT_HEAD (&s->cap_head, cap, entries);
+ LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+
+ hw = NULL;
+ while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
+ audio_attach_capture (s, hw);
+ }
+ return cap;
+
+ err3:
+ qemu_free (cap->hw.mix_buf);
+ err2:
+ qemu_free (cap);
+ err1:
+ qemu_free (cb);
+ err0:
+ return NULL;
+ }
+}
+
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
+{
+ struct capture_callback *cb;
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ if (cb->opaque == cb_opaque) {
+ cb->ops.destroy (cb_opaque);
+ LIST_REMOVE (cb, entries);
+ qemu_free (cb);
+
+ if (!cap->cb_head.lh_first) {
+ SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
+
+ while (sw) {
+ SWVoiceCap *sc = (SWVoiceCap *) sw;
+#ifdef DEBUG_CAPTURE
+ dolog ("freeing %s\n", sw->name);
+#endif
+
+ sw1 = sw->entries.le_next;
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ sw->rate = NULL;
+ }
+ LIST_REMOVE (sw, entries);
+ LIST_REMOVE (sc, entries);
+ qemu_free (sc);
+ sw = sw1;
+ }
+ LIST_REMOVE (cap, entries);
+ qemu_free (cap);
+ }
+ return;
+ }
+ }
+}
diff --git a/audio/audio.h b/audio/audio.h
new file mode 100644
index 0000000..c097f39
--- /dev/null
+++ b/audio/audio.h
@@ -0,0 +1,169 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_H
+#define QEMU_AUDIO_H
+
+#include "config.h"
+#include "sys-queue.h"
+
+typedef void (*audio_callback_fn_t) (void *opaque, int avail);
+
+typedef enum {
+ AUD_FMT_U8,
+ AUD_FMT_S8,
+ AUD_FMT_U16,
+ AUD_FMT_S16
+} audfmt_e;
+
+#ifdef WORDS_BIGENDIAN
+#define AUDIO_HOST_ENDIANNESS 1
+#else
+#define AUDIO_HOST_ENDIANNESS 0
+#endif
+
+typedef struct {
+ int freq;
+ int nchannels;
+ audfmt_e fmt;
+ int endianness;
+} audsettings_t;
+
+typedef enum {
+ AUD_CNOTIFY_ENABLE,
+ AUD_CNOTIFY_DISABLE
+} audcnotification_e;
+
+struct audio_capture_ops {
+ void (*notify) (void *opaque, audcnotification_e cmd);
+ void (*capture) (void *opaque, void *buf, int size);
+ void (*destroy) (void *opaque);
+};
+
+struct capture_ops {
+ void (*info) (void *opaque);
+ void (*destroy) (void *opaque);
+};
+
+typedef struct CaptureState {
+ void *opaque;
+ struct capture_ops ops;
+ LIST_ENTRY (CaptureState) entries;
+} CaptureState;
+
+typedef struct AudioState AudioState;
+typedef struct SWVoiceOut SWVoiceOut;
+typedef struct CaptureVoiceOut CaptureVoiceOut;
+typedef struct SWVoiceIn SWVoiceIn;
+
+typedef struct QEMUSoundCard {
+ AudioState *audio;
+ char *name;
+ LIST_ENTRY (QEMUSoundCard) entries;
+} QEMUSoundCard;
+
+typedef struct QEMUAudioTimeStamp {
+ uint64_t old_ts;
+} QEMUAudioTimeStamp;
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap);
+void AUD_log (const char *cap, const char *fmt, ...)
+#ifdef __GNUC__
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+#endif
+ ;
+
+AudioState *AUD_init (void);
+void AUD_help (void);
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
+void AUD_remove_card (QEMUSoundCard *card);
+CaptureVoiceOut *AUD_add_capture (
+ AudioState *s,
+ audsettings_t *as,
+ struct audio_capture_ops *ops,
+ void *opaque
+ );
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
+
+SWVoiceOut *AUD_open_out (
+ QEMUSoundCard *card,
+ SWVoiceOut *sw,
+ const char *name,
+ void *callback_opaque,
+ audio_callback_fn_t callback_fn,
+ audsettings_t *settings
+ );
+
+void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
+int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
+int AUD_get_buffer_size_out (SWVoiceOut *sw);
+void AUD_set_active_out (SWVoiceOut *sw, int on);
+int AUD_is_active_out (SWVoiceOut *sw);
+
+void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+
+SWVoiceIn *AUD_open_in (
+ QEMUSoundCard *card,
+ SWVoiceIn *sw,
+ const char *name,
+ void *callback_opaque,
+ audio_callback_fn_t callback_fn,
+ audsettings_t *settings
+ );
+
+void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
+int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
+void AUD_set_active_in (SWVoiceIn *sw, int on);
+int AUD_is_active_in (SWVoiceIn *sw);
+
+void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+
+static inline void *advance (void *p, int incr)
+{
+ uint8_t *d = p;
+ return (d + incr);
+}
+
+uint32_t popcount (uint32_t u);
+uint32_t lsbindex (uint32_t u);
+
+#ifdef __GNUC__
+#define audio_MIN(a, b) ( __extension__ ({ \
+ __typeof (a) ta = a; \
+ __typeof (b) tb = b; \
+ ((ta)>(tb)?(tb):(ta)); \
+}))
+
+#define audio_MAX(a, b) ( __extension__ ({ \
+ __typeof (a) ta = a; \
+ __typeof (b) tb = b; \
+ ((ta)<(tb)?(tb):(ta)); \
+}))
+#else
+#define audio_MIN(a, b) ((a)>(b)?(b):(a))
+#define audio_MAX(a, b) ((a)<(b)?(b):(a))
+#endif
+
+#endif /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
new file mode 100644
index 0000000..1a15d4c
--- /dev/null
+++ b/audio/audio_int.h
@@ -0,0 +1,280 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_INT_H
+#define QEMU_AUDIO_INT_H
+
+#ifdef CONFIG_COREAUDIO
+#define FLOAT_MIXENG
+/* #define RECIPROCAL */
+#endif
+#include "mixeng.h"
+
+struct audio_pcm_ops;
+
+typedef enum {
+ AUD_OPT_INT,
+ AUD_OPT_FMT,
+ AUD_OPT_STR,
+ AUD_OPT_BOOL
+} audio_option_tag_e;
+
+struct audio_option {
+ const char *name;
+ audio_option_tag_e tag;
+ void *valp;
+ const char *descr;
+ int *overridenp;
+ int overriden;
+};
+
+struct audio_callback {
+ void *opaque;
+ audio_callback_fn_t fn;
+};
+
+struct audio_pcm_info {
+ int bits;
+ int sign;
+ int freq;
+ int nchannels;
+ int align;
+ int shift;
+ int bytes_per_second;
+ int swap_endianness;
+};
+
+typedef struct SWVoiceCap SWVoiceCap;
+
+typedef struct HWVoiceOut {
+ int enabled;
+ int pending_disable;
+ struct audio_pcm_info info;
+
+ f_sample *clip;
+
+ int rpos;
+ uint64_t ts_helper;
+
+ st_sample_t *mix_buf;
+
+ int samples;
+ LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
+ LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
+ struct audio_pcm_ops *pcm_ops;
+ LIST_ENTRY (HWVoiceOut) entries;
+} HWVoiceOut;
+
+typedef struct HWVoiceIn {
+ int enabled;
+ struct audio_pcm_info info;
+
+ t_sample *conv;
+
+ int wpos;
+ int total_samples_captured;
+ uint64_t ts_helper;
+
+ st_sample_t *conv_buf;
+
+ int samples;
+ LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
+ struct audio_pcm_ops *pcm_ops;
+ LIST_ENTRY (HWVoiceIn) entries;
+} HWVoiceIn;
+
+struct SWVoiceOut {
+ struct audio_pcm_info info;
+ t_sample *conv;
+ int64_t ratio;
+ st_sample_t *buf;
+ void *rate;
+ int total_hw_samples_mixed;
+ int active;
+ int empty;
+ HWVoiceOut *hw;
+ char *name;
+ volume_t vol;
+ struct audio_callback callback;
+ LIST_ENTRY (SWVoiceOut) entries;
+};
+
+struct SWVoiceIn {
+ int active;
+ struct audio_pcm_info info;
+ int64_t ratio;
+ void *rate;
+ int total_hw_samples_acquired;
+ st_sample_t *buf;
+ f_sample *clip;
+ HWVoiceIn *hw;
+ char *name;
+ volume_t vol;
+ struct audio_callback callback;
+ LIST_ENTRY (SWVoiceIn) entries;
+};
+
+struct audio_driver {
+ const char *name;
+ const char *descr;
+ struct audio_option *options;
+ void *(*init) (void);
+ void (*fini) (void *);
+ struct audio_pcm_ops *pcm_ops;
+ int can_be_default;
+ int max_voices_out;
+ int max_voices_in;
+ int voice_size_out;
+ int voice_size_in;
+};
+
+struct audio_pcm_ops {
+ int (*init_out)(HWVoiceOut *hw, audsettings_t *as);
+ void (*fini_out)(HWVoiceOut *hw);
+ int (*run_out) (HWVoiceOut *hw);
+ int (*write) (SWVoiceOut *sw, void *buf, int size);
+ int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+
+ int (*init_in) (HWVoiceIn *hw, audsettings_t *as);
+ void (*fini_in) (HWVoiceIn *hw);
+ int (*run_in) (HWVoiceIn *hw);
+ int (*read) (SWVoiceIn *sw, void *buf, int size);
+ int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
+};
+
+struct capture_callback {
+ struct audio_capture_ops ops;
+ void *opaque;
+ LIST_ENTRY (capture_callback) entries;
+};
+
+struct CaptureVoiceOut {
+ HWVoiceOut hw;
+ void *buf;
+ LIST_HEAD (cb_listhead, capture_callback) cb_head;
+ LIST_ENTRY (CaptureVoiceOut) entries;
+};
+
+struct SWVoiceCap {
+ SWVoiceOut sw;
+ CaptureVoiceOut *cap;
+ LIST_ENTRY (SWVoiceCap) entries;
+};
+
+struct AudioState {
+ struct audio_driver *drv;
+ void *drv_opaque;
+
+ QEMUTimer *ts;
+ LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
+ LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
+ LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
+ LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
+ int nb_hw_voices_out;
+ int nb_hw_voices_in;
+};
+
+extern struct audio_driver no_audio_driver;
+extern struct audio_driver oss_audio_driver;
+extern struct audio_driver sdl_audio_driver;
+extern struct audio_driver wav_audio_driver;
+extern struct audio_driver fmod_audio_driver;
+extern struct audio_driver alsa_audio_driver;
+extern struct audio_driver coreaudio_audio_driver;
+extern struct audio_driver dsound_audio_driver;
+extern volume_t nominal_volume;
+
+void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
+
+int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
+int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
+
+int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
+int audio_pcm_hw_get_live_out (HWVoiceOut *hw);
+int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
+
+int audio_bug (const char *funcname, int cond);
+void *audio_calloc (const char *funcname, int nmemb, size_t size);
+
+#define VOICE_ENABLE 1
+#define VOICE_DISABLE 2
+
+static inline int audio_ring_dist (int dst, int src, int len)
+{
+ return (dst >= src) ? (dst - src) : (len - src + dst);
+}
+
+#if defined __GNUC__
+#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
+#define INIT_FIELD(f) . f
+#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
+#else
+#define GCC_ATTR /**/
+#define INIT_FIELD(f) /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
+static void GCC_ATTR dolog (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+}
+
+#ifdef DEBUG
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+}
+#else
+#if defined NDEBUG && defined __GNUC__
+#define ldebug(...)
+#elif defined NDEBUG && defined _MSC_VER
+#define ldebug __noop
+#else
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+ (void) fmt;
+}
+#endif
+#endif
+
+#undef GCC_ATTR
+
+#define AUDIO_STRINGIFY_(n) #n
+#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
+
+#if defined _MSC_VER || defined __GNUC__
+#define AUDIO_FUNC __FUNCTION__
+#else
+#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
+#endif
+
+#endif /* audio_int.h */
diff --git a/audio/audio_template.h b/audio/audio_template.h
new file mode 100644
index 0000000..13e1c3e
--- /dev/null
+++ b/audio/audio_template.h
@@ -0,0 +1,570 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef DAC
+#define NAME "playback"
+#define HWBUF hw->mix_buf
+#define TYPE out
+#define HW HWVoiceOut
+#define SW SWVoiceOut
+#else
+#define NAME "capture"
+#define TYPE in
+#define HW HWVoiceIn
+#define SW SWVoiceIn
+#define HWBUF hw->conv_buf
+#endif
+
+static void glue (audio_init_nb_voices_, TYPE) (
+ AudioState *s,
+ struct audio_driver *drv
+ )
+{
+ int max_voices = glue (drv->max_voices_, TYPE);
+ int voice_size = glue (drv->voice_size_, TYPE);
+
+ if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
+ if (!max_voices) {
+#ifdef DAC
+ dolog ("Driver `%s' does not support " NAME "\n", drv->name);
+#endif
+ }
+ else {
+ dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
+ drv->name,
+ glue (s->nb_hw_voices_, TYPE),
+ max_voices);
+ }
+ glue (s->nb_hw_voices_, TYPE) = max_voices;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
+ dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
+ drv->name, max_voices);
+ glue (s->nb_hw_voices_, TYPE) = 0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
+ dolog ("drv=`%s' voice_size=%d max_voices=0\n",
+ drv->name, voice_size);
+ }
+}
+
+static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
+{
+ if (HWBUF) {
+ qemu_free (HWBUF);
+ }
+
+ HWBUF = NULL;
+}
+
+static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
+{
+ HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
+ if (!HWBUF) {
+ dolog ("Could not allocate " NAME " buffer (%d samples)\n",
+ hw->samples);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
+{
+ if (sw->buf) {
+ qemu_free (sw->buf);
+ }
+
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ }
+
+ sw->buf = NULL;
+ sw->rate = NULL;
+}
+
+static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
+{
+ int samples;
+
+#ifdef DAC
+ samples = sw->hw->samples;
+#else
+ samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
+#endif
+
+ sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
+ if (!sw->buf) {
+ dolog ("Could not allocate buffer for `%s' (%d samples)\n",
+ SW_NAME (sw), samples);
+ return -1;
+ }
+
+#ifdef DAC
+ sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
+#else
+ sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
+#endif
+ if (!sw->rate) {
+ qemu_free (sw->buf);
+ sw->buf = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+static int glue (audio_pcm_sw_init_, TYPE) (
+ SW *sw,
+ HW *hw,
+ const char *name,
+ audsettings_t *as
+ )
+{
+ int err;
+
+ audio_pcm_init_info (&sw->info, as);
+ sw->hw = hw;
+ sw->active = 0;
+#ifdef DAC
+ sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
+ sw->total_hw_samples_mixed = 0;
+ sw->empty = 1;
+#else
+ sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
+#endif
+
+#ifdef DAC
+ sw->conv = mixeng_conv
+#else
+ sw->clip = mixeng_clip
+#endif
+ [sw->info.nchannels == 2]
+ [sw->info.sign]
+ [sw->info.swap_endianness]
+ [sw->info.bits == 16];
+
+ sw->name = qemu_strdup (name);
+ err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
+ if (err) {
+ qemu_free (sw->name);
+ sw->name = NULL;
+ }
+ return err;
+}
+
+static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
+{
+ glue (audio_pcm_sw_free_resources_, TYPE) (sw);
+ if (sw->name) {
+ qemu_free (sw->name);
+ sw->name = NULL;
+ }
+}
+
+static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
+{
+ LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
+}
+
+static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
+{
+ LIST_REMOVE (sw, entries);
+}
+
+static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
+{
+ HW *hw = *hwp;
+
+ if (!hw->sw_head.lh_first) {
+#ifdef DAC
+ audio_detach_capture (hw);
+#endif
+ LIST_REMOVE (hw, entries);
+ glue (s->nb_hw_voices_, TYPE) += 1;
+ glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
+ glue (hw->pcm_ops->fini_, TYPE) (hw);
+ qemu_free (hw);
+ *hwp = NULL;
+ }
+}
+
+static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
+{
+ return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
+}
+
+static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
+{
+ while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+ if (hw->enabled) {
+ return hw;
+ }
+ }
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
+ AudioState *s,
+ HW *hw,
+ audsettings_t *as
+ )
+{
+ while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+ if (audio_pcm_info_eq (&hw->info, as)) {
+ return hw;
+ }
+ }
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
+{
+ HW *hw;
+ struct audio_driver *drv = s->drv;
+
+ if (!glue (s->nb_hw_voices_, TYPE)) {
+ return NULL;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !drv)) {
+ dolog ("No host audio driver\n");
+ return NULL;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
+ dolog ("Host audio driver without pcm_ops\n");
+ return NULL;
+ }
+
+ hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
+ if (!hw) {
+ dolog ("Can not allocate voice `%s' size %d\n",
+ drv->name, glue (drv->voice_size_, TYPE));
+ return NULL;
+ }
+
+ hw->pcm_ops = drv->pcm_ops;
+ LIST_INIT (&hw->sw_head);
+#ifdef DAC
+ LIST_INIT (&hw->cap_head);
+#endif
+ if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+ goto err0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
+ dolog ("hw->samples=%d\n", hw->samples);
+ goto err1;
+ }
+
+#ifdef DAC
+ hw->clip = mixeng_clip
+#else
+ hw->conv = mixeng_conv
+#endif
+ [hw->info.nchannels == 2]
+ [hw->info.sign]
+ [hw->info.swap_endianness]
+ [hw->info.bits == 16];
+
+ if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
+ goto err1;
+ }
+
+ LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
+ glue (s->nb_hw_voices_, TYPE) -= 1;
+#ifdef DAC
+ audio_attach_capture (s, hw);
+#endif
+ return hw;
+
+ err1:
+ glue (hw->pcm_ops->fini_, TYPE) (hw);
+ err0:
+ qemu_free (hw);
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
+{
+ HW *hw;
+
+ if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+ hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
+ if (hw) {
+ return hw;
+ }
+ }
+
+ hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
+ if (hw) {
+ return hw;
+ }
+
+ hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
+ if (hw) {
+ return hw;
+ }
+
+ return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
+}
+
+static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
+ AudioState *s,
+ const char *sw_name,
+ audsettings_t *as
+ )
+{
+ SW *sw;
+ HW *hw;
+ audsettings_t hw_as;
+
+ if (glue (conf.fixed_, TYPE).enabled) {
+ hw_as = glue (conf.fixed_, TYPE).settings;
+ }
+ else {
+ hw_as = *as;
+ }
+
+ sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
+ if (!sw) {
+ dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
+ sw_name ? sw_name : "unknown", sizeof (*sw));
+ goto err1;
+ }
+
+ hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
+ if (!hw) {
+ goto err2;
+ }
+
+ glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
+
+ if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
+ goto err3;
+ }
+
+ return sw;
+
+err3:
+ glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+ glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
+err2:
+ qemu_free (sw);
+err1:
+ return NULL;
+}
+
+static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
+{
+ glue (audio_pcm_sw_fini_, TYPE) (sw);
+ glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+ glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
+ qemu_free (sw);
+}
+
+void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
+{
+ if (sw) {
+ if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
+ dolog ("card=%p card->audio=%p\n",
+ card, card ? card->audio : NULL);
+ return;
+ }
+
+ glue (audio_close_, TYPE) (card->audio, sw);
+ }
+}
+
+SW *glue (AUD_open_, TYPE) (
+ QEMUSoundCard *card,
+ SW *sw,
+ const char *name,
+ void *callback_opaque ,
+ audio_callback_fn_t callback_fn,
+ audsettings_t *as
+ )
+{
+ AudioState *s;
+#ifdef DAC
+ int live = 0;
+ SW *old_sw = NULL;
+#endif
+
+ ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
+ name, as->freq, as->nchannels, as->fmt);
+
+ if (audio_bug (AUDIO_FUNC,
+ !card || !card->audio || !name || !callback_fn || !as)) {
+ dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
+ card, card ? card->audio : NULL, name, callback_fn, as);
+ goto fail;
+ }
+
+ s = card->audio;
+
+ if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
+ audio_print_settings (as);
+ goto fail;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !s->drv)) {
+ dolog ("Can not open `%s' (no host audio driver)\n", name);
+ goto fail;
+ }
+
+ if (sw && audio_pcm_info_eq (&sw->info, as)) {
+ return sw;
+ }
+
+#ifdef DAC
+ if (conf.plive && sw && (!sw->active && !sw->empty)) {
+ live = sw->total_hw_samples_mixed;
+
+#ifdef DEBUG_PLIVE
+ dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
+ dolog ("Old %s freq %d, bits %d, channels %d\n",
+ SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
+ dolog ("New %s freq %d, bits %d, channels %d\n",
+ name,
+ freq,
+ (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
+ nchannels);
+#endif
+
+ if (live) {
+ old_sw = sw;
+ old_sw->callback.fn = NULL;
+ sw = NULL;
+ }
+ }
+#endif
+
+ if (!glue (conf.fixed_, TYPE).enabled && sw) {
+ glue (AUD_close_, TYPE) (card, sw);
+ sw = NULL;
+ }
+
+ if (sw) {
+ HW *hw = sw->hw;
+
+ if (!hw) {
+ dolog ("Internal logic error voice `%s' has no hardware store\n",
+ SW_NAME (sw));
+ goto fail;
+ }
+
+ glue (audio_pcm_sw_fini_, TYPE) (sw);
+ if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
+ goto fail;
+ }
+ }
+ else {
+ sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
+ if (!sw) {
+ dolog ("Failed to create voice `%s'\n", name);
+ return NULL;
+ }
+ }
+
+ if (sw) {
+ sw->vol = nominal_volume;
+ sw->callback.fn = callback_fn;
+ sw->callback.opaque = callback_opaque;
+
+#ifdef DAC
+ if (live) {
+ int mixed =
+ (live << old_sw->info.shift)
+ * old_sw->info.bytes_per_second
+ / sw->info.bytes_per_second;
+
+#ifdef DEBUG_PLIVE
+ dolog ("Silence will be mixed %d\n", mixed);
+#endif
+ sw->total_hw_samples_mixed += mixed;
+ }
+#endif
+
+#ifdef DEBUG_AUDIO
+ dolog ("%s\n", name);
+ audio_pcm_print_info ("hw", &sw->hw->info);
+ audio_pcm_print_info ("sw", &sw->info);
+#endif
+ }
+
+ return sw;
+
+ fail:
+ glue (AUD_close_, TYPE) (card, sw);
+ return NULL;
+}
+
+int glue (AUD_is_active_, TYPE) (SW *sw)
+{
+ return sw ? sw->active : 0;
+}
+
+void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+ if (!sw) {
+ return;
+ }
+
+ ts->old_ts = sw->hw->ts_helper;
+}
+
+uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+ uint64_t delta, cur_ts, old_ts;
+
+ if (!sw) {
+ return 0;
+ }
+
+ cur_ts = sw->hw->ts_helper;
+ old_ts = ts->old_ts;
+ /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
+
+ if (cur_ts >= old_ts) {
+ delta = cur_ts - old_ts;
+ }
+ else {
+ delta = UINT64_MAX - old_ts + cur_ts;
+ }
+
+ if (!delta) {
+ return 0;
+ }
+
+ return (delta * sw->hw->info.freq) / 1000000;
+}
+
+#undef TYPE
+#undef HW
+#undef SW
+#undef HWBUF
+#undef NAME
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
new file mode 100644
index 0000000..8512f12
--- /dev/null
+++ b/audio/coreaudio.c
@@ -0,0 +1,554 @@
+/*
+ * QEMU OS X CoreAudio audio driver
+ *
+ * Copyright (c) 2005 Mike Kronenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <string.h> /* strerror */
+#include <pthread.h> /* pthread_X */
+
+#include "vl.h"
+
+#define AUDIO_CAP "coreaudio"
+#include "audio_int.h"
+
+struct {
+ int buffer_frames;
+ int nbuffers;
+ int isAtexit;
+} conf = {
+ .buffer_frames = 512,
+ .nbuffers = 4,
+ .isAtexit = 0
+};
+
+typedef struct coreaudioVoiceOut {
+ HWVoiceOut hw;
+ pthread_mutex_t mutex;
+ int isAtexit;
+ AudioDeviceID outputDeviceID;
+ UInt32 audioDevicePropertyBufferFrameSize;
+ AudioStreamBasicDescription outputStreamBasicDescription;
+ int live;
+ int decr;
+ int rpos;
+} coreaudioVoiceOut;
+
+static void coreaudio_logstatus (OSStatus status)
+{
+ char *str = "BUG";
+
+ switch(status) {
+ case kAudioHardwareNoError:
+ str = "kAudioHardwareNoError";
+ break;
+
+ case kAudioHardwareNotRunningError:
+ str = "kAudioHardwareNotRunningError";
+ break;
+
+ case kAudioHardwareUnspecifiedError:
+ str = "kAudioHardwareUnspecifiedError";
+ break;
+
+ case kAudioHardwareUnknownPropertyError:
+ str = "kAudioHardwareUnknownPropertyError";
+ break;
+
+ case kAudioHardwareBadPropertySizeError:
+ str = "kAudioHardwareBadPropertySizeError";
+ break;
+
+ case kAudioHardwareIllegalOperationError:
+ str = "kAudioHardwareIllegalOperationError";
+ break;
+
+ case kAudioHardwareBadDeviceError:
+ str = "kAudioHardwareBadDeviceError";
+ break;
+
+ case kAudioHardwareBadStreamError:
+ str = "kAudioHardwareBadStreamError";
+ break;
+
+ case kAudioHardwareUnsupportedOperationError:
+ str = "kAudioHardwareUnsupportedOperationError";
+ break;
+
+ case kAudioDeviceUnsupportedFormatError:
+ str = "kAudioDeviceUnsupportedFormatError";
+ break;
+
+ case kAudioDevicePermissionsError:
+ str = "kAudioDevicePermissionsError";
+ break;
+
+ default:
+ AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
+ return;
+ }
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
+ OSStatus status,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_log (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
+ OSStatus status,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+{
+ OSStatus status;
+ UInt32 result = 0;
+ UInt32 propertySize = sizeof(outputDeviceID);
+ status = AudioDeviceGetProperty(
+ outputDeviceID, 0, 0,
+ kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+ return result;
+}
+
+static void coreaudio_atexit (void)
+{
+ conf.isAtexit = 1;
+}
+
+static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_lock (&core->mutex);
+ if (err) {
+ dolog ("Could not lock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&core->mutex);
+ if (err) {
+ dolog ("Could not unlock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int coreaudio_run_out (HWVoiceOut *hw)
+{
+ int live, decr;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ if (coreaudio_lock (core, "coreaudio_run_out")) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+
+ if (core->decr > live) {
+ ldebug ("core->decr %d live %d core->live %d\n",
+ core->decr,
+ live,
+ core->live);
+ }
+
+ decr = audio_MIN (core->decr, live);
+ core->decr -= decr;
+
+ core->live = live - decr;
+ hw->rpos = core->rpos;
+
+ coreaudio_unlock (core, "coreaudio_run_out");
+ return decr;
+}
+
+/* callback to feed audiooutput buffer */
+static OSStatus audioDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *out = outOutputData->mBuffers[0].mData;
+ HWVoiceOut *hw = hwptr;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
+ int rpos, live;
+ st_sample_t *src;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ frameCount = core->audioDevicePropertyBufferFrameSize;
+ live = core->live;
+
+ /* if there are not enough samples, set signal and return */
+ if (live < frameCount) {
+ inInputTime = 0;
+ coreaudio_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ rpos = core->rpos;
+ src = hw->mix_buf + rpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ *out++ = src[frame].l; /* left channel */
+ *out++ = src[frame].r; /* right channel */
+#else
+#ifdef RECIPROCAL
+ *out++ = src[frame].l * scale; /* left channel */
+ *out++ = src[frame].r * scale; /* right channel */
+#else
+ *out++ = src[frame].l / scale; /* left channel */
+ *out++ = src[frame].r / scale; /* right channel */
+#endif
+#endif
+ }
+
+ rpos = (rpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->rpos = rpos;
+
+ coreaudio_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ OSStatus status;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ UInt32 propertySize;
+ int err;
+ int bits = 8;
+ const char *typ = "playback";
+ AudioValueRange frameRange;
+
+ /* create mutex */
+ err = pthread_mutex_init(&core->mutex, NULL);
+ if (err) {
+ dolog("Could not create mutex\nReason: %s\n", strerror (err));
+ return -1;
+ }
+
+ if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
+ bits = 16;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+
+ /* open default output device */
+ propertySize = sizeof(core->outputDeviceID);
+ status = AudioHardwareGetProperty(
+ kAudioHardwarePropertyDefaultOutputDevice,
+ &propertySize,
+ &core->outputDeviceID);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get default output Device\n");
+ return -1;
+ }
+ if (core->outputDeviceID == kAudioDeviceUnknown) {
+ dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
+ return -1;
+ }
+
+ /* get minimum and maximum buffer frame sizes */
+ propertySize = sizeof(frameRange);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ 0,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &propertySize,
+ &frameRange);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame range\n");
+ return -1;
+ }
+
+ if (frameRange.mMinimum > conf.buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+ }
+ else if (frameRange.mMaximum < conf.buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ }
+ else {
+ core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+ }
+
+ /* set Buffer Frame Size */
+ propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ status = AudioDeviceSetProperty(
+ core->outputDeviceID,
+ NULL,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ propertySize,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not set device buffer frame size %ld\n",
+ core->audioDevicePropertyBufferFrameSize);
+ return -1;
+ }
+
+ /* get Buffer Frame Size */
+ propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ &propertySize,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame size\n");
+ return -1;
+ }
+ hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+
+ /* get StreamFormat */
+ propertySize = sizeof(core->outputStreamBasicDescription);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ false,
+ kAudioDevicePropertyStreamFormat,
+ &propertySize,
+ &core->outputStreamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get Device Stream properties\n");
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Samplerate */
+ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
+ propertySize = sizeof(core->outputStreamBasicDescription);
+ status = AudioDeviceSetProperty(
+ core->outputDeviceID,
+ 0,
+ 0,
+ 0,
+ kAudioDevicePropertyStreamFormat,
+ propertySize,
+ &core->outputStreamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
+ as->freq);
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Callback */
+ status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* start Playback */
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not start playback\n");
+ AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void coreaudio_fini_out (HWVoiceOut *hw)
+{
+ OSStatus status;
+ int err;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ if (!conf.isAtexit) {
+ /* stop playback */
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not stop playback\n");
+ }
+ }
+
+ /* remove callback */
+ status = AudioDeviceRemoveIOProc(core->outputDeviceID,
+ audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not remove IOProc\n");
+ }
+ }
+ core->outputDeviceID = kAudioDeviceUnknown;
+
+ /* destroy mutex */
+ err = pthread_mutex_destroy(&core->mutex);
+ if (err) {
+ dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ }
+}
+
+static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ OSStatus status;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ /* start playback */
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not resume playback\n");
+ }
+ }
+ break;
+
+ case VOICE_DISABLE:
+ /* stop playback */
+ if (!conf.isAtexit) {
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not pause playback\n");
+ }
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static void *coreaudio_audio_init (void)
+{
+ atexit(coreaudio_atexit);
+ return &coreaudio_audio_init;
+}
+
+static void coreaudio_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option coreaudio_options[] = {
+ {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
+ "Size of the buffer in frames", NULL, 0},
+ {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
+ "Number of buffers", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops coreaudio_pcm_ops = {
+ coreaudio_init_out,
+ coreaudio_fini_out,
+ coreaudio_run_out,
+ coreaudio_write,
+ coreaudio_ctl_out,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct audio_driver coreaudio_audio_driver = {
+ INIT_FIELD (name = ) "coreaudio",
+ INIT_FIELD (descr = )
+ "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ INIT_FIELD (options = ) coreaudio_options,
+ INIT_FIELD (init = ) coreaudio_audio_init,
+ INIT_FIELD (fini = ) coreaudio_audio_fini,
+ INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 0,
+ INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+ INIT_FIELD (voice_size_in = ) 0
+};
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
new file mode 100644
index 0000000..0896b04
--- /dev/null
+++ b/audio/dsound_template.h
@@ -0,0 +1,282 @@
+/*
+ * QEMU DirectSound audio driver header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef DSBTYPE_IN
+#define NAME "capture buffer"
+#define TYPE in
+#define IFACE IDirectSoundCaptureBuffer
+#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
+#define FIELD dsound_capture_buffer
+#else
+#define NAME "playback buffer"
+#define TYPE out
+#define IFACE IDirectSoundBuffer
+#define BUFPTR LPDIRECTSOUNDBUFFER
+#define FIELD dsound_buffer
+#endif
+
+static int glue (dsound_unlock_, TYPE) (
+ BUFPTR buf,
+ LPVOID p1,
+ LPVOID p2,
+ DWORD blen1,
+ DWORD blen2
+ )
+{
+ HRESULT hr;
+
+ hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not unlock " NAME "\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int glue (dsound_lock_, TYPE) (
+ BUFPTR buf,
+ struct audio_pcm_info *info,
+ DWORD pos,
+ DWORD len,
+ LPVOID *p1p,
+ LPVOID *p2p,
+ DWORD *blen1p,
+ DWORD *blen2p,
+ int entire
+ )
+{
+ HRESULT hr;
+ int i;
+ LPVOID p1 = NULL, p2 = NULL;
+ DWORD blen1 = 0, blen2 = 0;
+ DWORD flag;
+
+#ifdef DSBTYPE_IN
+ flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
+#else
+ flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
+#endif
+ for (i = 0; i < conf.lock_retries; ++i) {
+ hr = glue (IFACE, _Lock) (
+ buf,
+ pos,
+ len,
+ &p1,
+ &blen1,
+ &p2,
+ &blen2,
+ flag
+ );
+
+ if (FAILED (hr)) {
+#ifndef DSBTYPE_IN
+ if (hr == DSERR_BUFFERLOST) {
+ if (glue (dsound_restore_, TYPE) (buf)) {
+ dsound_logerr (hr, "Could not lock " NAME "\n");
+ goto fail;
+ }
+ continue;
+ }
+#endif
+ dsound_logerr (hr, "Could not lock " NAME "\n");
+ goto fail;
+ }
+
+ break;
+ }
+
+ if (i == conf.lock_retries) {
+ dolog ("%d attempts to lock " NAME " failed\n", i);
+ goto fail;
+ }
+
+ if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
+ dolog ("DirectSound returned misaligned buffer %ld %ld\n",
+ blen1, blen2);
+ glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
+ goto fail;
+ }
+
+ if (!p1 && blen1) {
+ dolog ("warning: !p1 && blen1=%ld\n", blen1);
+ blen1 = 0;
+ }
+
+ if (!p2 && blen2) {
+ dolog ("warning: !p2 && blen2=%ld\n", blen2);
+ blen2 = 0;
+ }
+
+ *p1p = p1;
+ *p2p = p2;
+ *blen1p = blen1;
+ *blen2p = blen2;
+ return 0;
+
+ fail:
+ *p1p = NULL - 1;
+ *p2p = NULL - 1;
+ *blen1p = -1;
+ *blen2p = -1;
+ return -1;
+}
+
+#ifdef DSBTYPE_IN
+static void dsound_fini_in (HWVoiceIn *hw)
+#else
+static void dsound_fini_out (HWVoiceOut *hw)
+#endif
+{
+ HRESULT hr;
+#ifdef DSBTYPE_IN
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+#else
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+#endif
+
+ if (ds->FIELD) {
+ hr = glue (IFACE, _Stop) (ds->FIELD);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop " NAME "\n");
+ }
+
+ hr = glue (IFACE, _Release) (ds->FIELD);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release " NAME "\n");
+ }
+ ds->FIELD = NULL;
+ }
+}
+
+#ifdef DSBTYPE_IN
+static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
+#else
+static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
+#endif
+{
+ int err;
+ HRESULT hr;
+ dsound *s = &glob_dsound;
+ WAVEFORMATEX wfx;
+ audsettings_t obt_as;
+#ifdef DSBTYPE_IN
+ const char *typ = "ADC";
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ DSCBUFFERDESC bd;
+ DSCBCAPS bc;
+#else
+ const char *typ = "DAC";
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ DSBUFFERDESC bd;
+ DSBCAPS bc;
+#endif
+
+ err = waveformat_from_audio_settings (&wfx, as);
+ if (err) {
+ return -1;
+ }
+
+ memset (&bd, 0, sizeof (bd));
+ bd.dwSize = sizeof (bd);
+ bd.lpwfxFormat = &wfx;
+#ifdef DSBTYPE_IN
+ bd.dwBufferBytes = conf.bufsize_in;
+ hr = IDirectSoundCapture_CreateCaptureBuffer (
+ s->dsound_capture,
+ &bd,
+ &ds->dsound_capture_buffer,
+ NULL
+ );
+#else
+ bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ bd.dwBufferBytes = conf.bufsize_out;
+ hr = IDirectSound_CreateSoundBuffer (
+ s->dsound,
+ &bd,
+ &ds->dsound_buffer,
+ NULL
+ );
+#endif
+
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
+ return -1;
+ }
+
+ hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+ goto fail0;
+ }
+
+#ifdef DEBUG_DSOUND
+ dolog (NAME "\n");
+ print_wave_format (&wfx);
+#endif
+
+ memset (&bc, 0, sizeof (bc));
+ bc.dwSize = sizeof (bc);
+
+ hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+ goto fail0;
+ }
+
+ err = waveformat_to_audio_settings (&wfx, &obt_as);
+ if (err) {
+ goto fail0;
+ }
+
+ ds->first_time = 1;
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ if (bc.dwBufferBytes & hw->info.align) {
+ dolog (
+ "GetCaps returned misaligned buffer size %ld, alignment %d\n",
+ bc.dwBufferBytes, hw->info.align + 1
+ );
+ }
+ hw->samples = bc.dwBufferBytes >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+ dolog ("caps %ld, desc %ld\n",
+ bc.dwBufferBytes, bd.dwBufferBytes);
+
+ dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
+ hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
+#endif
+ return 0;
+
+ fail0:
+ glue (dsound_fini_, TYPE) (hw);
+ return -1;
+}
+
+#undef NAME
+#undef TYPE
+#undef IFACE
+#undef BUFPTR
+#undef FIELD
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
new file mode 100644
index 0000000..90a0333
--- /dev/null
+++ b/audio/dsoundaudio.c
@@ -0,0 +1,1080 @@
+/*
+ * QEMU DirectSound audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
+ */
+
+#include "vl.h"
+
+#define AUDIO_CAP "dsound"
+#include "audio_int.h"
+
+#include <windows.h>
+#include <objbase.h>
+#include <dsound.h>
+
+/* #define DEBUG_DSOUND */
+
+static struct {
+ int lock_retries;
+ int restore_retries;
+ int getstatus_retries;
+ int set_primary;
+ int bufsize_in;
+ int bufsize_out;
+ audsettings_t settings;
+ int latency_millis;
+} conf = {
+ 1,
+ 1,
+ 1,
+ 0,
+ 16384,
+ 16384,
+ {
+ 44100,
+ 2,
+ AUD_FMT_S16
+ },
+ 10
+};
+
+typedef struct {
+ LPDIRECTSOUND dsound;
+ LPDIRECTSOUNDCAPTURE dsound_capture;
+ LPDIRECTSOUNDBUFFER dsound_primary_buffer;
+ audsettings_t settings;
+} dsound;
+
+static dsound glob_dsound;
+
+typedef struct {
+ HWVoiceOut hw;
+ LPDIRECTSOUNDBUFFER dsound_buffer;
+ DWORD old_pos;
+ int first_time;
+#ifdef DEBUG_DSOUND
+ DWORD old_ppos;
+ DWORD played;
+ DWORD mixed;
+#endif
+} DSoundVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int first_time;
+ LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
+} DSoundVoiceIn;
+
+static void dsound_log_hresult (HRESULT hr)
+{
+ const char *str = "BUG";
+
+ switch (hr) {
+ case DS_OK:
+ str = "The method succeeded";
+ break;
+#ifdef DS_NO_VIRTUALIZATION
+ case DS_NO_VIRTUALIZATION:
+ str = "The buffer was created, but another 3D algorithm was substituted";
+ break;
+#endif
+#ifdef DS_INCOMPLETE
+ case DS_INCOMPLETE:
+ str = "The method succeeded, but not all the optional effects were obtained";
+ break;
+#endif
+#ifdef DSERR_ACCESSDENIED
+ case DSERR_ACCESSDENIED:
+ str = "The request failed because access was denied";
+ break;
+#endif
+#ifdef DSERR_ALLOCATED
+ case DSERR_ALLOCATED:
+ str = "The request failed because resources, such as a priority level, were already in use by another caller";
+ break;
+#endif
+#ifdef DSERR_ALREADYINITIALIZED
+ case DSERR_ALREADYINITIALIZED:
+ str = "The object is already initialized";
+ break;
+#endif
+#ifdef DSERR_BADFORMAT
+ case DSERR_BADFORMAT:
+ str = "The specified wave format is not supported";
+ break;
+#endif
+#ifdef DSERR_BADSENDBUFFERGUID
+ case DSERR_BADSENDBUFFERGUID:
+ str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
+ break;
+#endif
+#ifdef DSERR_BUFFERLOST
+ case DSERR_BUFFERLOST:
+ str = "The buffer memory has been lost and must be restored";
+ break;
+#endif
+#ifdef DSERR_BUFFERTOOSMALL
+ case DSERR_BUFFERTOOSMALL:
+ str = "The buffer size is not great enough to enable effects processing";
+ break;
+#endif
+#ifdef DSERR_CONTROLUNAVAIL
+ case DSERR_CONTROLUNAVAIL:
+ str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC";
+ break;
+#endif
+#ifdef DSERR_DS8_REQUIRED
+ case DSERR_DS8_REQUIRED:
+ str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
+ break;
+#endif
+#ifdef DSERR_FXUNAVAILABLE
+ case DSERR_FXUNAVAILABLE:
+ str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software";
+ break;
+#endif
+#ifdef DSERR_GENERIC
+ case DSERR_GENERIC :
+ str = "An undetermined error occurred inside the DirectSound subsystem";
+ break;
+#endif
+#ifdef DSERR_INVALIDCALL
+ case DSERR_INVALIDCALL:
+ str = "This function is not valid for the current state of this object";
+ break;
+#endif
+#ifdef DSERR_INVALIDPARAM
+ case DSERR_INVALIDPARAM:
+ str = "An invalid parameter was passed to the returning function";
+ break;
+#endif
+#ifdef DSERR_NOAGGREGATION
+ case DSERR_NOAGGREGATION:
+ str = "The object does not support aggregation";
+ break;
+#endif
+#ifdef DSERR_NODRIVER
+ case DSERR_NODRIVER:
+ str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
+ break;
+#endif
+#ifdef DSERR_NOINTERFACE
+ case DSERR_NOINTERFACE:
+ str = "The requested COM interface is not available";
+ break;
+#endif
+#ifdef DSERR_OBJECTNOTFOUND
+ case DSERR_OBJECTNOTFOUND:
+ str = "The requested object was not found";
+ break;
+#endif
+#ifdef DSERR_OTHERAPPHASPRIO
+ case DSERR_OTHERAPPHASPRIO:
+ str = "Another application has a higher priority level, preventing this call from succeeding";
+ break;
+#endif
+#ifdef DSERR_OUTOFMEMORY
+ case DSERR_OUTOFMEMORY:
+ str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
+ break;
+#endif
+#ifdef DSERR_PRIOLEVELNEEDED
+ case DSERR_PRIOLEVELNEEDED:
+ str = "A cooperative level of DSSCL_PRIORITY or higher is required";
+ break;
+#endif
+#ifdef DSERR_SENDLOOP
+ case DSERR_SENDLOOP:
+ str = "A circular loop of send effects was detected";
+ break;
+#endif
+#ifdef DSERR_UNINITIALIZED
+ case DSERR_UNINITIALIZED:
+ str = "The Initialize method has not been called or has not been called successfully before other methods were called";
+ break;
+#endif
+#ifdef DSERR_UNSUPPORTED
+ case DSERR_UNSUPPORTED:
+ str = "The function called is not supported at this time";
+ break;
+#endif
+ default:
+ AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
+ return;
+ }
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) dsound_logerr (
+ HRESULT hr,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ dsound_log_hresult (hr);
+}
+
+static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
+ HRESULT hr,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ dsound_log_hresult (hr);
+}
+
+static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+{
+ return (millis * info->bytes_per_second) / 1000;
+}
+
+#ifdef DEBUG_DSOUND
+static void print_wave_format (WAVEFORMATEX *wfx)
+{
+ dolog ("tag = %d\n", wfx->wFormatTag);
+ dolog ("nChannels = %d\n", wfx->nChannels);
+ dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec);
+ dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
+ dolog ("nBlockAlign = %d\n", wfx->nBlockAlign);
+ dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample);
+ dolog ("cbSize = %d\n", wfx->cbSize);
+}
+#endif
+
+static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
+{
+ HRESULT hr;
+ int i;
+
+ for (i = 0; i < conf.restore_retries; ++i) {
+ hr = IDirectSoundBuffer_Restore (dsb);
+
+ switch (hr) {
+ case DS_OK:
+ return 0;
+
+ case DSERR_BUFFERLOST:
+ continue;
+
+ default:
+ dsound_logerr (hr, "Could not restore playback buffer\n");
+ return -1;
+ }
+ }
+
+ dolog ("%d attempts to restore playback buffer failed\n", i);
+ return -1;
+}
+
+static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
+{
+ memset (wfx, 0, sizeof (*wfx));
+
+ wfx->wFormatTag = WAVE_FORMAT_PCM;
+ wfx->nChannels = as->nchannels;
+ wfx->nSamplesPerSec = as->freq;
+ wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
+ wfx->nBlockAlign = 1 << (as->nchannels == 2);
+ wfx->cbSize = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ wfx->wBitsPerSample = 8;
+ break;
+
+ case AUD_FMT_U8:
+ wfx->wBitsPerSample = 8;
+ break;
+
+ case AUD_FMT_S16:
+ wfx->wBitsPerSample = 16;
+ wfx->nAvgBytesPerSec <<= 1;
+ wfx->nBlockAlign <<= 1;
+ break;
+
+ case AUD_FMT_U16:
+ wfx->wBitsPerSample = 16;
+ wfx->nAvgBytesPerSec <<= 1;
+ wfx->nBlockAlign <<= 1;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
+{
+ if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
+ dolog ("Invalid wave format, tag is not PCM, but %d\n",
+ wfx->wFormatTag);
+ return -1;
+ }
+
+ if (!wfx->nSamplesPerSec) {
+ dolog ("Invalid wave format, frequency is zero\n");
+ return -1;
+ }
+ as->freq = wfx->nSamplesPerSec;
+
+ switch (wfx->nChannels) {
+ case 1:
+ as->nchannels = 1;
+ break;
+
+ case 2:
+ as->nchannels = 2;
+ break;
+
+ default:
+ dolog (
+ "Invalid wave format, number of channels is not 1 or 2, but %d\n",
+ wfx->nChannels
+ );
+ return -1;
+ }
+
+ switch (wfx->wBitsPerSample) {
+ case 8:
+ as->fmt = AUD_FMT_U8;
+ break;
+
+ case 16:
+ as->fmt = AUD_FMT_S16;
+ break;
+
+ default:
+ dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n",
+ wfx->wBitsPerSample);
+ return -1;
+ }
+
+ return 0;
+}
+
+#include "dsound_template.h"
+#define DSBTYPE_IN
+#include "dsound_template.h"
+#undef DSBTYPE_IN
+
+static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
+{
+ HRESULT hr;
+ int i;
+
+ for (i = 0; i < conf.getstatus_retries; ++i) {
+ hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get playback buffer status\n");
+ return -1;
+ }
+
+ if (*statusp & DSERR_BUFFERLOST) {
+ if (dsound_restore_out (dsb)) {
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
+ DWORD *statusp)
+{
+ HRESULT hr;
+
+ hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get capture buffer status\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+ int src_len1 = dst_len;
+ int src_len2 = 0;
+ int pos = hw->rpos + dst_len;
+ st_sample_t *src1 = hw->mix_buf + hw->rpos;
+ st_sample_t *src2 = NULL;
+
+ if (pos > hw->samples) {
+ src_len1 = hw->samples - hw->rpos;
+ src2 = hw->mix_buf;
+ src_len2 = dst_len - src_len1;
+ pos = src_len2;
+ }
+
+ if (src_len1) {
+ hw->clip (dst, src1, src_len1);
+ }
+
+ if (src_len2) {
+ dst = advance (dst, src_len1 << hw->info.shift);
+ hw->clip (dst, src2, src_len2);
+ }
+
+ hw->rpos = pos % hw->samples;
+}
+
+static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
+{
+ int err;
+ LPVOID p1, p2;
+ DWORD blen1, blen2, len1, len2;
+
+ err = dsound_lock_out (
+ dsb,
+ &hw->info,
+ 0,
+ hw->samples << hw->info.shift,
+ &p1, &p2,
+ &blen1, &blen2,
+ 1
+ );
+ if (err) {
+ return;
+ }
+
+ len1 = blen1 >> hw->info.shift;
+ len2 = blen2 >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+ dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
+ p1, blen1, len1,
+ p2, blen2, len2);
+#endif
+
+ if (p1 && len1) {
+ audio_pcm_info_clear_buf (&hw->info, p1, len1);
+ }
+
+ if (p2 && len2) {
+ audio_pcm_info_clear_buf (&hw->info, p2, len2);
+ }
+
+ dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+}
+
+static void dsound_close (dsound *s)
+{
+ HRESULT hr;
+
+ if (s->dsound_primary_buffer) {
+ hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release primary buffer\n");
+ }
+ s->dsound_primary_buffer = NULL;
+ }
+}
+
+static int dsound_open (dsound *s)
+{
+ int err;
+ HRESULT hr;
+ WAVEFORMATEX wfx;
+ DSBUFFERDESC dsbd;
+ HWND hwnd;
+
+ hwnd = GetForegroundWindow ();
+ hr = IDirectSound_SetCooperativeLevel (
+ s->dsound,
+ hwnd,
+ DSSCL_PRIORITY
+ );
+
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not set cooperative level for window %p\n",
+ hwnd);
+ return -1;
+ }
+
+ if (!conf.set_primary) {
+ return 0;
+ }
+
+ err = waveformat_from_audio_settings (&wfx, &conf.settings);
+ if (err) {
+ return -1;
+ }
+
+ memset (&dsbd, 0, sizeof (dsbd));
+ dsbd.dwSize = sizeof (dsbd);
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+ dsbd.dwBufferBytes = 0;
+ dsbd.lpwfxFormat = NULL;
+
+ hr = IDirectSound_CreateSoundBuffer (
+ s->dsound,
+ &dsbd,
+ &s->dsound_primary_buffer,
+ NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create primary playback buffer\n");
+ return -1;
+ }
+
+ hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not set primary playback buffer format\n");
+ }
+
+ hr = IDirectSoundBuffer_GetFormat (
+ s->dsound_primary_buffer,
+ &wfx,
+ sizeof (wfx),
+ NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get primary playback buffer format\n");
+ goto fail0;
+ }
+
+#ifdef DEBUG_DSOUND
+ dolog ("Primary\n");
+ print_wave_format (&wfx);
+#endif
+
+ err = waveformat_to_audio_settings (&wfx, &s->settings);
+ if (err) {
+ goto fail0;
+ }
+
+ return 0;
+
+ fail0:
+ dsound_close (s);
+ return -1;
+}
+
+static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ HRESULT hr;
+ DWORD status;
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+
+ if (!dsb) {
+ dolog ("Attempt to control voice without a buffer\n");
+ return 0;
+ }
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (dsound_get_status_out (dsb, &status)) {
+ return -1;
+ }
+
+ if (status & DSBSTATUS_PLAYING) {
+ dolog ("warning: Voice is already playing\n");
+ return 0;
+ }
+
+ dsound_clear_sample (hw, dsb);
+
+ hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not start playing buffer\n");
+ return -1;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (dsound_get_status_out (dsb, &status)) {
+ return -1;
+ }
+
+ if (status & DSBSTATUS_PLAYING) {
+ hr = IDirectSoundBuffer_Stop (dsb);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop playing buffer\n");
+ return -1;
+ }
+ }
+ else {
+ dolog ("warning: Voice is not playing\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int dsound_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int dsound_run_out (HWVoiceOut *hw)
+{
+ int err;
+ HRESULT hr;
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+ int live, len, hwshift;
+ DWORD blen1, blen2;
+ DWORD len1, len2;
+ DWORD decr;
+ DWORD wpos, ppos, old_pos;
+ LPVOID p1, p2;
+ int bufsize;
+
+ if (!dsb) {
+ dolog ("Attempt to run empty with playback buffer\n");
+ return 0;
+ }
+
+ hwshift = hw->info.shift;
+ bufsize = hw->samples << hwshift;
+
+ live = audio_pcm_hw_get_live_out (hw);
+
+ hr = IDirectSoundBuffer_GetCurrentPosition (
+ dsb,
+ &ppos,
+ ds->first_time ? &wpos : NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get playback buffer position\n");
+ return 0;
+ }
+
+ len = live << hwshift;
+
+ if (ds->first_time) {
+ if (conf.latency_millis) {
+ DWORD cur_blat;
+
+ cur_blat = audio_ring_dist (wpos, ppos, bufsize);
+ ds->first_time = 0;
+ old_pos = wpos;
+ old_pos +=
+ millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
+ old_pos %= bufsize;
+ old_pos &= ~hw->info.align;
+ }
+ else {
+ old_pos = wpos;
+ }
+#ifdef DEBUG_DSOUND
+ ds->played = 0;
+ ds->mixed = 0;
+#endif
+ }
+ else {
+ if (ds->old_pos == ppos) {
+#ifdef DEBUG_DSOUND
+ dolog ("old_pos == ppos\n");
+#endif
+ return 0;
+ }
+
+#ifdef DEBUG_DSOUND
+ ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
+#endif
+ old_pos = ds->old_pos;
+ }
+
+ if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+ len = ppos - old_pos;
+ }
+ else {
+ if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
+ len = bufsize - old_pos + ppos;
+ }
+ }
+
+ if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
+ dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
+ len, bufsize, old_pos, ppos);
+ return 0;
+ }
+
+ len &= ~hw->info.align;
+ if (!len) {
+ return 0;
+ }
+
+#ifdef DEBUG_DSOUND
+ ds->old_ppos = ppos;
+#endif
+ err = dsound_lock_out (
+ dsb,
+ &hw->info,
+ old_pos,
+ len,
+ &p1, &p2,
+ &blen1, &blen2,
+ 0
+ );
+ if (err) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ dsound_write_sample (hw, p1, len1);
+ }
+
+ if (p2 && len2) {
+ dsound_write_sample (hw, p2, len2);
+ }
+
+ dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+ ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
+
+#ifdef DEBUG_DSOUND
+ ds->mixed += decr << hwshift;
+
+ dolog ("played %lu mixed %lu diff %ld sec %f\n",
+ ds->played,
+ ds->mixed,
+ ds->mixed - ds->played,
+ abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
+#endif
+ return decr;
+}
+
+static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ HRESULT hr;
+ DWORD status;
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+
+ if (!dscb) {
+ dolog ("Attempt to control capture voice without a buffer\n");
+ return -1;
+ }
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (dsound_get_status_in (dscb, &status)) {
+ return -1;
+ }
+
+ if (status & DSCBSTATUS_CAPTURING) {
+ dolog ("warning: Voice is already capturing\n");
+ return 0;
+ }
+
+ /* clear ?? */
+
+ hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not start capturing\n");
+ return -1;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (dsound_get_status_in (dscb, &status)) {
+ return -1;
+ }
+
+ if (status & DSCBSTATUS_CAPTURING) {
+ hr = IDirectSoundCaptureBuffer_Stop (dscb);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop capturing\n");
+ return -1;
+ }
+ }
+ else {
+ dolog ("warning: Voice is not capturing\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int dsound_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int dsound_run_in (HWVoiceIn *hw)
+{
+ int err;
+ HRESULT hr;
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+ int live, len, dead;
+ DWORD blen1, blen2;
+ DWORD len1, len2;
+ DWORD decr;
+ DWORD cpos, rpos;
+ LPVOID p1, p2;
+ int hwshift;
+
+ if (!dscb) {
+ dolog ("Attempt to run without capture buffer\n");
+ return 0;
+ }
+
+ hwshift = hw->info.shift;
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ if (!dead) {
+ return 0;
+ }
+
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
+ dscb,
+ &cpos,
+ ds->first_time ? &rpos : NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get capture buffer position\n");
+ return 0;
+ }
+
+ if (ds->first_time) {
+ ds->first_time = 0;
+ if (rpos & hw->info.align) {
+ ldebug ("warning: Misaligned capture read position %ld(%d)\n",
+ rpos, hw->info.align);
+ }
+ hw->wpos = rpos >> hwshift;
+ }
+
+ if (cpos & hw->info.align) {
+ ldebug ("warning: Misaligned capture position %ld(%d)\n",
+ cpos, hw->info.align);
+ }
+ cpos >>= hwshift;
+
+ len = audio_ring_dist (cpos, hw->wpos, hw->samples);
+ if (!len) {
+ return 0;
+ }
+ len = audio_MIN (len, dead);
+
+ err = dsound_lock_in (
+ dscb,
+ &hw->info,
+ hw->wpos << hwshift,
+ len << hwshift,
+ &p1,
+ &p2,
+ &blen1,
+ &blen2,
+ 0
+ );
+ if (err) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
+ }
+
+ if (p2 && len2) {
+ hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
+ }
+
+ dsound_unlock_in (dscb, p1, p2, blen1, blen2);
+ hw->wpos = (hw->wpos + decr) % hw->samples;
+ return decr;
+}
+
+static void dsound_audio_fini (void *opaque)
+{
+ HRESULT hr;
+ dsound *s = opaque;
+
+ if (!s->dsound) {
+ return;
+ }
+
+ hr = IDirectSound_Release (s->dsound);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSound\n");
+ }
+ s->dsound = NULL;
+
+ if (!s->dsound_capture) {
+ return;
+ }
+
+ hr = IDirectSoundCapture_Release (s->dsound_capture);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+ }
+ s->dsound_capture = NULL;
+}
+
+static void *dsound_audio_init (void)
+{
+ int err;
+ HRESULT hr;
+ dsound *s = &glob_dsound;
+
+ hr = CoInitialize (NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize COM\n");
+ return NULL;
+ }
+
+ hr = CoCreateInstance (
+ &CLSID_DirectSound,
+ NULL,
+ CLSCTX_ALL,
+ &IID_IDirectSound,
+ (void **) &s->dsound
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create DirectSound instance\n");
+ return NULL;
+ }
+
+ hr = IDirectSound_Initialize (s->dsound, NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize DirectSound\n");
+
+ hr = IDirectSound_Release (s->dsound);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSound\n");
+ }
+ s->dsound = NULL;
+ return NULL;
+ }
+
+ hr = CoCreateInstance (
+ &CLSID_DirectSoundCapture,
+ NULL,
+ CLSCTX_ALL,
+ &IID_IDirectSoundCapture,
+ (void **) &s->dsound_capture
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
+ }
+ else {
+ hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
+
+ hr = IDirectSoundCapture_Release (s->dsound_capture);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+ }
+ s->dsound_capture = NULL;
+ }
+ }
+
+ err = dsound_open (s);
+ if (err) {
+ dsound_audio_fini (s);
+ return NULL;
+ }
+
+ return s;
+}
+
+static struct audio_option dsound_options[] = {
+ {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
+ "Number of times to attempt locking the buffer", NULL, 0},
+ {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
+ "Number of times to attempt restoring the buffer", NULL, 0},
+ {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
+ "Number of times to attempt getting status of the buffer", NULL, 0},
+ {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
+ "Set the parameters of primary buffer", NULL, 0},
+ {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
+ "(undocumented)", NULL, 0},
+ {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
+ "Primary buffer frequency", NULL, 0},
+ {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
+ "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
+ {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
+ "Primary buffer format", NULL, 0},
+ {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
+ "(undocumented)", NULL, 0},
+ {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
+ "(undocumented)", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops dsound_pcm_ops = {
+ dsound_init_out,
+ dsound_fini_out,
+ dsound_run_out,
+ dsound_write,
+ dsound_ctl_out,
+
+ dsound_init_in,
+ dsound_fini_in,
+ dsound_run_in,
+ dsound_read,
+ dsound_ctl_in
+};
+
+struct audio_driver dsound_audio_driver = {
+ INIT_FIELD (name = ) "dsound",
+ INIT_FIELD (descr = )
+ "DirectSound http://wikipedia.org/wiki/DirectSound",
+ INIT_FIELD (options = ) dsound_options,
+ INIT_FIELD (init = ) dsound_audio_init,
+ INIT_FIELD (fini = ) dsound_audio_fini,
+ INIT_FIELD (pcm_ops = ) &dsound_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn)
+};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
new file mode 100644
index 0000000..5875ba1
--- /dev/null
+++ b/audio/fmodaudio.c
@@ -0,0 +1,685 @@
+/*
+ * QEMU FMOD audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <fmod.h>
+#include <fmod_errors.h>
+#include "vl.h"
+
+#define AUDIO_CAP "fmod"
+#include "audio_int.h"
+
+typedef struct FMODVoiceOut {
+ HWVoiceOut hw;
+ unsigned int old_pos;
+ FSOUND_SAMPLE *fmod_sample;
+ int channel;
+} FMODVoiceOut;
+
+typedef struct FMODVoiceIn {
+ HWVoiceIn hw;
+ FSOUND_SAMPLE *fmod_sample;
+} FMODVoiceIn;
+
+static struct {
+ const char *drvname;
+ int nb_samples;
+ int freq;
+ int nb_channels;
+ int bufsize;
+ int threshold;
+ int broken_adc;
+} conf = {
+ NULL,
+ 2048 * 2,
+ 44100,
+ 2,
+ 0,
+ 0,
+ 0
+};
+
+static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n",
+ FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n",
+ FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static int fmod_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void fmod_clear_sample (FMODVoiceOut *fmd)
+{
+ HWVoiceOut *hw = &fmd->hw;
+ int status;
+ void *p1 = 0, *p2 = 0;
+ unsigned int len1 = 0, len2 = 0;
+
+ status = FSOUND_Sample_Lock (
+ fmd->fmod_sample,
+ 0,
+ hw->samples << hw->info.shift,
+ &p1,
+ &p2,
+ &len1,
+ &len2
+ );
+
+ if (!status) {
+ fmod_logerr ("Failed to lock sample\n");
+ return;
+ }
+
+ if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
+ dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+ len1, len2, hw->info.align + 1);
+ goto fail;
+ }
+
+ if ((len1 + len2) - (hw->samples << hw->info.shift)) {
+ dolog ("Lock returned incomplete length %d, %d\n",
+ len1 + len2, hw->samples << hw->info.shift);
+ goto fail;
+ }
+
+ audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
+
+ fail:
+ status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
+ if (!status) {
+ fmod_logerr ("Failed to unlock sample\n");
+ }
+}
+
+static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+ int src_len1 = dst_len;
+ int src_len2 = 0;
+ int pos = hw->rpos + dst_len;
+ st_sample_t *src1 = hw->mix_buf + hw->rpos;
+ st_sample_t *src2 = NULL;
+
+ if (pos > hw->samples) {
+ src_len1 = hw->samples - hw->rpos;
+ src2 = hw->mix_buf;
+ src_len2 = dst_len - src_len1;
+ pos = src_len2;
+ }
+
+ if (src_len1) {
+ hw->clip (dst, src1, src_len1);
+ }
+
+ if (src_len2) {
+ dst = advance (dst, src_len1 << hw->info.shift);
+ hw->clip (dst, src2, src_len2);
+ }
+
+ hw->rpos = pos % hw->samples;
+}
+
+static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
+ unsigned int blen1, unsigned int blen2)
+{
+ int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
+ if (!status) {
+ fmod_logerr ("Failed to unlock sample\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int fmod_lock_sample (
+ FSOUND_SAMPLE *sample,
+ struct audio_pcm_info *info,
+ int pos,
+ int len,
+ void **p1,
+ void **p2,
+ unsigned int *blen1,
+ unsigned int *blen2
+ )
+{
+ int status;
+
+ status = FSOUND_Sample_Lock (
+ sample,
+ pos << info->shift,
+ len << info->shift,
+ p1,
+ p2,
+ blen1,
+ blen2
+ );
+
+ if (!status) {
+ fmod_logerr ("Failed to lock sample\n");
+ return -1;
+ }
+
+ if ((*blen1 & info->align) || (*blen2 & info->align)) {
+ dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+ *blen1, *blen2, info->align + 1);
+
+ fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
+
+ *p1 = NULL - 1;
+ *p2 = NULL - 1;
+ *blen1 = ~0U;
+ *blen2 = ~0U;
+ return -1;
+ }
+
+ if (!*p1 && *blen1) {
+ dolog ("warning: !p1 && blen1=%d\n", *blen1);
+ *blen1 = 0;
+ }
+
+ if (!p2 && *blen2) {
+ dolog ("warning: !p2 && blen2=%d\n", *blen2);
+ *blen2 = 0;
+ }
+
+ return 0;
+}
+
+static int fmod_run_out (HWVoiceOut *hw)
+{
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+ int live, decr;
+ void *p1 = 0, *p2 = 0;
+ unsigned int blen1 = 0, blen2 = 0;
+ unsigned int len1 = 0, len2 = 0;
+ int nb_live;
+
+ live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+ if (!live) {
+ return 0;
+ }
+
+ if (!hw->pending_disable
+ && nb_live
+ && (conf.threshold && live <= conf.threshold)) {
+ ldebug ("live=%d nb_live=%d\n", live, nb_live);
+ return 0;
+ }
+
+ decr = live;
+
+ if (fmd->channel >= 0) {
+ int len = decr;
+ int old_pos = fmd->old_pos;
+ int ppos = FSOUND_GetCurrentPosition (fmd->channel);
+
+ if (ppos == old_pos || !ppos) {
+ return 0;
+ }
+
+ if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+ len = ppos - old_pos;
+ }
+ else {
+ if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
+ len = hw->samples - old_pos + ppos;
+ }
+ }
+ decr = len;
+
+ if (audio_bug (AUDIO_FUNC, decr < 0)) {
+ dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
+ decr, live, ppos, old_pos, len);
+ return 0;
+ }
+ }
+
+
+ if (!decr) {
+ return 0;
+ }
+
+ if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+ fmd->old_pos, decr,
+ &p1, &p2,
+ &blen1, &blen2)) {
+ return 0;
+ }
+
+ len1 = blen1 >> hw->info.shift;
+ len2 = blen2 >> hw->info.shift;
+ ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ fmod_write_sample (hw, p1, len1);
+ }
+
+ if (p2 && len2) {
+ fmod_write_sample (hw, p2, len2);
+ }
+
+ fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+
+ fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
+ return decr;
+}
+
+static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
+{
+ int mode = FSOUND_LOOP_NORMAL;
+
+ switch (fmt) {
+ case AUD_FMT_S8:
+ mode |= FSOUND_SIGNED | FSOUND_8BITS;
+ break;
+
+ case AUD_FMT_U8:
+ mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
+ break;
+
+ case AUD_FMT_S16:
+ mode |= FSOUND_SIGNED | FSOUND_16BITS;
+ break;
+
+ case AUD_FMT_U16:
+ mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_FMOD
+ abort ();
+#endif
+ mode |= FSOUND_8BITS;
+ }
+ mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
+ return mode;
+}
+
+static void fmod_fini_out (HWVoiceOut *hw)
+{
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+ if (fmd->fmod_sample) {
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ fmd->fmod_sample = 0;
+
+ if (fmd->channel >= 0) {
+ FSOUND_StopSound (fmd->channel);
+ }
+ }
+}
+
+static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ int bits16, mode, channel;
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+ audsettings_t obt_as = *as;
+
+ mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+ fmd->fmod_sample = FSOUND_Sample_Alloc (
+ FSOUND_FREE, /* index */
+ conf.nb_samples, /* length */
+ mode, /* mode */
+ as->freq, /* freq */
+ 255, /* volume */
+ 128, /* pan */
+ 255 /* priority */
+ );
+
+ if (!fmd->fmod_sample) {
+ fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
+ return -1;
+ }
+
+ channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
+ if (channel < 0) {
+ fmod_logerr2 ("DAC", "Failed to start playing sound\n");
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ return -1;
+ }
+ fmd->channel = channel;
+
+ /* FMOD always operates on little endian frames? */
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+ bits16 = (mode & FSOUND_16BITS) != 0;
+ hw->samples = conf.nb_samples;
+ return 0;
+}
+
+static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ int status;
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ fmod_clear_sample (fmd);
+ status = FSOUND_SetPaused (fmd->channel, 0);
+ if (!status) {
+ fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
+ }
+ break;
+
+ case VOICE_DISABLE:
+ status = FSOUND_SetPaused (fmd->channel, 1);
+ if (!status) {
+ fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ int bits16, mode;
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+ audsettings_t obt_as = *as;
+
+ if (conf.broken_adc) {
+ return -1;
+ }
+
+ mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+ fmd->fmod_sample = FSOUND_Sample_Alloc (
+ FSOUND_FREE, /* index */
+ conf.nb_samples, /* length */
+ mode, /* mode */
+ as->freq, /* freq */
+ 255, /* volume */
+ 128, /* pan */
+ 255 /* priority */
+ );
+
+ if (!fmd->fmod_sample) {
+ fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
+ return -1;
+ }
+
+ /* FMOD always operates on little endian frames? */
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+ bits16 = (mode & FSOUND_16BITS) != 0;
+ hw->samples = conf.nb_samples;
+ return 0;
+}
+
+static void fmod_fini_in (HWVoiceIn *hw)
+{
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+ if (fmd->fmod_sample) {
+ FSOUND_Record_Stop ();
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ fmd->fmod_sample = 0;
+ }
+}
+
+static int fmod_run_in (HWVoiceIn *hw)
+{
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int live, dead, new_pos, len;
+ unsigned int blen1 = 0, blen2 = 0;
+ unsigned int len1, len2;
+ unsigned int decr;
+ void *p1, *p2;
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ if (!dead) {
+ return 0;
+ }
+
+ new_pos = FSOUND_Record_GetPosition ();
+ if (new_pos < 0) {
+ fmod_logerr ("Could not get recording position\n");
+ return 0;
+ }
+
+ len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
+ if (!len) {
+ return 0;
+ }
+ len = audio_MIN (len, dead);
+
+ if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+ hw->wpos, len,
+ &p1, &p2,
+ &blen1, &blen2)) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && blen1) {
+ hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
+ }
+ if (p2 && len2) {
+ hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
+ }
+
+ fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+ hw->wpos = (hw->wpos + decr) % hw->samples;
+ return decr;
+}
+
+static struct {
+ const char *name;
+ int type;
+} drvtab[] = {
+ {"none", FSOUND_OUTPUT_NOSOUND},
+#ifdef _WIN32
+ {"winmm", FSOUND_OUTPUT_WINMM},
+ {"dsound", FSOUND_OUTPUT_DSOUND},
+ {"a3d", FSOUND_OUTPUT_A3D},
+ {"asio", FSOUND_OUTPUT_ASIO},
+#endif
+#ifdef __linux__
+ {"oss", FSOUND_OUTPUT_OSS},
+ {"alsa", FSOUND_OUTPUT_ALSA},
+ {"esd", FSOUND_OUTPUT_ESD},
+#endif
+#ifdef __APPLE__
+ {"mac", FSOUND_OUTPUT_MAC},
+#endif
+#if 0
+ {"xbox", FSOUND_OUTPUT_XBOX},
+ {"ps2", FSOUND_OUTPUT_PS2},
+ {"gcube", FSOUND_OUTPUT_GC},
+#endif
+ {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
+};
+
+static void *fmod_audio_init (void)
+{
+ size_t i;
+ double ver;
+ int status;
+ int output_type = -1;
+ const char *drv = conf.drvname;
+
+ ver = FSOUND_GetVersion ();
+ if (ver < FMOD_VERSION) {
+ dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
+ return NULL;
+ }
+
+#ifdef __linux__
+ if (ver < 3.75) {
+ dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
+ "ADC will be disabled.\n");
+ conf.broken_adc = 1;
+ }
+#endif
+
+ if (drv) {
+ int found = 0;
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ if (!strcmp (drv, drvtab[i].name)) {
+ output_type = drvtab[i].type;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ dolog ("Unknown FMOD driver `%s'\n", drv);
+ dolog ("Valid drivers:\n");
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ dolog (" %s\n", drvtab[i].name);
+ }
+ }
+ }
+
+ if (output_type != -1) {
+ status = FSOUND_SetOutput (output_type);
+ if (!status) {
+ fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
+ return NULL;
+ }
+ }
+
+ if (conf.bufsize) {
+ status = FSOUND_SetBufferSize (conf.bufsize);
+ if (!status) {
+ fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
+ }
+ }
+
+ status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
+ if (!status) {
+ fmod_logerr ("FSOUND_Init failed\n");
+ return NULL;
+ }
+
+ return &conf;
+}
+
+static int fmod_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ int status;
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
+ if (!status) {
+ fmod_logerr ("Failed to start recording\n");
+ }
+ break;
+
+ case VOICE_DISABLE:
+ status = FSOUND_Record_Stop ();
+ if (!status) {
+ fmod_logerr ("Failed to stop recording\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static void fmod_audio_fini (void *opaque)
+{
+ (void) opaque;
+ FSOUND_Close ();
+}
+
+static struct audio_option fmod_options[] = {
+ {"DRV", AUD_OPT_STR, &conf.drvname,
+ "FMOD driver", NULL, 0},
+ {"FREQ", AUD_OPT_INT, &conf.freq,
+ "Default frequency", NULL, 0},
+ {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+ "Buffer size in samples", NULL, 0},
+ {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
+ "Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
+ {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
+ "(undocumented)", NULL, 0},
+#if 0
+ {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
+ "(undocumented)"},
+#endif
+
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops fmod_pcm_ops = {
+ fmod_init_out,
+ fmod_fini_out,
+ fmod_run_out,
+ fmod_write,
+ fmod_ctl_out,
+
+ fmod_init_in,
+ fmod_fini_in,
+ fmod_run_in,
+ fmod_read,
+ fmod_ctl_in
+};
+
+struct audio_driver fmod_audio_driver = {
+ INIT_FIELD (name = ) "fmod",
+ INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
+ INIT_FIELD (options = ) fmod_options,
+ INIT_FIELD (init = ) fmod_audio_init,
+ INIT_FIELD (fini = ) fmod_audio_fini,
+ INIT_FIELD (pcm_ops = ) &fmod_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
+};
diff --git a/audio/mixeng.c b/audio/mixeng.c
new file mode 100644
index 0000000..6308d41
--- /dev/null
+++ b/audio/mixeng.c
@@ -0,0 +1,277 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define AUDIO_CAP "mixeng"
+#include "audio_int.h"
+
+#define NOVOL
+
+/* 8 bit */
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+
+/* Signed 8 bit */
+#define IN_T int8_t
+#define IN_MIN SCHAR_MIN
+#define IN_MAX SCHAR_MAX
+#define SIGNED
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 8 bit */
+#define IN_T uint8_t
+#define IN_MIN 0
+#define IN_MAX UCHAR_MAX
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+
+/* Signed 16 bit */
+#define IN_T int16_t
+#define IN_MIN SHRT_MIN
+#define IN_MAX SHRT_MAX
+#define SIGNED
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+#define IN_T uint16_t
+#define IN_MIN 0
+#define IN_MAX USHRT_MAX
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+t_sample *mixeng_conv[2][2][2][2] = {
+ {
+ {
+ {
+ conv_natural_uint8_t_to_mono,
+ conv_natural_uint16_t_to_mono
+ },
+ {
+ conv_natural_uint8_t_to_mono,
+ conv_swap_uint16_t_to_mono
+ }
+ },
+ {
+ {
+ conv_natural_int8_t_to_mono,
+ conv_natural_int16_t_to_mono
+ },
+ {
+ conv_natural_int8_t_to_mono,
+ conv_swap_int16_t_to_mono
+ }
+ }
+ },
+ {
+ {
+ {
+ conv_natural_uint8_t_to_stereo,
+ conv_natural_uint16_t_to_stereo
+ },
+ {
+ conv_natural_uint8_t_to_stereo,
+ conv_swap_uint16_t_to_stereo
+ }
+ },
+ {
+ {
+ conv_natural_int8_t_to_stereo,
+ conv_natural_int16_t_to_stereo
+ },
+ {
+ conv_natural_int8_t_to_stereo,
+ conv_swap_int16_t_to_stereo
+ }
+ }
+ }
+};
+
+f_sample *mixeng_clip[2][2][2][2] = {
+ {
+ {
+ {
+ clip_natural_uint8_t_from_mono,
+ clip_natural_uint16_t_from_mono
+ },
+ {
+ clip_natural_uint8_t_from_mono,
+ clip_swap_uint16_t_from_mono
+ }
+ },
+ {
+ {
+ clip_natural_int8_t_from_mono,
+ clip_natural_int16_t_from_mono
+ },
+ {
+ clip_natural_int8_t_from_mono,
+ clip_swap_int16_t_from_mono
+ }
+ }
+ },
+ {
+ {
+ {
+ clip_natural_uint8_t_from_stereo,
+ clip_natural_uint16_t_from_stereo
+ },
+ {
+ clip_natural_uint8_t_from_stereo,
+ clip_swap_uint16_t_from_stereo
+ }
+ },
+ {
+ {
+ clip_natural_int8_t_from_stereo,
+ clip_natural_int16_t_from_stereo
+ },
+ {
+ clip_natural_int8_t_from_stereo,
+ clip_swap_int16_t_from_stereo
+ }
+ }
+ }
+};
+
+/*
+ * August 21, 1998
+ * Copyright 1998 Fabrice Bellard.
+ *
+ * [Rewrote completly the code of Lance Norskog And Sundry
+ * Contributors with a more efficient algorithm.]
+ *
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+ * Sound Tools rate change effect file.
+ */
+/*
+ * Linear Interpolation.
+ *
+ * The use of fractional increment allows us to use no buffer. It
+ * avoid the problems at the end of the buffer we had with the old
+ * method which stored a possibly big buffer of size
+ * lcm(in_rate,out_rate).
+ *
+ * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
+ * the input & output frequencies are equal, a delay of one sample is
+ * introduced. Limited to processing 32-bit count worth of samples.
+ *
+ * 1 << FRAC_BITS evaluating to zero in several places. Changed with
+ * an (unsigned long) cast to make it safe. MarkMLl 2/1/99
+ */
+
+/* Private data */
+struct rate {
+ uint64_t opos;
+ uint64_t opos_inc;
+ uint32_t ipos; /* position in the input stream (integer) */
+ st_sample_t ilast; /* last sample in the input stream */
+};
+
+/*
+ * Prepare processing.
+ */
+void *st_rate_start (int inrate, int outrate)
+{
+ struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
+
+ if (!rate) {
+ dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
+ return NULL;
+ }
+
+ rate->opos = 0;
+
+ /* increment */
+ rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
+
+ rate->ipos = 0;
+ rate->ilast.l = 0;
+ rate->ilast.r = 0;
+ return rate;
+}
+
+#define NAME st_rate_flow_mix
+#define OP(a, b) a += b
+#include "rate_template.h"
+
+#define NAME st_rate_flow
+#define OP(a, b) a = b
+#include "rate_template.h"
+
+void st_rate_stop (void *opaque)
+{
+ qemu_free (opaque);
+}
+
+void mixeng_clear (st_sample_t *buf, int len)
+{
+ memset (buf, 0, len * sizeof (st_sample_t));
+}
diff --git a/audio/mixeng.h b/audio/mixeng.h
new file mode 100644
index 0000000..9e3bac1
--- /dev/null
+++ b/audio/mixeng.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU Mixing engine header
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_MIXENG_H
+#define QEMU_MIXENG_H
+
+#ifdef FLOAT_MIXENG
+typedef float real_t;
+typedef struct { int mute; real_t r; real_t l; } volume_t;
+typedef struct { real_t l; real_t r; } st_sample_t;
+#else
+typedef struct { int mute; int64_t r; int64_t l; } volume_t;
+typedef struct { int64_t l; int64_t r; } st_sample_t;
+#endif
+
+typedef void (t_sample) (st_sample_t *dst, const void *src,
+ int samples, volume_t *vol);
+typedef void (f_sample) (void *dst, const st_sample_t *src, int samples);
+
+extern t_sample *mixeng_conv[2][2][2][2];
+extern f_sample *mixeng_clip[2][2][2][2];
+
+void *st_rate_start (int inrate, int outrate);
+void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+ int *isamp, int *osamp);
+void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+ int *isamp, int *osamp);
+void st_rate_stop (void *opaque);
+void mixeng_clear (st_sample_t *buf, int len);
+
+#endif /* mixeng.h */
diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
new file mode 100644
index 0000000..d726441
--- /dev/null
+++ b/audio/mixeng_template.h
@@ -0,0 +1,177 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Tusen tack till Mike Nordell
+ * dec++'ified by Dscho
+ */
+
+#ifndef SIGNED
+#define HALF (IN_MAX >> 1)
+#endif
+
+#ifdef NOVOL
+#define VOL(a, b) a
+#else
+#ifdef FLOAT_MIXENG
+#define VOL(a, b) ((a) * (b))
+#else
+#define VOL(a, b) ((a) * (b)) >> 32
+#endif
+#endif
+
+#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
+
+#ifdef FLOAT_MIXENG
+static real_t inline glue (conv_, ET) (IN_T v)
+{
+ IN_T nv = ENDIAN_CONVERT (v);
+
+#ifdef RECIPROCAL
+#ifdef SIGNED
+ return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
+#else
+ return (nv - HALF) * (1.f / (real_t) IN_MAX);
+#endif
+#else /* !RECIPROCAL */
+#ifdef SIGNED
+ return nv / (real_t) (IN_MAX - IN_MIN);
+#else
+ return (nv - HALF) / (real_t) IN_MAX;
+#endif
+#endif
+}
+
+static IN_T inline glue (clip_, ET) (real_t v)
+{
+ if (v >= 0.5) {
+ return IN_MAX;
+ }
+ else if (v < -0.5) {
+ return IN_MIN;
+ }
+
+#ifdef SIGNED
+ return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
+#else
+ return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
+#endif
+}
+
+#else /* !FLOAT_MIXENG */
+
+static inline int64_t glue (conv_, ET) (IN_T v)
+{
+ IN_T nv = ENDIAN_CONVERT (v);
+#ifdef SIGNED
+ return ((int64_t) nv) << (32 - SHIFT);
+#else
+ return ((int64_t) nv - HALF) << (32 - SHIFT);
+#endif
+}
+
+static inline IN_T glue (clip_, ET) (int64_t v)
+{
+ if (v >= 0x7f000000) {
+ return IN_MAX;
+ }
+ else if (v < -2147483648LL) {
+ return IN_MIN;
+ }
+
+#ifdef SIGNED
+ return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
+#else
+ return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
+#endif
+}
+#endif
+
+static void glue (glue (conv_, ET), _to_stereo)
+ (st_sample_t *dst, const void *src, int samples, volume_t *vol)
+{
+ st_sample_t *out = dst;
+ IN_T *in = (IN_T *) src;
+#ifndef NOVOL
+ if (vol->mute) {
+ mixeng_clear (dst, samples);
+ return;
+ }
+#else
+ (void) vol;
+#endif
+ while (samples--) {
+ out->l = VOL (glue (conv_, ET) (*in++), vol->l);
+ out->r = VOL (glue (conv_, ET) (*in++), vol->r);
+ out += 1;
+ }
+}
+
+static void glue (glue (conv_, ET), _to_mono)
+ (st_sample_t *dst, const void *src, int samples, volume_t *vol)
+{
+ st_sample_t *out = dst;
+ IN_T *in = (IN_T *) src;
+#ifndef NOVOL
+ if (vol->mute) {
+ mixeng_clear (dst, samples);
+ return;
+ }
+#else
+ (void) vol;
+#endif
+ while (samples--) {
+ out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
+ out->r = out->l;
+ out += 1;
+ in += 1;
+ }
+}
+
+static void glue (glue (clip_, ET), _from_stereo)
+ (void *dst, const st_sample_t *src, int samples)
+{
+ const st_sample_t *in = src;
+ IN_T *out = (IN_T *) dst;
+ while (samples--) {
+ *out++ = glue (clip_, ET) (in->l);
+ *out++ = glue (clip_, ET) (in->r);
+ in += 1;
+ }
+}
+
+static void glue (glue (clip_, ET), _from_mono)
+ (void *dst, const st_sample_t *src, int samples)
+{
+ const st_sample_t *in = src;
+ IN_T *out = (IN_T *) dst;
+ while (samples--) {
+ *out++ = glue (clip_, ET) (in->l + in->r);
+ in += 1;
+ }
+}
+
+#undef ET
+#undef HALF
+#undef VOL
diff --git a/audio/noaudio.c b/audio/noaudio.c
new file mode 100644
index 0000000..a3423e5
--- /dev/null
+++ b/audio/noaudio.c
@@ -0,0 +1,172 @@
+/*
+ * QEMU Timer based audio emulation
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define AUDIO_CAP "noaudio"
+#include "audio_int.h"
+
+typedef struct NoVoiceOut {
+ HWVoiceOut hw;
+ int64_t old_ticks;
+} NoVoiceOut;
+
+typedef struct NoVoiceIn {
+ HWVoiceIn hw;
+ int64_t old_ticks;
+} NoVoiceIn;
+
+static int no_run_out (HWVoiceOut *hw)
+{
+ NoVoiceOut *no = (NoVoiceOut *) hw;
+ int live, decr, samples;
+ int64_t now;
+ int64_t ticks;
+ int64_t bytes;
+
+ live = audio_pcm_hw_get_live_out (&no->hw);
+ if (!live) {
+ return 0;
+ }
+
+ now = qemu_get_clock (vm_clock);
+ ticks = now - no->old_ticks;
+ bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+ bytes = audio_MIN (bytes, INT_MAX);
+ samples = bytes >> hw->info.shift;
+
+ no->old_ticks = now;
+ decr = audio_MIN (live, samples);
+ hw->rpos = (hw->rpos + decr) % hw->samples;
+ return decr;
+}
+
+static int no_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = 1024;
+ return 0;
+}
+
+static void no_fini_out (HWVoiceOut *hw)
+{
+ (void) hw;
+}
+
+static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = 1024;
+ return 0;
+}
+
+static void no_fini_in (HWVoiceIn *hw)
+{
+ (void) hw;
+}
+
+static int no_run_in (HWVoiceIn *hw)
+{
+ NoVoiceIn *no = (NoVoiceIn *) hw;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ int samples = 0;
+
+ if (dead) {
+ int64_t now = qemu_get_clock (vm_clock);
+ int64_t ticks = now - no->old_ticks;
+ int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+ no->old_ticks = now;
+ bytes = audio_MIN (bytes, INT_MAX);
+ samples = bytes >> hw->info.shift;
+ samples = audio_MIN (samples, dead);
+ }
+ return samples;
+}
+
+static int no_read (SWVoiceIn *sw, void *buf, int size)
+{
+ int samples = size >> sw->info.shift;
+ int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+ int to_clear = audio_MIN (samples, total);
+ audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
+ return to_clear;
+}
+
+static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static void *no_audio_init (void)
+{
+ return &no_audio_init;
+}
+
+static void no_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_pcm_ops no_pcm_ops = {
+ no_init_out,
+ no_fini_out,
+ no_run_out,
+ no_write,
+ no_ctl_out,
+
+ no_init_in,
+ no_fini_in,
+ no_run_in,
+ no_read,
+ no_ctl_in
+};
+
+struct audio_driver no_audio_driver = {
+ INIT_FIELD (name = ) "none",
+ INIT_FIELD (descr = ) "Timer based audio emulation",
+ INIT_FIELD (options = ) NULL,
+ INIT_FIELD (init = ) no_audio_init,
+ INIT_FIELD (fini = ) no_audio_fini,
+ INIT_FIELD (pcm_ops = ) &no_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn)
+};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
new file mode 100644
index 0000000..125e4c8
--- /dev/null
+++ b/audio/ossaudio.c
@@ -0,0 +1,768 @@
+/*
+ * QEMU OSS audio driver
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include "vl.h"
+
+#define AUDIO_CAP "oss"
+#include "audio_int.h"
+
+typedef struct OSSVoiceOut {
+ HWVoiceOut hw;
+ void *pcm_buf;
+ int fd;
+ int nfrags;
+ int fragsize;
+ int mmapped;
+ int old_optr;
+} OSSVoiceOut;
+
+typedef struct OSSVoiceIn {
+ HWVoiceIn hw;
+ void *pcm_buf;
+ int fd;
+ int nfrags;
+ int fragsize;
+ int old_optr;
+} OSSVoiceIn;
+
+static struct {
+ int try_mmap;
+ int nfrags;
+ int fragsize;
+ const char *devpath_out;
+ const char *devpath_in;
+ int debug;
+} conf = {
+ .try_mmap = 0,
+ .nfrags = 4,
+ .fragsize = 4096,
+ .devpath_out = "/dev/dsp",
+ .devpath_in = "/dev/dsp",
+ .debug = 0
+};
+
+struct oss_params {
+ int freq;
+ audfmt_e fmt;
+ int nchannels;
+ int nfrags;
+ int fragsize;
+};
+
+static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
+ int err,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void oss_anal_close (int *fdp)
+{
+ int err = close (*fdp);
+ if (err) {
+ oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
+ }
+ *fdp = -1;
+}
+
+static int oss_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int aud_to_ossfmt (audfmt_e fmt)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ return AFMT_S8;
+
+ case AUD_FMT_U8:
+ return AFMT_U8;
+
+ case AUD_FMT_S16:
+ return AFMT_S16_LE;
+
+ case AUD_FMT_U16:
+ return AFMT_U16_LE;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return AFMT_U8;
+ }
+}
+
+static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+{
+ switch (ossfmt) {
+ case AFMT_S8:
+ *endianness =0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case AFMT_U8:
+ *endianness = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case AFMT_S16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AFMT_U16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case AFMT_S16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AFMT_U16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ default:
+ dolog ("Unrecognized audio format %d\n", ossfmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined DEBUG_MISMATCHES || defined DEBUG
+static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
+{
+ dolog ("parameter | requested value | obtained value\n");
+ dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
+ dolog ("channels | %10d | %10d\n",
+ req->nchannels, obt->nchannels);
+ dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
+ dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
+ dolog ("fragsize | %10d | %10d\n",
+ req->fragsize, obt->fragsize);
+}
+#endif
+
+static int oss_open (int in, struct oss_params *req,
+ struct oss_params *obt, int *pfd)
+{
+ int fd;
+ int mmmmssss;
+ audio_buf_info abinfo;
+ int fmt, freq, nchannels;
+ const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+ const char *typ = in ? "ADC" : "DAC";
+
+ fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
+ if (-1 == fd) {
+ oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
+ return -1;
+ }
+
+ freq = req->freq;
+ nchannels = req->nchannels;
+ fmt = req->fmt;
+
+ if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
+ oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
+ oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
+ req->nchannels);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
+ oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
+ oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
+ goto err;
+ }
+
+ mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
+ oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
+ req->nfrags, req->fragsize);
+ goto err;
+ }
+
+ if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
+ oss_logerr2 (errno, typ, "Failed to get buffer length\n");
+ goto err;
+ }
+
+ obt->fmt = fmt;
+ obt->nchannels = nchannels;
+ obt->freq = freq;
+ obt->nfrags = abinfo.fragstotal;
+ obt->fragsize = abinfo.fragsize;
+ *pfd = fd;
+
+#ifdef DEBUG_MISMATCHES
+ if ((req->fmt != obt->fmt) ||
+ (req->nchannels != obt->nchannels) ||
+ (req->freq != obt->freq) ||
+ (req->fragsize != obt->fragsize) ||
+ (req->nfrags != obt->nfrags)) {
+ dolog ("Audio parameters mismatch\n");
+ oss_dump_info (req, obt);
+ }
+#endif
+
+#ifdef DEBUG
+ oss_dump_info (req, obt);
+#endif
+ return 0;
+
+ err:
+ oss_anal_close (&fd);
+ return -1;
+}
+
+static int oss_run_out (HWVoiceOut *hw)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+ int err, rpos, live, decr;
+ int samples;
+ uint8_t *dst;
+ st_sample_t *src;
+ struct audio_buf_info abinfo;
+ struct count_info cntinfo;
+ int bufsize;
+
+ live = audio_pcm_hw_get_live_out (hw);
+ if (!live) {
+ return 0;
+ }
+
+ bufsize = hw->samples << hw->info.shift;
+
+ if (oss->mmapped) {
+ int bytes;
+
+ err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
+ if (err < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+ return 0;
+ }
+
+ if (cntinfo.ptr == oss->old_optr) {
+ if (abs (hw->samples - live) < 64) {
+ dolog ("warning: Overrun\n");
+ }
+ return 0;
+ }
+
+ if (cntinfo.ptr > oss->old_optr) {
+ bytes = cntinfo.ptr - oss->old_optr;
+ }
+ else {
+ bytes = bufsize + cntinfo.ptr - oss->old_optr;
+ }
+
+ decr = audio_MIN (bytes >> hw->info.shift, live);
+ }
+ else {
+ err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
+ if (err < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+ return 0;
+ }
+
+ if (abinfo.bytes > bufsize) {
+ if (conf.debug) {
+ dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
+ "please report your OS/audio hw to malc@pulsesoft.com\n",
+ abinfo.bytes, bufsize);
+ }
+ abinfo.bytes = bufsize;
+ }
+
+ if (abinfo.bytes < 0) {
+ if (conf.debug) {
+ dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
+ abinfo.bytes, bufsize);
+ }
+ return 0;
+ }
+
+ decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
+ if (!decr) {
+ return 0;
+ }
+ }
+
+ samples = decr;
+ rpos = hw->rpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
+ int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+ src = hw->mix_buf + rpos;
+ dst = advance (oss->pcm_buf, rpos << hw->info.shift);
+
+ hw->clip (dst, src, convert_samples);
+ if (!oss->mmapped) {
+ int written;
+
+ written = write (oss->fd, dst, convert_samples << hw->info.shift);
+ /* XXX: follow errno recommendations ? */
+ if (written == -1) {
+ oss_logerr (
+ errno,
+ "Failed to write %d bytes of audio data from %p\n",
+ convert_samples << hw->info.shift,
+ dst
+ );
+ continue;
+ }
+
+ if (written != convert_samples << hw->info.shift) {
+ int wsamples = written >> hw->info.shift;
+ int wbytes = wsamples << hw->info.shift;
+ if (wbytes != written) {
+ dolog ("warning: Misaligned write %d (requested %d), "
+ "alignment %d\n",
+ wbytes, written, hw->info.align + 1);
+ }
+ decr -= wsamples;
+ rpos = (rpos + wsamples) % hw->samples;
+ break;
+ }
+ }
+
+ rpos = (rpos + convert_samples) % hw->samples;
+ samples -= convert_samples;
+ }
+ if (oss->mmapped) {
+ oss->old_optr = cntinfo.ptr;
+ }
+
+ hw->rpos = rpos;
+ return decr;
+}
+
+static void oss_fini_out (HWVoiceOut *hw)
+{
+ int err;
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ ldebug ("oss_fini\n");
+ oss_anal_close (&oss->fd);
+
+ if (oss->pcm_buf) {
+ if (oss->mmapped) {
+ err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+ if (err) {
+ oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
+ oss->pcm_buf, hw->samples << hw->info.shift);
+ }
+ }
+ else {
+ qemu_free (oss->pcm_buf);
+ }
+ oss->pcm_buf = NULL;
+ }
+}
+
+static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+ struct oss_params req, obt;
+ int endianness;
+ int err;
+ int fd;
+ audfmt_e effective_fmt;
+ audsettings_t obt_as;
+
+ oss->fd = -1;
+
+ req.fmt = aud_to_ossfmt (as->fmt);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.fragsize = conf.fragsize;
+ req.nfrags = conf.nfrags;
+
+ if (oss_open (0, &req, &obt, &fd)) {
+ return -1;
+ }
+
+ err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ oss->nfrags = obt.nfrags;
+ oss->fragsize = obt.fragsize;
+
+ if (obt.nfrags * obt.fragsize & hw->info.align) {
+ dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
+ obt.nfrags * obt.fragsize, hw->info.align + 1);
+ }
+
+ hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+
+ oss->mmapped = 0;
+ if (conf.try_mmap) {
+ oss->pcm_buf = mmap (
+ 0,
+ hw->samples << hw->info.shift,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fd,
+ 0
+ );
+ if (oss->pcm_buf == MAP_FAILED) {
+ oss_logerr (errno, "Failed to map %d bytes of DAC\n",
+ hw->samples << hw->info.shift);
+ } else {
+ int err;
+ int trig = 0;
+ if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+ }
+ else {
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ }
+ else {
+ oss->mmapped = 1;
+ }
+ }
+
+ if (!oss->mmapped) {
+ err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+ if (err) {
+ oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
+ oss->pcm_buf, hw->samples << hw->info.shift);
+ }
+ }
+ }
+ }
+
+ if (!oss->mmapped) {
+ oss->pcm_buf = audio_calloc (
+ AUDIO_FUNC,
+ hw->samples,
+ 1 << hw->info.shift
+ );
+ if (!oss->pcm_buf) {
+ dolog (
+ "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+ hw->samples,
+ 1 << hw->info.shift
+ );
+ oss_anal_close (&fd);
+ return -1;
+ }
+ }
+
+ oss->fd = fd;
+ return 0;
+}
+
+static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ int trig;
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ ldebug ("enabling voice\n");
+ audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ return -1;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ ldebug ("disabling voice\n");
+ trig = 0;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+ return -1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+ struct oss_params req, obt;
+ int endianness;
+ int err;
+ int fd;
+ audfmt_e effective_fmt;
+ audsettings_t obt_as;
+
+ oss->fd = -1;
+
+ req.fmt = aud_to_ossfmt (as->fmt);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.fragsize = conf.fragsize;
+ req.nfrags = conf.nfrags;
+ if (oss_open (1, &req, &obt, &fd)) {
+ return -1;
+ }
+
+ err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ oss->nfrags = obt.nfrags;
+ oss->fragsize = obt.fragsize;
+
+ if (obt.nfrags * obt.fragsize & hw->info.align) {
+ dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
+ obt.nfrags * obt.fragsize, hw->info.align + 1);
+ }
+
+ hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+ oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!oss->pcm_buf) {
+ dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ oss->fd = fd;
+ return 0;
+}
+
+static void oss_fini_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ oss_anal_close (&oss->fd);
+
+ if (oss->pcm_buf) {
+ qemu_free (oss->pcm_buf);
+ oss->pcm_buf = NULL;
+ }
+}
+
+static int oss_run_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int i;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ size_t read_samples = 0;
+ struct {
+ int add;
+ int len;
+ } bufs[2] = {
+ { hw->wpos, 0 },
+ { 0, 0 }
+ };
+
+ if (!dead) {
+ return 0;
+ }
+
+ if (hw->wpos + dead > hw->samples) {
+ bufs[0].len = (hw->samples - hw->wpos) << hwshift;
+ bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
+ }
+ else {
+ bufs[0].len = dead << hwshift;
+ }
+
+
+ for (i = 0; i < 2; ++i) {
+ ssize_t nread;
+
+ if (bufs[i].len) {
+ void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
+ nread = read (oss->fd, p, bufs[i].len);
+
+ if (nread > 0) {
+ if (nread & hw->info.align) {
+ dolog ("warning: Misaligned read %zd (requested %d), "
+ "alignment %d\n", nread, bufs[i].add << hwshift,
+ hw->info.align + 1);
+ }
+ read_samples += nread >> hwshift;
+ hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
+ &nominal_volume);
+ }
+
+ if (bufs[i].len - nread) {
+ if (nread == -1) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ break;
+ default:
+ oss_logerr (
+ errno,
+ "Failed to read %d bytes of audio (to %p)\n",
+ bufs[i].len, p
+ );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ hw->wpos = (hw->wpos + read_samples) % hw->samples;
+ return read_samples;
+}
+
+static int oss_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static void *oss_audio_init (void)
+{
+ return &conf;
+}
+
+static void oss_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option oss_options[] = {
+ {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
+ "Fragment size in bytes", NULL, 0},
+ {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
+ "Number of fragments", NULL, 0},
+ {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
+ "Try using memory mapped access", NULL, 0},
+ {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
+ "Path to DAC device", NULL, 0},
+ {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
+ "Path to ADC device", NULL, 0},
+ {"DEBUG", AUD_OPT_BOOL, &conf.debug,
+ "Turn on some debugging messages", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops oss_pcm_ops = {
+ oss_init_out,
+ oss_fini_out,
+ oss_run_out,
+ oss_write,
+ oss_ctl_out,
+
+ oss_init_in,
+ oss_fini_in,
+ oss_run_in,
+ oss_read,
+ oss_ctl_in
+};
+
+struct audio_driver oss_audio_driver = {
+ INIT_FIELD (name = ) "oss",
+ INIT_FIELD (descr = ) "OSS http://www.opensound.com",
+ INIT_FIELD (options = ) oss_options,
+ INIT_FIELD (init = ) oss_audio_init,
+ INIT_FIELD (fini = ) oss_audio_fini,
+ INIT_FIELD (pcm_ops = ) &oss_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn)
+};
diff --git a/audio/rate_template.h b/audio/rate_template.h
new file mode 100644
index 0000000..398d305
--- /dev/null
+++ b/audio/rate_template.h
@@ -0,0 +1,111 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+ int *isamp, int *osamp)
+{
+ struct rate *rate = opaque;
+ st_sample_t *istart, *iend;
+ st_sample_t *ostart, *oend;
+ st_sample_t ilast, icur, out;
+#ifdef FLOAT_MIXENG
+ real_t t;
+#else
+ int64_t t;
+#endif
+
+ ilast = rate->ilast;
+
+ istart = ibuf;
+ iend = ibuf + *isamp;
+
+ ostart = obuf;
+ oend = obuf + *osamp;
+
+ if (rate->opos_inc == (1ULL + UINT_MAX)) {
+ int i, n = *isamp > *osamp ? *osamp : *isamp;
+ for (i = 0; i < n; i++) {
+ OP (obuf[i].l, ibuf[i].l);
+ OP (obuf[i].r, ibuf[i].r);
+ }
+ *isamp = n;
+ *osamp = n;
+ return;
+ }
+
+ while (obuf < oend) {
+
+ /* Safety catch to make sure we have input samples. */
+ if (ibuf >= iend) {
+ break;
+ }
+
+ /* read as many input samples so that ipos > opos */
+
+ while (rate->ipos <= (rate->opos >> 32)) {
+ ilast = *ibuf++;
+ rate->ipos++;
+ /* See if we finished the input buffer yet */
+ if (ibuf >= iend) {
+ goto the_end;
+ }
+ }
+
+ icur = *ibuf;
+
+ /* interpolate */
+#ifdef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
+#else
+ t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
+#endif
+ out.l = (ilast.l * (1.0 - t)) + icur.l * t;
+ out.r = (ilast.r * (1.0 - t)) + icur.r * t;
+#else
+ t = rate->opos & 0xffffffff;
+ out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
+ out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
+#endif
+
+ /* output sample & increment position */
+ OP (obuf->l, out.l);
+ OP (obuf->r, out.r);
+ obuf += 1;
+ rate->opos += rate->opos_inc;
+ }
+
+the_end:
+ *isamp = ibuf - istart;
+ *osamp = obuf - ostart;
+ rate->ilast = ilast;
+}
+
+#undef NAME
+#undef OP
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
new file mode 100644
index 0000000..f2a6896
--- /dev/null
+++ b/audio/sdlaudio.c
@@ -0,0 +1,433 @@
+/*
+ * QEMU SDL audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "vl.h"
+
+#define AUDIO_CAP "sdl"
+#include "audio_int.h"
+
+typedef struct SDLVoiceOut {
+ HWVoiceOut hw;
+ int live;
+ int rpos;
+ int decr;
+} SDLVoiceOut;
+
+static struct {
+ int nb_samples;
+} conf = {
+ 1024
+};
+
+struct SDLAudioState {
+ int exit;
+ SDL_mutex *mutex;
+ SDL_sem *sem;
+ int initialized;
+} glob_sdl;
+typedef struct SDLAudioState SDLAudioState;
+
+static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
+}
+
+static int sdl_lock (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_LockMutex (s->mutex)) {
+ sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_unlock (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_UnlockMutex (s->mutex)) {
+ sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_post (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_SemPost (s->sem)) {
+ sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_wait (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_SemWait (s->sem)) {
+ sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
+{
+ if (sdl_unlock (s, forfn)) {
+ return -1;
+ }
+
+ return sdl_post (s, forfn);
+}
+
+static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ *shift = 0;
+ return AUDIO_S8;
+
+ case AUD_FMT_U8:
+ *shift = 0;
+ return AUDIO_U8;
+
+ case AUD_FMT_S16:
+ *shift = 1;
+ return AUDIO_S16LSB;
+
+ case AUD_FMT_U16:
+ *shift = 1;
+ return AUDIO_U16LSB;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return AUDIO_U8;
+ }
+}
+
+static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
+{
+ switch (sdlfmt) {
+ case AUDIO_S8:
+ *endianess = 0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case AUDIO_U8:
+ *endianess = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case AUDIO_S16LSB:
+ *endianess = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AUDIO_U16LSB:
+ *endianess = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case AUDIO_S16MSB:
+ *endianess = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AUDIO_U16MSB:
+ *endianess = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ default:
+ dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
+{
+ int status;
+
+ status = SDL_OpenAudio (req, obt);
+ if (status) {
+ sdl_logerr ("SDL_OpenAudio failed\n");
+ }
+ return status;
+}
+
+static void sdl_close (SDLAudioState *s)
+{
+ if (s->initialized) {
+ sdl_lock (s, "sdl_close");
+ s->exit = 1;
+ sdl_unlock_and_post (s, "sdl_close");
+ SDL_PauseAudio (1);
+ SDL_CloseAudio ();
+ s->initialized = 0;
+ }
+}
+
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+ SDLVoiceOut *sdl = opaque;
+ SDLAudioState *s = &glob_sdl;
+ HWVoiceOut *hw = &sdl->hw;
+ int samples = len >> hw->info.shift;
+
+ if (s->exit) {
+ return;
+ }
+
+ while (samples) {
+ int to_mix, decr;
+
+ /* dolog ("in callback samples=%d\n", samples); */
+ sdl_wait (s, "sdl_callback");
+ if (s->exit) {
+ return;
+ }
+
+ if (sdl_lock (s, "sdl_callback")) {
+ return;
+ }
+
+ if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
+ dolog ("sdl->live=%d hw->samples=%d\n",
+ sdl->live, hw->samples);
+ return;
+ }
+
+ if (!sdl->live) {
+ goto again;
+ }
+
+ /* dolog ("in callback live=%d\n", live); */
+ to_mix = audio_MIN (samples, sdl->live);
+ decr = to_mix;
+ while (to_mix) {
+ int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
+ st_sample_t *src = hw->mix_buf + hw->rpos;
+
+ /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
+ hw->clip (buf, src, chunk);
+ sdl->rpos = (sdl->rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ buf += chunk << hw->info.shift;
+ }
+ samples -= decr;
+ sdl->live -= decr;
+ sdl->decr += decr;
+
+ again:
+ if (sdl_unlock (s, "sdl_callback")) {
+ return;
+ }
+ }
+ /* dolog ("done len=%d\n", len); */
+}
+
+static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int sdl_run_out (HWVoiceOut *hw)
+{
+ int decr, live;
+ SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+ SDLAudioState *s = &glob_sdl;
+
+ if (sdl_lock (s, "sdl_callback")) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+
+ if (sdl->decr > live) {
+ ldebug ("sdl->decr %d live %d sdl->live %d\n",
+ sdl->decr,
+ live,
+ sdl->live);
+ }
+
+ decr = audio_MIN (sdl->decr, live);
+ sdl->decr -= decr;
+
+ sdl->live = live - decr;
+ hw->rpos = sdl->rpos;
+
+ if (sdl->live > 0) {
+ sdl_unlock_and_post (s, "sdl_callback");
+ }
+ else {
+ sdl_unlock (s, "sdl_callback");
+ }
+ return decr;
+}
+
+static void sdl_fini_out (HWVoiceOut *hw)
+{
+ (void) hw;
+
+ sdl_close (&glob_sdl);
+}
+
+static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+ SDLAudioState *s = &glob_sdl;
+ SDL_AudioSpec req, obt;
+ int shift;
+ int endianess;
+ int err;
+ audfmt_e effective_fmt;
+ audsettings_t obt_as;
+
+ shift <<= as->nchannels == 2;
+
+ req.freq = as->freq;
+ req.format = aud_to_sdlfmt (as->fmt, &shift);
+ req.channels = as->nchannels;
+ req.samples = conf.nb_samples;
+ req.callback = sdl_callback;
+ req.userdata = sdl;
+
+ if (sdl_open (&req, &obt)) {
+ return -1;
+ }
+
+ err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
+ if (err) {
+ sdl_close (s);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.channels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianess;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ s->initialized = 1;
+ s->exit = 0;
+ SDL_PauseAudio (0);
+ return 0;
+}
+
+static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ SDL_PauseAudio (0);
+ break;
+
+ case VOICE_DISABLE:
+ SDL_PauseAudio (1);
+ break;
+ }
+ return 0;
+}
+
+static void *sdl_audio_init (void)
+{
+ SDLAudioState *s = &glob_sdl;
+
+ if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
+ sdl_logerr ("SDL failed to initialize audio subsystem\n");
+ return NULL;
+ }
+
+ s->mutex = SDL_CreateMutex ();
+ if (!s->mutex) {
+ sdl_logerr ("Failed to create SDL mutex\n");
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return NULL;
+ }
+
+ s->sem = SDL_CreateSemaphore (0);
+ if (!s->sem) {
+ sdl_logerr ("Failed to create SDL semaphore\n");
+ SDL_DestroyMutex (s->mutex);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return NULL;
+ }
+
+ return s;
+}
+
+static void sdl_audio_fini (void *opaque)
+{
+ SDLAudioState *s = opaque;
+ sdl_close (s);
+ SDL_DestroySemaphore (s->sem);
+ SDL_DestroyMutex (s->mutex);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+static struct audio_option sdl_options[] = {
+ {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+ "Size of SDL buffer in samples", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops sdl_pcm_ops = {
+ sdl_init_out,
+ sdl_fini_out,
+ sdl_run_out,
+ sdl_write_out,
+ sdl_ctl_out,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct audio_driver sdl_audio_driver = {
+ INIT_FIELD (name = ) "sdl",
+ INIT_FIELD (descr = ) "SDL http://www.libsdl.org",
+ INIT_FIELD (options = ) sdl_options,
+ INIT_FIELD (init = ) sdl_audio_init,
+ INIT_FIELD (fini = ) sdl_audio_fini,
+ INIT_FIELD (pcm_ops = ) &sdl_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 0,
+ INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
+ INIT_FIELD (voice_size_in = ) 0
+};
diff --git a/audio/sys-queue.h b/audio/sys-queue.h
new file mode 100644
index 0000000..5b6e2a0
--- /dev/null
+++ b/audio/sys-queue.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef _SYS_QUEUE_H
+#define _SYS_QUEUE_H 1
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) { \
+ (head)->lh_first = NULL; \
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+}
+
+#define LIST_REMOVE(elm, field) { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+}
+
+#define TAILQ_REMOVE(head, elm, field) { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+}
+
+#define CIRCLEQ_REMOVE(head, elm, field) { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+}
+#endif /* sys/queue.h */
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
new file mode 100644
index 0000000..c359fc4
--- /dev/null
+++ b/audio/wavaudio.c
@@ -0,0 +1,255 @@
+/*
+ * QEMU WAV audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define AUDIO_CAP "wav"
+#include "audio_int.h"
+
+typedef struct WAVVoiceOut {
+ HWVoiceOut hw;
+ QEMUFile *f;
+ int64_t old_ticks;
+ void *pcm_buf;
+ int total_samples;
+} WAVVoiceOut;
+
+static struct {
+ audsettings_t settings;
+ const char *wav_path;
+} conf = {
+ {
+ 44100,
+ 2,
+ AUD_FMT_S16
+ },
+ "qemu.wav"
+};
+
+static int wav_run_out (HWVoiceOut *hw)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ int rpos, live, decr, samples;
+ uint8_t *dst;
+ st_sample_t *src;
+ int64_t now = qemu_get_clock (vm_clock);
+ int64_t ticks = now - wav->old_ticks;
+ int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+ if (bytes > INT_MAX) {
+ samples = INT_MAX >> hw->info.shift;
+ }
+ else {
+ samples = bytes >> hw->info.shift;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+ if (!live) {
+ return 0;
+ }
+
+ wav->old_ticks = now;
+ decr = audio_MIN (live, samples);
+ samples = decr;
+ rpos = hw->rpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
+ int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+ src = hw->mix_buf + rpos;
+ dst = advance (wav->pcm_buf, rpos << hw->info.shift);
+
+ hw->clip (dst, src, convert_samples);
+ qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
+
+ rpos = (rpos + convert_samples) % hw->samples;
+ samples -= convert_samples;
+ wav->total_samples += convert_samples;
+ }
+
+ hw->rpos = rpos;
+ return decr;
+}
+
+static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t) (val & 0xff);
+ val >>= 8;
+ }
+}
+
+static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ int bits16 = 0, stereo = 0;
+ uint8_t hdr[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+ 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+ };
+ audsettings_t wav_as = conf.settings;
+
+ (void) as;
+
+ stereo = wav_as.nchannels == 2;
+ switch (wav_as.fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ bits16 = 0;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ bits16 = 1;
+ break;
+ }
+
+ hdr[34] = bits16 ? 0x10 : 0x08;
+
+ wav_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &wav_as);
+
+ hw->samples = 1024;
+ wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!wav->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ le_store (hdr + 22, hw->info.nchannels, 2);
+ le_store (hdr + 24, hw->info.freq, 4);
+ le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
+ le_store (hdr + 32, 1 << (bits16 + stereo), 2);
+
+ wav->f = fopen (conf.wav_path, "wb");
+ if (!wav->f) {
+ dolog ("Failed to open wave file `%s'\nReason: %s\n",
+ conf.wav_path, strerror (errno));
+ qemu_free (wav->pcm_buf);
+ wav->pcm_buf = NULL;
+ return -1;
+ }
+
+ qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+ return 0;
+}
+
+static void wav_fini_out (HWVoiceOut *hw)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ uint8_t rlen[4];
+ uint8_t dlen[4];
+ uint32_t datalen = wav->total_samples << hw->info.shift;
+ uint32_t rifflen = datalen + 36;
+
+ if (!wav->f) {
+ return;
+ }
+
+ le_store (rlen, rifflen, 4);
+ le_store (dlen, datalen, 4);
+
+ qemu_fseek (wav->f, 4, SEEK_SET);
+ qemu_put_buffer (wav->f, rlen, 4);
+
+ qemu_fseek (wav->f, 32, SEEK_CUR);
+ qemu_put_buffer (wav->f, dlen, 4);
+
+ fclose (wav->f);
+ wav->f = NULL;
+
+ qemu_free (wav->pcm_buf);
+ wav->pcm_buf = NULL;
+}
+
+static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static void *wav_audio_init (void)
+{
+ return &conf;
+}
+
+static void wav_audio_fini (void *opaque)
+{
+ (void) opaque;
+ ldebug ("wav_fini");
+}
+
+struct audio_option wav_options[] = {
+ {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
+ "Frequency", NULL, 0},
+
+ {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
+ "Format", NULL, 0},
+
+ {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
+ "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
+
+ {"PATH", AUD_OPT_STR, &conf.wav_path,
+ "Path to wave file", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+struct audio_pcm_ops wav_pcm_ops = {
+ wav_init_out,
+ wav_fini_out,
+ wav_run_out,
+ wav_write_out,
+ wav_ctl_out,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct audio_driver wav_audio_driver = {
+ INIT_FIELD (name = ) "wav",
+ INIT_FIELD (descr = )
+ "WAV renderer http://wikipedia.org/wiki/WAV",
+ INIT_FIELD (options = ) wav_options,
+ INIT_FIELD (init = ) wav_audio_init,
+ INIT_FIELD (fini = ) wav_audio_fini,
+ INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
+ INIT_FIELD (can_be_default = ) 0,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 0,
+ INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
+ INIT_FIELD (voice_size_in = ) 0
+};
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
new file mode 100644
index 0000000..0f6f7bf
--- /dev/null
+++ b/audio/wavcapture.c
@@ -0,0 +1,164 @@
+#include "vl.h"
+
+typedef struct {
+ QEMUFile *f;
+ int bytes;
+ char *path;
+ int freq;
+ int bits;
+ int nchannels;
+ CaptureVoiceOut *cap;
+} WAVState;
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t) (val & 0xff);
+ val >>= 8;
+ }
+}
+
+static void wav_notify (void *opaque, audcnotification_e cmd)
+{
+ (void) opaque;
+ (void) cmd;
+}
+
+static void wav_destroy (void *opaque)
+{
+ WAVState *wav = opaque;
+ uint8_t rlen[4];
+ uint8_t dlen[4];
+ uint32_t datalen = wav->bytes;
+ uint32_t rifflen = datalen + 36;
+
+ if (!wav->f) {
+ return;
+ }
+
+ le_store (rlen, rifflen, 4);
+ le_store (dlen, datalen, 4);
+
+ qemu_fseek (wav->f, 4, SEEK_SET);
+ qemu_put_buffer (wav->f, rlen, 4);
+
+ qemu_fseek (wav->f, 32, SEEK_CUR);
+ qemu_put_buffer (wav->f, dlen, 4);
+ fclose (wav->f);
+ if (wav->path) {
+ qemu_free (wav->path);
+ }
+}
+
+static void wav_capture (void *opaque, void *buf, int size)
+{
+ WAVState *wav = opaque;
+
+ qemu_put_buffer (wav->f, buf, size);
+ wav->bytes += size;
+}
+
+static void wav_capture_destroy (void *opaque)
+{
+ WAVState *wav = opaque;
+
+ AUD_del_capture (wav->cap, wav);
+}
+
+static void wav_capture_info (void *opaque)
+{
+ WAVState *wav = opaque;
+ char *path = wav->path;
+
+ term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
+ wav->freq, wav->bits, wav->nchannels,
+ path ? path : "<not available>", wav->bytes);
+}
+
+static struct capture_ops wav_capture_ops = {
+ .destroy = wav_capture_destroy,
+ .info = wav_capture_info
+};
+
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+ int bits, int nchannels)
+{
+ WAVState *wav;
+ uint8_t hdr[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+ 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+ };
+ audsettings_t as;
+ struct audio_capture_ops ops;
+ int stereo, bits16, shift;
+ CaptureVoiceOut *cap;
+
+ if (bits != 8 && bits != 16) {
+ term_printf ("incorrect bit count %d, must be 8 or 16\n", bits);
+ return -1;
+ }
+
+ if (nchannels != 1 && nchannels != 2) {
+ term_printf ("incorrect channel count %d, must be 1 or 2\n",
+ nchannels);
+ return -1;
+ }
+
+ stereo = nchannels == 2;
+ bits16 = bits == 16;
+
+ as.freq = freq;
+ as.nchannels = 1 << stereo;
+ as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.endianness = 0;
+
+ ops.notify = wav_notify;
+ ops.capture = wav_capture;
+ ops.destroy = wav_destroy;
+
+ wav = qemu_mallocz (sizeof (*wav));
+ if (!wav) {
+ term_printf ("Could not allocate memory for wav capture (%zu bytes)",
+ sizeof (*wav));
+ return -1;
+ }
+
+ shift = bits16 + stereo;
+ hdr[34] = bits16 ? 0x10 : 0x08;
+
+ le_store (hdr + 22, as.nchannels, 2);
+ le_store (hdr + 24, freq, 4);
+ le_store (hdr + 28, freq << shift, 4);
+ le_store (hdr + 32, 1 << shift, 2);
+
+ wav->f = fopen (path, "wb");
+ if (!wav->f) {
+ term_printf ("Failed to open wave file `%s'\nReason: %s\n",
+ path, strerror (errno));
+ qemu_free (wav);
+ return -1;
+ }
+
+ wav->path = qemu_strdup (path);
+ wav->bits = bits;
+ wav->nchannels = nchannels;
+ wav->freq = freq;
+
+ qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+
+ cap = AUD_add_capture (NULL, &as, &ops, wav);
+ if (!cap) {
+ term_printf ("Failed to add audio capture\n");
+ qemu_free (wav);
+ return -1;
+ }
+
+ wav->cap = cap;
+ s->opaque = wav;
+ s->ops = wav_capture_ops;
+ return 0;
+}
diff --git a/block-bochs.c b/block-bochs.c
new file mode 100644
index 0000000..62317af
--- /dev/null
+++ b/block-bochs.c
@@ -0,0 +1,224 @@
+/*
+ * Block driver for the various disk image formats used by Bochs
+ * Currently only for "growing" type in read-only mode
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+
+/**************************************************************/
+
+#define HEADER_MAGIC "Bochs Virtual HD Image"
+#define HEADER_VERSION 0x00010000
+#define HEADER_SIZE 512
+
+#define REDOLOG_TYPE "Redolog"
+#define GROWING_TYPE "Growing"
+
+// not allocated: 0xffffffff
+
+// always little-endian
+struct bochs_header {
+ char magic[32]; // "Bochs Virtual HD Image"
+ char type[16]; // "Redolog"
+ char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+ uint32_t version;
+ uint32_t header; // size of header
+
+ union {
+ struct {
+ uint32_t catalog; // num of entries
+ uint32_t bitmap; // bitmap size
+ uint32_t extent; // extent size
+ uint64_t disk; // disk size
+ char padding[HEADER_SIZE - 64 - 8 - 20];
+ } redolog;
+ char padding[HEADER_SIZE - 64 - 8];
+ } extra;
+};
+
+typedef struct BDRVBochsState {
+ int fd;
+
+ uint32_t *catalog_bitmap;
+ int catalog_size;
+
+ int data_offset;
+
+ int bitmap_blocks;
+ int extent_blocks;
+ int extent_size;
+} BDRVBochsState;
+
+static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const struct bochs_header *bochs = (const void *)buf;
+
+ if (buf_size < HEADER_SIZE)
+ return 0;
+
+ if (!strcmp(bochs->magic, HEADER_MAGIC) &&
+ !strcmp(bochs->type, REDOLOG_TYPE) &&
+ !strcmp(bochs->subtype, GROWING_TYPE) &&
+ (le32_to_cpu(bochs->version) == HEADER_VERSION))
+ return 100;
+
+ return 0;
+}
+
+static int bochs_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVBochsState *s = bs->opaque;
+ int fd, i;
+ struct bochs_header bochs;
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ }
+
+ bs->read_only = 1; // no write support yet
+
+ s->fd = fd;
+
+ if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
+ goto fail;
+ }
+
+ if (strcmp(bochs.magic, HEADER_MAGIC) ||
+ strcmp(bochs.type, REDOLOG_TYPE) ||
+ strcmp(bochs.subtype, GROWING_TYPE) ||
+ (le32_to_cpu(bochs.version) != HEADER_VERSION)) {
+ goto fail;
+ }
+
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
+
+ lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
+
+ s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
+ s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
+ if (!s->catalog_bitmap)
+ goto fail;
+ if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
+ s->catalog_size * 4)
+ goto fail;
+ for (i = 0; i < s->catalog_size; i++)
+ le32_to_cpus(&s->catalog_bitmap[i]);
+
+ s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
+
+ s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
+ s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
+
+ s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
+
+ return 0;
+ fail:
+ close(fd);
+ return -1;
+}
+
+static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+ BDRVBochsState *s = bs->opaque;
+ int64_t offset = sector_num * 512;
+ int64_t extent_index, extent_offset, bitmap_offset, block_offset;
+ char bitmap_entry;
+
+ // seek to sector
+ extent_index = offset / s->extent_size;
+ extent_offset = (offset % s->extent_size) / 512;
+
+ if (s->catalog_bitmap[extent_index] == 0xffffffff)
+ {
+// fprintf(stderr, "page not allocated [%x - %x:%x]\n",
+// sector_num, extent_index, extent_offset);
+ return -1; // not allocated
+ }
+
+ bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
+ (s->extent_blocks + s->bitmap_blocks));
+ block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
+
+// fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n",
+// sector_num, extent_index, extent_offset,
+// le32_to_cpu(s->catalog_bitmap[extent_index]),
+// bitmap_offset, block_offset);
+
+ // read in bitmap for current extent
+ lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
+
+ read(s->fd, &bitmap_entry, 1);
+
+ if (!((bitmap_entry >> (extent_offset % 8)) & 1))
+ {
+// fprintf(stderr, "sector (%x) in bitmap not allocated\n",
+// sector_num);
+ return -1; // not allocated
+ }
+
+ lseek(s->fd, block_offset, SEEK_SET);
+
+ return 0;
+}
+
+static int bochs_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVBochsState *s = bs->opaque;
+ int ret;
+
+ while (nb_sectors > 0) {
+ if (!seek_to_sector(bs, sector_num))
+ {
+ ret = read(s->fd, buf, 512);
+ if (ret != 512)
+ return -1;
+ }
+ else
+ memset(buf, 0, 512);
+ nb_sectors--;
+ sector_num++;
+ buf += 512;
+ }
+ return 0;
+}
+
+static void bochs_close(BlockDriverState *bs)
+{
+ BDRVBochsState *s = bs->opaque;
+ qemu_free(s->catalog_bitmap);
+ close(s->fd);
+}
+
+BlockDriver bdrv_bochs = {
+ "bochs",
+ sizeof(BDRVBochsState),
+ bochs_probe,
+ bochs_open,
+ bochs_read,
+ NULL,
+ bochs_close,
+};
diff --git a/block-cloop.c b/block-cloop.c
new file mode 100644
index 0000000..c617e1b
--- /dev/null
+++ b/block-cloop.c
@@ -0,0 +1,169 @@
+/*
+ * QEMU Block driver for CLOOP images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+#include <zlib.h>
+
+typedef struct BDRVCloopState {
+ int fd;
+ uint32_t block_size;
+ uint32_t n_blocks;
+ uint64_t* offsets;
+ uint32_t sectors_per_block;
+ uint32_t current_block;
+ uint8_t *compressed_block;
+ uint8_t *uncompressed_block;
+ z_stream zstream;
+} BDRVCloopState;
+
+static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const char* magic_version_2_0="#!/bin/sh\n"
+ "#V2.0 Format\n"
+ "modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
+ int length=strlen(magic_version_2_0);
+ if(length>buf_size)
+ length=buf_size;
+ if(!memcmp(magic_version_2_0,buf,length))
+ return 2;
+ return 0;
+}
+
+static int cloop_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVCloopState *s = bs->opaque;
+ uint32_t offsets_size,max_compressed_block_size=1,i;
+
+ s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (s->fd < 0)
+ return -1;
+ bs->read_only = 1;
+
+ /* read header */
+ if(lseek(s->fd,128,SEEK_SET)<0) {
+cloop_close:
+ close(s->fd);
+ return -1;
+ }
+ if(read(s->fd,&s->block_size,4)<4)
+ goto cloop_close;
+ s->block_size=be32_to_cpu(s->block_size);
+ if(read(s->fd,&s->n_blocks,4)<4)
+ goto cloop_close;
+ s->n_blocks=be32_to_cpu(s->n_blocks);
+
+ /* read offsets */
+ offsets_size=s->n_blocks*sizeof(uint64_t);
+ if(!(s->offsets=(uint64_t*)malloc(offsets_size)))
+ goto cloop_close;
+ if(read(s->fd,s->offsets,offsets_size)<offsets_size)
+ goto cloop_close;
+ for(i=0;i<s->n_blocks;i++) {
+ s->offsets[i]=be64_to_cpu(s->offsets[i]);
+ if(i>0) {
+ uint32_t size=s->offsets[i]-s->offsets[i-1];
+ if(size>max_compressed_block_size)
+ max_compressed_block_size=size;
+ }
+ }
+
+ /* initialize zlib engine */
+ if(!(s->compressed_block = malloc(max_compressed_block_size+1)))
+ goto cloop_close;
+ if(!(s->uncompressed_block = malloc(s->block_size)))
+ goto cloop_close;
+ if(inflateInit(&s->zstream) != Z_OK)
+ goto cloop_close;
+ s->current_block=s->n_blocks;
+
+ s->sectors_per_block = s->block_size/512;
+ bs->total_sectors = s->n_blocks*s->sectors_per_block;
+ return 0;
+}
+
+static inline int cloop_read_block(BDRVCloopState *s,int block_num)
+{
+ if(s->current_block != block_num) {
+ int ret;
+ uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
+
+ lseek(s->fd, s->offsets[block_num], SEEK_SET);
+ ret = read(s->fd, s->compressed_block, bytes);
+ if (ret != bytes)
+ return -1;
+
+ s->zstream.next_in = s->compressed_block;
+ s->zstream.avail_in = bytes;
+ s->zstream.next_out = s->uncompressed_block;
+ s->zstream.avail_out = s->block_size;
+ ret = inflateReset(&s->zstream);
+ if(ret != Z_OK)
+ return -1;
+ ret = inflate(&s->zstream, Z_FINISH);
+ if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
+ return -1;
+
+ s->current_block = block_num;
+ }
+ return 0;
+}
+
+static int cloop_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVCloopState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++) {
+ uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
+ block_num=(sector_num+i)/s->sectors_per_block;
+ if(cloop_read_block(s, block_num) != 0)
+ return -1;
+ memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
+ }
+ return 0;
+}
+
+static void cloop_close(BlockDriverState *bs)
+{
+ BDRVCloopState *s = bs->opaque;
+ close(s->fd);
+ if(s->n_blocks>0)
+ free(s->offsets);
+ free(s->compressed_block);
+ free(s->uncompressed_block);
+ inflateEnd(&s->zstream);
+}
+
+BlockDriver bdrv_cloop = {
+ "cloop",
+ sizeof(BDRVCloopState),
+ cloop_probe,
+ cloop_open,
+ cloop_read,
+ NULL,
+ cloop_close,
+};
+
+
diff --git a/block-cow.c b/block-cow.c
new file mode 100644
index 0000000..6af8b74
--- /dev/null
+++ b/block-cow.c
@@ -0,0 +1,271 @@
+/*
+ * Block driver for the COW format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _WIN32
+#include "vl.h"
+#include "block_int.h"
+#include <sys/mman.h>
+
+/**************************************************************/
+/* COW block driver using file system holes */
+
+/* user mode linux compatible COW file */
+#define COW_MAGIC 0x4f4f4f4d /* MOOO */
+#define COW_VERSION 2
+
+struct cow_header_v2 {
+ uint32_t magic;
+ uint32_t version;
+ char backing_file[1024];
+ int32_t mtime;
+ uint64_t size;
+ uint32_t sectorsize;
+};
+
+typedef struct BDRVCowState {
+ int fd;
+ uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
+ uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
+ int cow_bitmap_size;
+ int64_t cow_sectors_offset;
+} BDRVCowState;
+
+static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const struct cow_header_v2 *cow_header = (const void *)buf;
+
+ if (buf_size >= sizeof(struct cow_header_v2) &&
+ be32_to_cpu(cow_header->magic) == COW_MAGIC &&
+ be32_to_cpu(cow_header->version) == COW_VERSION)
+ return 100;
+ else
+ return 0;
+}
+
+static int cow_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVCowState *s = bs->opaque;
+ int fd;
+ struct cow_header_v2 cow_header;
+ int64_t size;
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ }
+ s->fd = fd;
+ /* see if it is a cow image */
+ if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
+ goto fail;
+ }
+
+ if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
+ be32_to_cpu(cow_header.version) != COW_VERSION) {
+ goto fail;
+ }
+
+ /* cow image found */
+ size = be64_to_cpu(cow_header.size);
+ bs->total_sectors = size / 512;
+
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ cow_header.backing_file);
+
+#if 0
+ if (cow_header.backing_file[0] != '\0') {
+ if (stat(cow_header.backing_file, &st) != 0) {
+ fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
+ goto fail;
+ }
+ if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
+ fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
+ goto fail;
+ }
+ fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
+ if (fd < 0)
+ goto fail;
+ bs->fd = fd;
+ }
+#endif
+ /* mmap the bitmap */
+ s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
+ s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size),
+ s->cow_bitmap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, s->fd, 0);
+ if (s->cow_bitmap_addr == MAP_FAILED)
+ goto fail;
+ s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header);
+ s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511;
+ return 0;
+ fail:
+ close(fd);
+ return -1;
+}
+
+static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum)
+{
+ bitmap[bitnum / 8] |= (1 << (bitnum%8));
+}
+
+static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
+{
+ return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
+}
+
+
+/* Return true if first block has been changed (ie. current version is
+ * in COW file). Set the number of continuous blocks for which that
+ * is true. */
+static inline int is_changed(uint8_t *bitmap,
+ int64_t sector_num, int nb_sectors,
+ int *num_same)
+{
+ int changed;
+
+ if (!bitmap || nb_sectors == 0) {
+ *num_same = nb_sectors;
+ return 0;
+ }
+
+ changed = is_bit_set(bitmap, sector_num);
+ for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
+ if (is_bit_set(bitmap, sector_num + *num_same) != changed)
+ break;
+ }
+
+ return changed;
+}
+
+static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVCowState *s = bs->opaque;
+ return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum);
+}
+
+static int cow_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVCowState *s = bs->opaque;
+ int ret, n;
+
+ while (nb_sectors > 0) {
+ if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
+ lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
+ ret = read(s->fd, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ } else {
+ memset(buf, 0, n * 512);
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int cow_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVCowState *s = bs->opaque;
+ int ret, i;
+
+ lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
+ ret = write(s->fd, buf, nb_sectors * 512);
+ if (ret != nb_sectors * 512)
+ return -1;
+ for (i = 0; i < nb_sectors; i++)
+ cow_set_bit(s->cow_bitmap, sector_num + i);
+ return 0;
+}
+
+static void cow_close(BlockDriverState *bs)
+{
+ BDRVCowState *s = bs->opaque;
+ munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
+ close(s->fd);
+}
+
+static int cow_create(const char *filename, int64_t image_sectors,
+ const char *image_filename, int flags)
+{
+ int fd, cow_fd;
+ struct cow_header_v2 cow_header;
+ struct stat st;
+
+ if (flags)
+ return -ENOTSUP;
+
+ cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (cow_fd < 0)
+ return -1;
+ memset(&cow_header, 0, sizeof(cow_header));
+ cow_header.magic = cpu_to_be32(COW_MAGIC);
+ cow_header.version = cpu_to_be32(COW_VERSION);
+ if (image_filename) {
+ fd = open(image_filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ close(cow_fd);
+ return -1;
+ }
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ cow_header.mtime = cpu_to_be32(st.st_mtime);
+ realpath(image_filename, cow_header.backing_file);
+ }
+ cow_header.sectorsize = cpu_to_be32(512);
+ cow_header.size = cpu_to_be64(image_sectors * 512);
+ write(cow_fd, &cow_header, sizeof(cow_header));
+ /* resize to include at least all the bitmap */
+ ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
+ close(cow_fd);
+ return 0;
+}
+
+static void cow_flush(BlockDriverState *bs)
+{
+ BDRVCowState *s = bs->opaque;
+ fsync(s->fd);
+}
+
+BlockDriver bdrv_cow = {
+ "cow",
+ sizeof(BDRVCowState),
+ cow_probe,
+ cow_open,
+ cow_read,
+ cow_write,
+ cow_close,
+ cow_create,
+ cow_flush,
+ cow_is_allocated,
+};
+#endif
diff --git a/block-dmg.c b/block-dmg.c
new file mode 100644
index 0000000..a16ab92
--- /dev/null
+++ b/block-dmg.c
@@ -0,0 +1,297 @@
+/*
+ * QEMU Block driver for DMG images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+#include "bswap.h"
+#include <zlib.h>
+
+typedef struct BDRVDMGState {
+ int fd;
+
+ /* each chunk contains a certain number of sectors,
+ * offsets[i] is the offset in the .dmg file,
+ * lengths[i] is the length of the compressed chunk,
+ * sectors[i] is the sector beginning at offsets[i],
+ * sectorcounts[i] is the number of sectors in that chunk,
+ * the sectors array is ordered
+ * 0<=i<n_chunks */
+
+ uint32_t n_chunks;
+ uint32_t* types;
+ uint64_t* offsets;
+ uint64_t* lengths;
+ uint64_t* sectors;
+ uint64_t* sectorcounts;
+ uint32_t current_chunk;
+ uint8_t *compressed_chunk;
+ uint8_t *uncompressed_chunk;
+ z_stream zstream;
+} BDRVDMGState;
+
+static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ int len=strlen(filename);
+ if(len>4 && !strcmp(filename+len-4,".dmg"))
+ return 2;
+ return 0;
+}
+
+static off_t read_off(int fd)
+{
+ uint64_t buffer;
+ if(read(fd,&buffer,8)<8)
+ return 0;
+ return be64_to_cpu(buffer);
+}
+
+static off_t read_uint32(int fd)
+{
+ uint32_t buffer;
+ if(read(fd,&buffer,4)<4)
+ return 0;
+ return be32_to_cpu(buffer);
+}
+
+static int dmg_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVDMGState *s = bs->opaque;
+ off_t info_begin,info_end,last_in_offset,last_out_offset;
+ uint32_t count;
+ uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
+
+ s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (s->fd < 0)
+ return -1;
+ bs->read_only = 1;
+ s->n_chunks = 0;
+ s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
+
+ /* read offset of info blocks */
+ if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
+dmg_close:
+ close(s->fd);
+ /* open raw instead */
+ bs->drv=&bdrv_raw;
+ return bs->drv->bdrv_open(bs,filename);
+ }
+ info_begin=read_off(s->fd);
+ if(info_begin==0)
+ goto dmg_close;
+ if(lseek(s->fd,info_begin,SEEK_SET)<0)
+ goto dmg_close;
+ if(read_uint32(s->fd)!=0x100)
+ goto dmg_close;
+ if((count = read_uint32(s->fd))==0)
+ goto dmg_close;
+ info_end = info_begin+count;
+ if(lseek(s->fd,0xf8,SEEK_CUR)<0)
+ goto dmg_close;
+
+ /* read offsets */
+ last_in_offset = last_out_offset = 0;
+ while(lseek(s->fd,0,SEEK_CUR)<info_end) {
+ uint32_t type;
+
+ count = read_uint32(s->fd);
+ if(count==0)
+ goto dmg_close;
+ type = read_uint32(s->fd);
+ if(type!=0x6d697368 || count<244)
+ lseek(s->fd,count-4,SEEK_CUR);
+ else {
+ int new_size, chunk_count;
+ if(lseek(s->fd,200,SEEK_CUR)<0)
+ goto dmg_close;
+ chunk_count = (count-204)/40;
+ new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
+ s->types = realloc(s->types, new_size/2);
+ s->offsets = realloc(s->offsets, new_size);
+ s->lengths = realloc(s->lengths, new_size);
+ s->sectors = realloc(s->sectors, new_size);
+ s->sectorcounts = realloc(s->sectorcounts, new_size);
+
+ for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
+ s->types[i] = read_uint32(s->fd);
+ if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
+ if(s->types[i]==0xffffffff) {
+ last_in_offset = s->offsets[i-1]+s->lengths[i-1];
+ last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
+ }
+ chunk_count--;
+ i--;
+ if(lseek(s->fd,36,SEEK_CUR)<0)
+ goto dmg_close;
+ continue;
+ }
+ read_uint32(s->fd);
+ s->sectors[i] = last_out_offset+read_off(s->fd);
+ s->sectorcounts[i] = read_off(s->fd);
+ s->offsets[i] = last_in_offset+read_off(s->fd);
+ s->lengths[i] = read_off(s->fd);
+ if(s->lengths[i]>max_compressed_size)
+ max_compressed_size = s->lengths[i];
+ if(s->sectorcounts[i]>max_sectors_per_chunk)
+ max_sectors_per_chunk = s->sectorcounts[i];
+ }
+ s->n_chunks+=chunk_count;
+ }
+ }
+
+ /* initialize zlib engine */
+ if(!(s->compressed_chunk = malloc(max_compressed_size+1)))
+ goto dmg_close;
+ if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk)))
+ goto dmg_close;
+ if(inflateInit(&s->zstream) != Z_OK)
+ goto dmg_close;
+
+ s->current_chunk = s->n_chunks;
+
+ return 0;
+}
+
+static inline int is_sector_in_chunk(BDRVDMGState* s,
+ uint32_t chunk_num,int sector_num)
+{
+ if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
+ s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
+ return 0;
+ else
+ return -1;
+}
+
+static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
+{
+ /* binary search */
+ uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
+ while(chunk1!=chunk2) {
+ chunk3 = (chunk1+chunk2)/2;
+ if(s->sectors[chunk3]>sector_num)
+ chunk2 = chunk3;
+ else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
+ return chunk3;
+ else
+ chunk1 = chunk3;
+ }
+ return s->n_chunks; /* error */
+}
+
+static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
+{
+ if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
+ int ret;
+ uint32_t chunk = search_chunk(s,sector_num);
+
+ if(chunk>=s->n_chunks)
+ return -1;
+
+ s->current_chunk = s->n_chunks;
+ switch(s->types[chunk]) {
+ case 0x80000005: { /* zlib compressed */
+ int i;
+
+ ret = lseek(s->fd, s->offsets[chunk], SEEK_SET);
+ if(ret<0)
+ return -1;
+
+ /* we need to buffer, because only the chunk as whole can be
+ * inflated. */
+ i=0;
+ do {
+ ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i);
+ if(ret<0 && errno==EINTR)
+ ret=0;
+ i+=ret;
+ } while(ret>=0 && ret+i<s->lengths[chunk]);
+
+ if (ret != s->lengths[chunk])
+ return -1;
+
+ s->zstream.next_in = s->compressed_chunk;
+ s->zstream.avail_in = s->lengths[chunk];
+ s->zstream.next_out = s->uncompressed_chunk;
+ s->zstream.avail_out = 512*s->sectorcounts[chunk];
+ ret = inflateReset(&s->zstream);
+ if(ret != Z_OK)
+ return -1;
+ ret = inflate(&s->zstream, Z_FINISH);
+ if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
+ return -1;
+ break; }
+ case 1: /* copy */
+ ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]);
+ if (ret != s->lengths[chunk])
+ return -1;
+ break;
+ case 2: /* zero */
+ memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
+ break;
+ }
+ s->current_chunk = chunk;
+ }
+ return 0;
+}
+
+static int dmg_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVDMGState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++) {
+ uint32_t sector_offset_in_chunk;
+ if(dmg_read_chunk(s, sector_num+i) != 0)
+ return -1;
+ sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
+ memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
+ }
+ return 0;
+}
+
+static void dmg_close(BlockDriverState *bs)
+{
+ BDRVDMGState *s = bs->opaque;
+ close(s->fd);
+ if(s->n_chunks>0) {
+ free(s->types);
+ free(s->offsets);
+ free(s->lengths);
+ free(s->sectors);
+ free(s->sectorcounts);
+ }
+ free(s->compressed_chunk);
+ free(s->uncompressed_chunk);
+ inflateEnd(&s->zstream);
+}
+
+BlockDriver bdrv_dmg = {
+ "dmg",
+ sizeof(BDRVDMGState),
+ dmg_probe,
+ dmg_open,
+ dmg_read,
+ NULL,
+ dmg_close,
+};
+
diff --git a/block-qcow.c b/block-qcow.c
new file mode 100644
index 0000000..e5b52fb
--- /dev/null
+++ b/block-qcow.c
@@ -0,0 +1,717 @@
+/*
+ * Block driver for the QCOW format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+#include <zlib.h>
+#include "aes.h"
+
+/**************************************************************/
+/* QEMU COW block driver with compression and encryption support */
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 1
+
+#define QCOW_CRYPT_NONE 0
+#define QCOW_CRYPT_AES 1
+
+#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+
+typedef struct QCowHeader {
+ uint32_t magic;
+ uint32_t version;
+ uint64_t backing_file_offset;
+ uint32_t backing_file_size;
+ uint32_t mtime;
+ uint64_t size; /* in bytes */
+ uint8_t cluster_bits;
+ uint8_t l2_bits;
+ uint32_t crypt_method;
+ uint64_t l1_table_offset;
+} QCowHeader;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVQcowState {
+ int fd;
+ int cluster_bits;
+ int cluster_size;
+ int cluster_sectors;
+ int l2_bits;
+ int l2_size;
+ int l1_size;
+ uint64_t cluster_offset_mask;
+ uint64_t l1_table_offset;
+ uint64_t *l1_table;
+ uint64_t *l2_cache;
+ uint64_t l2_cache_offsets[L2_CACHE_SIZE];
+ uint32_t l2_cache_counts[L2_CACHE_SIZE];
+ uint8_t *cluster_cache;
+ uint8_t *cluster_data;
+ uint64_t cluster_cache_offset;
+ uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+ uint32_t crypt_method_header;
+ AES_KEY aes_encrypt_key;
+ AES_KEY aes_decrypt_key;
+} BDRVQcowState;
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
+
+static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const QCowHeader *cow_header = (const void *)buf;
+
+ if (buf_size >= sizeof(QCowHeader) &&
+ be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
+ be32_to_cpu(cow_header->version) == QCOW_VERSION)
+ return 100;
+ else
+ return 0;
+}
+
+static int qcow_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVQcowState *s = bs->opaque;
+ int fd, len, i, shift;
+ QCowHeader header;
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ }
+ s->fd = fd;
+ if (read(fd, &header, sizeof(header)) != sizeof(header))
+ goto fail;
+ be32_to_cpus(&header.magic);
+ be32_to_cpus(&header.version);
+ be64_to_cpus(&header.backing_file_offset);
+ be32_to_cpus(&header.backing_file_size);
+ be32_to_cpus(&header.mtime);
+ be64_to_cpus(&header.size);
+ be32_to_cpus(&header.crypt_method);
+ be64_to_cpus(&header.l1_table_offset);
+
+ if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
+ goto fail;
+ if (header.size <= 1 || header.cluster_bits < 9)
+ goto fail;
+ if (header.crypt_method > QCOW_CRYPT_AES)
+ goto fail;
+ s->crypt_method_header = header.crypt_method;
+ if (s->crypt_method_header)
+ bs->encrypted = 1;
+ s->cluster_bits = header.cluster_bits;
+ s->cluster_size = 1 << s->cluster_bits;
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
+ s->l2_bits = header.l2_bits;
+ s->l2_size = 1 << s->l2_bits;
+ bs->total_sectors = header.size / 512;
+ s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;
+
+ /* read the level 1 table */
+ shift = s->cluster_bits + s->l2_bits;
+ s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
+
+ s->l1_table_offset = header.l1_table_offset;
+ s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+ if (!s->l1_table)
+ goto fail;
+ lseek(fd, s->l1_table_offset, SEEK_SET);
+ if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
+ s->l1_size * sizeof(uint64_t))
+ goto fail;
+ for(i = 0;i < s->l1_size; i++) {
+ be64_to_cpus(&s->l1_table[i]);
+ }
+ /* alloc L2 cache */
+ s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+ if (!s->l2_cache)
+ goto fail;
+ s->cluster_cache = qemu_malloc(s->cluster_size);
+ if (!s->cluster_cache)
+ goto fail;
+ s->cluster_data = qemu_malloc(s->cluster_size);
+ if (!s->cluster_data)
+ goto fail;
+ s->cluster_cache_offset = -1;
+
+ /* read the backing file name */
+ if (header.backing_file_offset != 0) {
+ len = header.backing_file_size;
+ if (len > 1023)
+ len = 1023;
+ lseek(fd, header.backing_file_offset, SEEK_SET);
+ if (read(fd, bs->backing_file, len) != len)
+ goto fail;
+ bs->backing_file[len] = '\0';
+ }
+ return 0;
+
+ fail:
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+ close(fd);
+ return -1;
+}
+
+static int qcow_set_key(BlockDriverState *bs, const char *key)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint8_t keybuf[16];
+ int len, i;
+
+ memset(keybuf, 0, 16);
+ len = strlen(key);
+ if (len > 16)
+ len = 16;
+ /* XXX: we could compress the chars to 7 bits to increase
+ entropy */
+ for(i = 0;i < len;i++) {
+ keybuf[i] = key[i];
+ }
+ s->crypt_method = s->crypt_method_header;
+
+ if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+ return -1;
+ if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+ return -1;
+#if 0
+ /* test */
+ {
+ uint8_t in[16];
+ uint8_t out[16];
+ uint8_t tmp[16];
+ for(i=0;i<16;i++)
+ in[i] = i;
+ AES_encrypt(in, tmp, &s->aes_encrypt_key);
+ AES_decrypt(tmp, out, &s->aes_decrypt_key);
+ for(i = 0; i < 16; i++)
+ printf(" %02x", tmp[i]);
+ printf("\n");
+ for(i = 0; i < 16; i++)
+ printf(" %02x", out[i]);
+ printf("\n");
+ }
+#endif
+ return 0;
+}
+
+/* The crypt function is compatible with the linux cryptoloop
+ algorithm for < 4 GB images. NOTE: out_buf == in_buf is
+ supported */
+static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+ uint8_t *out_buf, const uint8_t *in_buf,
+ int nb_sectors, int enc,
+ const AES_KEY *key)
+{
+ union {
+ uint64_t ll[2];
+ uint8_t b[16];
+ } ivec;
+ int i;
+
+ for(i = 0; i < nb_sectors; i++) {
+ ivec.ll[0] = cpu_to_le64(sector_num);
+ ivec.ll[1] = 0;
+ AES_cbc_encrypt(in_buf, out_buf, 512, key,
+ ivec.b, enc);
+ sector_num++;
+ in_buf += 512;
+ out_buf += 512;
+ }
+}
+
+/* 'allocate' is:
+ *
+ * 0 to not allocate.
+ *
+ * 1 to allocate a normal cluster (for sector indexes 'n_start' to
+ * 'n_end')
+ *
+ * 2 to allocate a compressed cluster of size
+ * 'compressed_size'. 'compressed_size' must be > 0 and <
+ * cluster_size
+ *
+ * return 0 if not allocated.
+ */
+static uint64_t get_cluster_offset(BlockDriverState *bs,
+ uint64_t offset, int allocate,
+ int compressed_size,
+ int n_start, int n_end)
+{
+ BDRVQcowState *s = bs->opaque;
+ int min_index, i, j, l1_index, l2_index;
+ uint64_t l2_offset, *l2_table, cluster_offset, tmp;
+ uint32_t min_count;
+ int new_l2_table;
+
+ l1_index = offset >> (s->l2_bits + s->cluster_bits);
+ l2_offset = s->l1_table[l1_index];
+ new_l2_table = 0;
+ if (!l2_offset) {
+ if (!allocate)
+ return 0;
+ /* allocate a new l2 entry */
+ l2_offset = lseek(s->fd, 0, SEEK_END);
+ /* round to cluster size */
+ l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
+ /* update the L1 entry */
+ s->l1_table[l1_index] = l2_offset;
+ tmp = cpu_to_be64(l2_offset);
+ lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET);
+ if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
+ return 0;
+ new_l2_table = 1;
+ }
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == s->l2_cache_offsets[i]) {
+ /* increment the hit count */
+ if (++s->l2_cache_counts[i] == 0xffffffff) {
+ for(j = 0; j < L2_CACHE_SIZE; j++) {
+ s->l2_cache_counts[j] >>= 1;
+ }
+ }
+ l2_table = s->l2_cache + (i << s->l2_bits);
+ goto found;
+ }
+ }
+ /* not found: load a new entry in the least used one */
+ min_index = 0;
+ min_count = 0xffffffff;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (s->l2_cache_counts[i] < min_count) {
+ min_count = s->l2_cache_counts[i];
+ min_index = i;
+ }
+ }
+ l2_table = s->l2_cache + (min_index << s->l2_bits);
+ lseek(s->fd, l2_offset, SEEK_SET);
+ if (new_l2_table) {
+ memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
+ if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
+ s->l2_size * sizeof(uint64_t))
+ return 0;
+ } else {
+ if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
+ s->l2_size * sizeof(uint64_t))
+ return 0;
+ }
+ s->l2_cache_offsets[min_index] = l2_offset;
+ s->l2_cache_counts[min_index] = 1;
+ found:
+ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+ cluster_offset = be64_to_cpu(l2_table[l2_index]);
+ if (!cluster_offset ||
+ ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
+ if (!allocate)
+ return 0;
+ /* allocate a new cluster */
+ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
+ (n_end - n_start) < s->cluster_sectors) {
+ /* if the cluster is already compressed, we must
+ decompress it in the case it is not completely
+ overwritten */
+ if (decompress_cluster(s, cluster_offset) < 0)
+ return 0;
+ cluster_offset = lseek(s->fd, 0, SEEK_END);
+ cluster_offset = (cluster_offset + s->cluster_size - 1) &
+ ~(s->cluster_size - 1);
+ /* write the cluster content */
+ lseek(s->fd, cluster_offset, SEEK_SET);
+ if (write(s->fd, s->cluster_cache, s->cluster_size) !=
+ s->cluster_size)
+ return -1;
+ } else {
+ cluster_offset = lseek(s->fd, 0, SEEK_END);
+ if (allocate == 1) {
+ /* round to cluster size */
+ cluster_offset = (cluster_offset + s->cluster_size - 1) &
+ ~(s->cluster_size - 1);
+ ftruncate(s->fd, cluster_offset + s->cluster_size);
+ /* if encrypted, we must initialize the cluster
+ content which won't be written */
+ if (s->crypt_method &&
+ (n_end - n_start) < s->cluster_sectors) {
+ uint64_t start_sect;
+ start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
+ memset(s->cluster_data + 512, 0xaa, 512);
+ for(i = 0; i < s->cluster_sectors; i++) {
+ if (i < n_start || i >= n_end) {
+ encrypt_sectors(s, start_sect + i,
+ s->cluster_data,
+ s->cluster_data + 512, 1, 1,
+ &s->aes_encrypt_key);
+ lseek(s->fd, cluster_offset + i * 512, SEEK_SET);
+ if (write(s->fd, s->cluster_data, 512) != 512)
+ return -1;
+ }
+ }
+ }
+ } else {
+ cluster_offset |= QCOW_OFLAG_COMPRESSED |
+ (uint64_t)compressed_size << (63 - s->cluster_bits);
+ }
+ }
+ /* update L2 table */
+ tmp = cpu_to_be64(cluster_offset);
+ l2_table[l2_index] = tmp;
+ lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET);
+ if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
+ return 0;
+ }
+ return cluster_offset;
+}
+
+static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ *pnum = n;
+ return (cluster_offset != 0);
+}
+
+static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
+ const uint8_t *buf, int buf_size)
+{
+ z_stream strm1, *strm = &strm1;
+ int ret, out_len;
+
+ memset(strm, 0, sizeof(*strm));
+
+ strm->next_in = (uint8_t *)buf;
+ strm->avail_in = buf_size;
+ strm->next_out = out_buf;
+ strm->avail_out = out_buf_size;
+
+ ret = inflateInit2(strm, -12);
+ if (ret != Z_OK)
+ return -1;
+ ret = inflate(strm, Z_FINISH);
+ out_len = strm->next_out - out_buf;
+ if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
+ out_len != out_buf_size) {
+ inflateEnd(strm);
+ return -1;
+ }
+ inflateEnd(strm);
+ return 0;
+}
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
+{
+ int ret, csize;
+ uint64_t coffset;
+
+ coffset = cluster_offset & s->cluster_offset_mask;
+ if (s->cluster_cache_offset != coffset) {
+ csize = cluster_offset >> (63 - s->cluster_bits);
+ csize &= (s->cluster_size - 1);
+ lseek(s->fd, coffset, SEEK_SET);
+ ret = read(s->fd, s->cluster_data, csize);
+ if (ret != csize)
+ return -1;
+ if (decompress_buffer(s->cluster_cache, s->cluster_size,
+ s->cluster_data, csize) < 0) {
+ return -1;
+ }
+ s->cluster_cache_offset = coffset;
+ }
+ return 0;
+}
+
+static int qcow_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ if (!cluster_offset) {
+ memset(buf, 0, 512 * n);
+ } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ if (decompress_cluster(s, cluster_offset) < 0)
+ return -1;
+ memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
+ } else {
+ lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
+ ret = read(s->fd, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ if (s->crypt_method) {
+ encrypt_sectors(s, sector_num, buf, buf, n, 0,
+ &s->aes_decrypt_key);
+ }
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int qcow_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
+ index_in_cluster,
+ index_in_cluster + n);
+ if (!cluster_offset)
+ return -1;
+ lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
+ if (s->crypt_method) {
+ encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
+ &s->aes_encrypt_key);
+ ret = write(s->fd, s->cluster_data, n * 512);
+ } else {
+ ret = write(s->fd, buf, n * 512);
+ }
+ if (ret != n * 512)
+ return -1;
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ s->cluster_cache_offset = -1; /* disable compressed cache */
+ return 0;
+}
+
+static void qcow_close(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+ close(s->fd);
+}
+
+static int qcow_create(const char *filename, int64_t total_size,
+ const char *backing_file, int flags)
+{
+ int fd, header_size, backing_filename_len, l1_size, i, shift;
+ QCowHeader header;
+ char backing_filename[1024];
+ uint64_t tmp;
+ struct stat st;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0)
+ return -1;
+ memset(&header, 0, sizeof(header));
+ header.magic = cpu_to_be32(QCOW_MAGIC);
+ header.version = cpu_to_be32(QCOW_VERSION);
+ header.size = cpu_to_be64(total_size * 512);
+ header_size = sizeof(header);
+ backing_filename_len = 0;
+ if (backing_file) {
+ if (strcmp(backing_file, "fat:")) {
+ const char *p;
+ /* XXX: this is a hack: we do not attempt to check for URL
+ like syntax */
+ p = strchr(backing_file, ':');
+ if (p && (p - backing_file) >= 2) {
+ /* URL like but exclude "c:" like filenames */
+ pstrcpy(backing_filename, sizeof(backing_filename),
+ backing_file);
+ } else {
+ realpath(backing_file, backing_filename);
+ if (stat(backing_filename, &st) != 0) {
+ return -1;
+ }
+ }
+ header.backing_file_offset = cpu_to_be64(header_size);
+ backing_filename_len = strlen(backing_filename);
+ header.backing_file_size = cpu_to_be32(backing_filename_len);
+ header_size += backing_filename_len;
+ } else
+ backing_file = NULL;
+ header.mtime = cpu_to_be32(st.st_mtime);
+ header.cluster_bits = 9; /* 512 byte cluster to avoid copying
+ unmodifyed sectors */
+ header.l2_bits = 12; /* 32 KB L2 tables */
+ } else {
+ header.cluster_bits = 12; /* 4 KB clusters */
+ header.l2_bits = 9; /* 4 KB L2 tables */
+ }
+ header_size = (header_size + 7) & ~7;
+ shift = header.cluster_bits + header.l2_bits;
+ l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
+
+ header.l1_table_offset = cpu_to_be64(header_size);
+ if (flags) {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+ } else {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+ }
+
+ /* write all the data */
+ write(fd, &header, sizeof(header));
+ if (backing_file) {
+ write(fd, backing_filename, backing_filename_len);
+ }
+ lseek(fd, header_size, SEEK_SET);
+ tmp = 0;
+ for(i = 0;i < l1_size; i++) {
+ write(fd, &tmp, sizeof(tmp));
+ }
+ close(fd);
+ return 0;
+}
+
+int qcow_make_empty(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint32_t l1_length = s->l1_size * sizeof(uint64_t);
+
+ memset(s->l1_table, 0, l1_length);
+ lseek(s->fd, s->l1_table_offset, SEEK_SET);
+ if (write(s->fd, s->l1_table, l1_length) < 0)
+ return -1;
+ ftruncate(s->fd, s->l1_table_offset + l1_length);
+
+ memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+ memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
+ memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
+
+ return 0;
+}
+
+int qcow_get_cluster_size(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ if (bs->drv != &bdrv_qcow)
+ return -1;
+ return s->cluster_size;
+}
+
+/* XXX: put compressed sectors first, then all the cluster aligned
+ tables to avoid losing bytes in alignment */
+int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf)
+{
+ BDRVQcowState *s = bs->opaque;
+ z_stream strm;
+ int ret, out_len;
+ uint8_t *out_buf;
+ uint64_t cluster_offset;
+
+ if (bs->drv != &bdrv_qcow)
+ return -1;
+
+ out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
+ if (!out_buf)
+ return -1;
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED, -12,
+ 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ qemu_free(out_buf);
+ return -1;
+ }
+
+ strm.avail_in = s->cluster_size;
+ strm.next_in = (uint8_t *)buf;
+ strm.avail_out = s->cluster_size;
+ strm.next_out = out_buf;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ qemu_free(out_buf);
+ deflateEnd(&strm);
+ return -1;
+ }
+ out_len = strm.next_out - out_buf;
+
+ deflateEnd(&strm);
+
+ if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ /* could not compress: write normal cluster */
+ qcow_write(bs, sector_num, buf, s->cluster_sectors);
+ } else {
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
+ out_len, 0, 0);
+ cluster_offset &= s->cluster_offset_mask;
+ lseek(s->fd, cluster_offset, SEEK_SET);
+ if (write(s->fd, out_buf, out_len) != out_len) {
+ qemu_free(out_buf);
+ return -1;
+ }
+ }
+
+ qemu_free(out_buf);
+ return 0;
+}
+
+static void qcow_flush(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ fsync(s->fd);
+}
+
+BlockDriver bdrv_qcow = {
+ "qcow",
+ sizeof(BDRVQcowState),
+ qcow_probe,
+ qcow_open,
+ qcow_read,
+ qcow_write,
+ qcow_close,
+ qcow_create,
+ qcow_flush,
+ qcow_is_allocated,
+ qcow_set_key,
+ qcow_make_empty
+};
+
+
diff --git a/block-vmdk.c b/block-vmdk.c
new file mode 100644
index 0000000..4cc3db8
--- /dev/null
+++ b/block-vmdk.c
@@ -0,0 +1,446 @@
+/*
+ * Block driver for the VMDK format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2005 Filip Navara
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+
+#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
+#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
+
+typedef struct {
+ uint32_t version;
+ uint32_t flags;
+ uint32_t disk_sectors;
+ uint32_t granularity;
+ uint32_t l1dir_offset;
+ uint32_t l1dir_size;
+ uint32_t file_sectors;
+ uint32_t cylinders;
+ uint32_t heads;
+ uint32_t sectors_per_track;
+} VMDK3Header;
+
+typedef struct {
+ uint32_t version;
+ uint32_t flags;
+ int64_t capacity;
+ int64_t granularity;
+ int64_t desc_offset;
+ int64_t desc_size;
+ int32_t num_gtes_per_gte;
+ int64_t rgd_offset;
+ int64_t gd_offset;
+ int64_t grain_offset;
+ char filler[1];
+ char check_bytes[4];
+} __attribute__((packed)) VMDK4Header;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVVmdkState {
+ int fd;
+ int64_t l1_table_offset;
+ int64_t l1_backup_table_offset;
+ uint32_t *l1_table;
+ uint32_t *l1_backup_table;
+ unsigned int l1_size;
+ uint32_t l1_entry_sectors;
+
+ unsigned int l2_size;
+ uint32_t *l2_cache;
+ uint32_t l2_cache_offsets[L2_CACHE_SIZE];
+ uint32_t l2_cache_counts[L2_CACHE_SIZE];
+
+ unsigned int cluster_sectors;
+} BDRVVmdkState;
+
+static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ uint32_t magic;
+
+ if (buf_size < 4)
+ return 0;
+ magic = be32_to_cpu(*(uint32_t *)buf);
+ if (magic == VMDK3_MAGIC ||
+ magic == VMDK4_MAGIC)
+ return 100;
+ else
+ return 0;
+}
+
+static int vmdk_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int fd, i;
+ uint32_t magic;
+ int l1_size;
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ bs->read_only = 1;
+ }
+ if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
+ goto fail;
+ magic = be32_to_cpu(magic);
+ if (magic == VMDK3_MAGIC) {
+ VMDK3Header header;
+ if (read(fd, &header, sizeof(header)) !=
+ sizeof(header))
+ goto fail;
+ s->cluster_sectors = le32_to_cpu(header.granularity);
+ s->l2_size = 1 << 9;
+ s->l1_size = 1 << 6;
+ bs->total_sectors = le32_to_cpu(header.disk_sectors);
+ s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
+ s->l1_backup_table_offset = 0;
+ s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ } else if (magic == VMDK4_MAGIC) {
+ VMDK4Header header;
+
+ if (read(fd, &header, sizeof(header)) != sizeof(header))
+ goto fail;
+ bs->total_sectors = le64_to_cpu(header.capacity);
+ s->cluster_sectors = le64_to_cpu(header.granularity);
+ s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
+ s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ if (s->l1_entry_sectors <= 0)
+ goto fail;
+ s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
+ / s->l1_entry_sectors;
+ s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
+ s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
+ } else {
+ goto fail;
+ }
+ /* read the L1 table */
+ l1_size = s->l1_size * sizeof(uint32_t);
+ s->l1_table = qemu_malloc(l1_size);
+ if (!s->l1_table)
+ goto fail;
+ if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1)
+ goto fail;
+ if (read(fd, s->l1_table, l1_size) != l1_size)
+ goto fail;
+ for(i = 0; i < s->l1_size; i++) {
+ le32_to_cpus(&s->l1_table[i]);
+ }
+
+ if (s->l1_backup_table_offset) {
+ s->l1_backup_table = qemu_malloc(l1_size);
+ if (!s->l1_backup_table)
+ goto fail;
+ if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
+ goto fail;
+ if (read(fd, s->l1_backup_table, l1_size) != l1_size)
+ goto fail;
+ for(i = 0; i < s->l1_size; i++) {
+ le32_to_cpus(&s->l1_backup_table[i]);
+ }
+ }
+
+ s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+ if (!s->l2_cache)
+ goto fail;
+ s->fd = fd;
+ return 0;
+ fail:
+ qemu_free(s->l1_backup_table);
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ close(fd);
+ return -1;
+}
+
+static uint64_t get_cluster_offset(BlockDriverState *bs,
+ uint64_t offset, int allocate)
+{
+ BDRVVmdkState *s = bs->opaque;
+ unsigned int l1_index, l2_offset, l2_index;
+ int min_index, i, j;
+ uint32_t min_count, *l2_table, tmp;
+ uint64_t cluster_offset;
+
+ l1_index = (offset >> 9) / s->l1_entry_sectors;
+ if (l1_index >= s->l1_size)
+ return 0;
+ l2_offset = s->l1_table[l1_index];
+ if (!l2_offset)
+ return 0;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == s->l2_cache_offsets[i]) {
+ /* increment the hit count */
+ if (++s->l2_cache_counts[i] == 0xffffffff) {
+ for(j = 0; j < L2_CACHE_SIZE; j++) {
+ s->l2_cache_counts[j] >>= 1;
+ }
+ }
+ l2_table = s->l2_cache + (i * s->l2_size);
+ goto found;
+ }
+ }
+ /* not found: load a new entry in the least used one */
+ min_index = 0;
+ min_count = 0xffffffff;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (s->l2_cache_counts[i] < min_count) {
+ min_count = s->l2_cache_counts[i];
+ min_index = i;
+ }
+ }
+ l2_table = s->l2_cache + (min_index * s->l2_size);
+ lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
+ if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) !=
+ s->l2_size * sizeof(uint32_t))
+ return 0;
+ s->l2_cache_offsets[min_index] = l2_offset;
+ s->l2_cache_counts[min_index] = 1;
+ found:
+ l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
+ cluster_offset = le32_to_cpu(l2_table[l2_index]);
+ if (!cluster_offset) {
+ if (!allocate)
+ return 0;
+ cluster_offset = lseek(s->fd, 0, SEEK_END);
+ ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
+ cluster_offset >>= 9;
+ /* update L2 table */
+ tmp = cpu_to_le32(cluster_offset);
+ l2_table[l2_index] = tmp;
+ lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
+ if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
+ return 0;
+ /* update backup L2 table */
+ if (s->l1_backup_table_offset != 0) {
+ l2_offset = s->l1_backup_table[l1_index];
+ lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
+ if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
+ return 0;
+ }
+ }
+ cluster_offset <<= 9;
+ return cluster_offset;
+}
+
+static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
+ index_in_cluster = sector_num % s->cluster_sectors;
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ *pnum = n;
+ return (cluster_offset != 0);
+}
+
+static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int ret, index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
+ index_in_cluster = sector_num % s->cluster_sectors;
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ if (!cluster_offset) {
+ memset(buf, 0, 512 * n);
+ } else {
+ lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
+ ret = read(s->fd, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int ret, index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
+ if (!cluster_offset)
+ return -1;
+ lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
+ ret = write(s->fd, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int vmdk_create(const char *filename, int64_t total_size,
+ const char *backing_file, int flags)
+{
+ int fd, i;
+ VMDK4Header header;
+ uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
+ char *desc_template =
+ "# Disk DescriptorFile\n"
+ "version=1\n"
+ "CID=%x\n"
+ "parentCID=ffffffff\n"
+ "createType=\"monolithicSparse\"\n"
+ "\n"
+ "# Extent description\n"
+ "RW %lu SPARSE \"%s\"\n"
+ "\n"
+ "# The Disk Data Base \n"
+ "#DDB\n"
+ "\n"
+ "ddb.virtualHWVersion = \"3\"\n"
+ "ddb.geometry.cylinders = \"%lu\"\n"
+ "ddb.geometry.heads = \"16\"\n"
+ "ddb.geometry.sectors = \"63\"\n"
+ "ddb.adapterType = \"ide\"\n";
+ char desc[1024];
+ const char *real_filename, *temp_str;
+
+ /* XXX: add support for backing file */
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0)
+ return -1;
+ magic = cpu_to_be32(VMDK4_MAGIC);
+ memset(&header, 0, sizeof(header));
+ header.version = cpu_to_le32(1);
+ header.flags = cpu_to_le32(3); /* ?? */
+ header.capacity = cpu_to_le64(total_size);
+ header.granularity = cpu_to_le64(128);
+ header.num_gtes_per_gte = cpu_to_le32(512);
+
+ grains = (total_size + header.granularity - 1) / header.granularity;
+ gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
+ gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
+ gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
+
+ header.desc_offset = 1;
+ header.desc_size = 20;
+ header.rgd_offset = header.desc_offset + header.desc_size;
+ header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
+ header.grain_offset =
+ ((header.gd_offset + gd_size + (gt_size * gt_count) +
+ header.granularity - 1) / header.granularity) *
+ header.granularity;
+
+ header.desc_offset = cpu_to_le64(header.desc_offset);
+ header.desc_size = cpu_to_le64(header.desc_size);
+ header.rgd_offset = cpu_to_le64(header.rgd_offset);
+ header.gd_offset = cpu_to_le64(header.gd_offset);
+ header.grain_offset = cpu_to_le64(header.grain_offset);
+
+ header.check_bytes[0] = 0xa;
+ header.check_bytes[1] = 0x20;
+ header.check_bytes[2] = 0xd;
+ header.check_bytes[3] = 0xa;
+
+ /* write all the data */
+ write(fd, &magic, sizeof(magic));
+ write(fd, &header, sizeof(header));
+
+ ftruncate(fd, header.grain_offset << 9);
+
+ /* write grain directory */
+ lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
+ for (i = 0, tmp = header.rgd_offset + gd_size;
+ i < gt_count; i++, tmp += gt_size)
+ write(fd, &tmp, sizeof(tmp));
+
+ /* write backup grain directory */
+ lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
+ for (i = 0, tmp = header.gd_offset + gd_size;
+ i < gt_count; i++, tmp += gt_size)
+ write(fd, &tmp, sizeof(tmp));
+
+ /* compose the descriptor */
+ real_filename = filename;
+ if ((temp_str = strrchr(real_filename, '\\')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, '/')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, ':')) != NULL)
+ real_filename = temp_str + 1;
+ sprintf(desc, desc_template, time(NULL), (unsigned long)total_size,
+ real_filename, total_size / (63 * 16));
+
+ /* write the descriptor */
+ lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
+ write(fd, desc, strlen(desc));
+
+ close(fd);
+ return 0;
+}
+
+static void vmdk_close(BlockDriverState *bs)
+{
+ BDRVVmdkState *s = bs->opaque;
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ close(s->fd);
+}
+
+static void vmdk_flush(BlockDriverState *bs)
+{
+ BDRVVmdkState *s = bs->opaque;
+ fsync(s->fd);
+}
+
+BlockDriver bdrv_vmdk = {
+ "vmdk",
+ sizeof(BDRVVmdkState),
+ vmdk_probe,
+ vmdk_open,
+ vmdk_read,
+ vmdk_write,
+ vmdk_close,
+ vmdk_create,
+ vmdk_flush,
+ vmdk_is_allocated,
+};
diff --git a/block-vpc.c b/block-vpc.c
new file mode 100644
index 0000000..bdc3b88
--- /dev/null
+++ b/block-vpc.c
@@ -0,0 +1,242 @@
+/*
+ * Block driver for Conectix/Microsoft Virtual PC images
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+
+/**************************************************************/
+
+#define HEADER_SIZE 512
+
+//#define CACHE
+
+// always big-endian
+struct vpc_subheader {
+ char magic[8]; // "conectix" / "cxsparse"
+ union {
+ struct {
+ uint32_t unk1[2];
+ uint32_t unk2; // always zero?
+ uint32_t subheader_offset;
+ uint32_t unk3; // some size?
+ char creator[4]; // "vpc "
+ uint16_t major;
+ uint16_t minor;
+ char guest[4]; // "Wi2k"
+ uint32_t unk4[7];
+ uint8_t vnet_id[16]; // virtual network id, purpose unknown
+ // next 16 longs are used, but dunno the purpose
+ // next 6 longs unknown, following 7 long maybe a serial
+ char padding[HEADER_SIZE - 84];
+ } main;
+ struct {
+ uint32_t unk1[2]; // all bits set
+ uint32_t unk2; // always zero?
+ uint32_t pagetable_offset;
+ uint32_t unk3;
+ uint32_t pagetable_entries; // 32bit/entry
+ uint32_t pageentry_size; // 512*8*512
+ uint32_t nb_sectors;
+ char padding[HEADER_SIZE - 40];
+ } sparse;
+ char padding[HEADER_SIZE - 8];
+ } type;
+};
+
+typedef struct BDRVVPCState {
+ int fd;
+
+ int pagetable_entries;
+ uint32_t *pagetable;
+
+ uint32_t pageentry_size;
+#ifdef CACHE
+ uint8_t *pageentry_u8;
+ uint32_t *pageentry_u32;
+ uint16_t *pageentry_u16;
+
+ uint64_t last_bitmap;
+#endif
+} BDRVVPCState;
+
+static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ if (buf_size >= 8 && !strncmp(buf, "conectix", 8))
+ return 100;
+ return 0;
+}
+
+static int vpc_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVVPCState *s = bs->opaque;
+ int fd, i;
+ struct vpc_subheader header;
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ }
+
+ bs->read_only = 1; // no write support yet
+
+ s->fd = fd;
+
+ if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
+ goto fail;
+
+ if (strncmp(header.magic, "conectix", 8))
+ goto fail;
+ lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET);
+
+ if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
+ goto fail;
+
+ if (strncmp(header.magic, "cxsparse", 8))
+ goto fail;
+
+ bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) *
+ be32_to_cpu(header.type.sparse.pageentry_size)) / 512;
+
+ lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET);
+
+ s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries);
+ s->pagetable = qemu_malloc(s->pagetable_entries * 4);
+ if (!s->pagetable)
+ goto fail;
+ if (read(s->fd, s->pagetable, s->pagetable_entries * 4) !=
+ s->pagetable_entries * 4)
+ goto fail;
+ for (i = 0; i < s->pagetable_entries; i++)
+ be32_to_cpus(&s->pagetable[i]);
+
+ s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size);
+#ifdef CACHE
+ s->pageentry_u8 = qemu_malloc(512);
+ if (!s->pageentry_u8)
+ goto fail;
+ s->pageentry_u32 = s->pageentry_u8;
+ s->pageentry_u16 = s->pageentry_u8;
+ s->last_pagetable = -1;
+#endif
+
+ return 0;
+ fail:
+ close(fd);
+ return -1;
+}
+
+static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+ BDRVVPCState *s = bs->opaque;
+ uint64_t offset = sector_num * 512;
+ uint64_t bitmap_offset, block_offset;
+ uint32_t pagetable_index, pageentry_index;
+
+ pagetable_index = offset / s->pageentry_size;
+ pageentry_index = (offset % s->pageentry_size) / 512;
+
+ if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff)
+ return -1; // not allocated
+
+ bitmap_offset = 512 * s->pagetable[pagetable_index];
+ block_offset = bitmap_offset + 512 + (512 * pageentry_index);
+
+// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
+// sector_num, pagetable_index, pageentry_index,
+// bitmap_offset, block_offset);
+
+// disabled by reason
+#if 0
+#ifdef CACHE
+ if (bitmap_offset != s->last_bitmap)
+ {
+ lseek(s->fd, bitmap_offset, SEEK_SET);
+
+ s->last_bitmap = bitmap_offset;
+
+ // Scary! Bitmap is stored as big endian 32bit entries,
+ // while we used to look it up byte by byte
+ read(s->fd, s->pageentry_u8, 512);
+ for (i = 0; i < 128; i++)
+ be32_to_cpus(&s->pageentry_u32[i]);
+ }
+
+ if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
+ return -1;
+#else
+ lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
+
+ read(s->fd, &bitmap_entry, 1);
+
+ if ((bitmap_entry >> (pageentry_index % 8)) & 1)
+ return -1; // not allocated
+#endif
+#endif
+ lseek(s->fd, block_offset, SEEK_SET);
+
+ return 0;
+}
+
+static int vpc_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVPCState *s = bs->opaque;
+ int ret;
+
+ while (nb_sectors > 0) {
+ if (!seek_to_sector(bs, sector_num))
+ {
+ ret = read(s->fd, buf, 512);
+ if (ret != 512)
+ return -1;
+ }
+ else
+ memset(buf, 0, 512);
+ nb_sectors--;
+ sector_num++;
+ buf += 512;
+ }
+ return 0;
+}
+
+static void vpc_close(BlockDriverState *bs)
+{
+ BDRVVPCState *s = bs->opaque;
+ qemu_free(s->pagetable);
+#ifdef CACHE
+ qemu_free(s->pageentry_u8);
+#endif
+ close(s->fd);
+}
+
+BlockDriver bdrv_vpc = {
+ "vpc",
+ sizeof(BDRVVPCState),
+ vpc_probe,
+ vpc_open,
+ vpc_read,
+ NULL,
+ vpc_close,
+};
diff --git a/block-vvfat.c b/block-vvfat.c
new file mode 100644
index 0000000..9dedf91
--- /dev/null
+++ b/block-vvfat.c
@@ -0,0 +1,2808 @@
+/* vim:set shiftwidth=4 ts=8: */
+/*
+ * QEMU Block driver for virtual VFAT (shadows a local directory)
+ *
+ * Copyright (c) 2004,2005 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include <assert.h>
+#include "vl.h"
+#include "block_int.h"
+
+#ifndef S_IWGRP
+#define S_IWGRP 0
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0
+#endif
+
+/* TODO: add ":bootsector=blabla.img:" */
+/* LATER TODO: add automatic boot sector generation from
+ BOOTEASY.ASM and Ranish Partition Manager
+ Note that DOS assumes the system files to be the first files in the
+ file system (test if the boot sector still relies on that fact)! */
+/* MAYBE TODO: write block-visofs.c */
+/* TODO: call try_commit() only after a timeout */
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+
+#define DLOG(a) a
+
+#undef stderr
+#define stderr STDERR
+FILE* stderr = NULL;
+
+static void checkpoint();
+
+#ifdef __MINGW32__
+void nonono(const char* file, int line, const char* msg) {
+ fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
+ exit(-5);
+}
+#undef assert
+#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a)
+#endif
+
+#else
+
+#define DLOG(a)
+
+#endif
+
+/* dynamic array functions */
+typedef struct array_t {
+ char* pointer;
+ unsigned int size,next,item_size;
+} array_t;
+
+static inline void array_init(array_t* array,unsigned int item_size)
+{
+ array->pointer=0;
+ array->size=0;
+ array->next=0;
+ array->item_size=item_size;
+}
+
+static inline void array_free(array_t* array)
+{
+ if(array->pointer)
+ free(array->pointer);
+ array->size=array->next=0;
+}
+
+/* does not automatically grow */
+static inline void* array_get(array_t* array,unsigned int index) {
+ assert(index >= 0);
+ assert(index < array->next);
+ return array->pointer + index * array->item_size;
+}
+
+static inline int array_ensure_allocated(array_t* array, int index)
+{
+ if((index + 1) * array->item_size > array->size) {
+ int new_size = (index + 32) * array->item_size;
+ array->pointer = realloc(array->pointer, new_size);
+ if (!array->pointer)
+ return -1;
+ array->size = new_size;
+ array->next = index + 1;
+ }
+
+ return 0;
+}
+
+static inline void* array_get_next(array_t* array) {
+ unsigned int next = array->next;
+ void* result;
+
+ if (array_ensure_allocated(array, next) < 0)
+ return NULL;
+
+ array->next = next + 1;
+ result = array_get(array, next);
+
+ return result;
+}
+
+static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
+ if((array->next+count)*array->item_size>array->size) {
+ int increment=count*array->item_size;
+ array->pointer=realloc(array->pointer,array->size+increment);
+ if(!array->pointer)
+ return 0;
+ array->size+=increment;
+ }
+ memmove(array->pointer+(index+count)*array->item_size,
+ array->pointer+index*array->item_size,
+ (array->next-index)*array->item_size);
+ array->next+=count;
+ return array->pointer+index*array->item_size;
+}
+
+/* this performs a "roll", so that the element which was at index_from becomes
+ * index_to, but the order of all other elements is preserved. */
+static inline int array_roll(array_t* array,int index_to,int index_from,int count)
+{
+ char* buf;
+ char* from;
+ char* to;
+ int is;
+
+ if(!array ||
+ index_to<0 || index_to>=array->next ||
+ index_from<0 || index_from>=array->next)
+ return -1;
+
+ if(index_to==index_from)
+ return 0;
+
+ is=array->item_size;
+ from=array->pointer+index_from*is;
+ to=array->pointer+index_to*is;
+ buf=malloc(is*count);
+ memcpy(buf,from,is*count);
+
+ if(index_to<index_from)
+ memmove(to+is*count,to,from-to);
+ else
+ memmove(from,from+is*count,to-from);
+
+ memcpy(to,buf,is*count);
+
+ free(buf);
+
+ return 0;
+}
+
+inline int array_remove_slice(array_t* array,int index, int count)
+{
+ assert(index >=0);
+ assert(count > 0);
+ assert(index + count <= array->next);
+ if(array_roll(array,array->next-1,index,count))
+ return -1;
+ array->next -= count;
+ return 0;
+}
+
+int array_remove(array_t* array,int index)
+{
+ return array_remove_slice(array, index, 1);
+}
+
+/* return the index for a given member */
+int array_index(array_t* array, void* pointer)
+{
+ size_t offset = (char*)pointer - array->pointer;
+ assert(offset >= 0);
+ assert((offset % array->item_size) == 0);
+ assert(offset/array->item_size < array->next);
+ return offset/array->item_size;
+}
+
+/* These structures are used to fake a disk and the VFAT filesystem.
+ * For this reason we need to use __attribute__((packed)). */
+
+typedef struct bootsector_t {
+ uint8_t jump[3];
+ uint8_t name[8];
+ uint16_t sector_size;
+ uint8_t sectors_per_cluster;
+ uint16_t reserved_sectors;
+ uint8_t number_of_fats;
+ uint16_t root_entries;
+ uint16_t total_sectors16;
+ uint8_t media_type;
+ uint16_t sectors_per_fat;
+ uint16_t sectors_per_track;
+ uint16_t number_of_heads;
+ uint32_t hidden_sectors;
+ uint32_t total_sectors;
+ union {
+ struct {
+ uint8_t drive_number;
+ uint8_t current_head;
+ uint8_t signature;
+ uint32_t id;
+ uint8_t volume_label[11];
+ } __attribute__((packed)) fat16;
+ struct {
+ uint32_t sectors_per_fat;
+ uint16_t flags;
+ uint8_t major,minor;
+ uint32_t first_cluster_of_root_directory;
+ uint16_t info_sector;
+ uint16_t backup_boot_sector;
+ uint16_t ignored;
+ } __attribute__((packed)) fat32;
+ } u;
+ uint8_t fat_type[8];
+ uint8_t ignored[0x1c0];
+ uint8_t magic[2];
+} __attribute__((packed)) bootsector_t;
+
+typedef struct partition_t {
+ uint8_t attributes; /* 0x80 = bootable */
+ uint8_t start_head;
+ uint8_t start_sector;
+ uint8_t start_cylinder;
+ uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */
+ uint8_t end_head;
+ uint8_t end_sector;
+ uint8_t end_cylinder;
+ uint32_t start_sector_long;
+ uint32_t end_sector_long;
+} __attribute__((packed)) partition_t;
+
+typedef struct mbr_t {
+ uint8_t ignored[0x1be];
+ partition_t partition[4];
+ uint8_t magic[2];
+} __attribute__((packed)) mbr_t;
+
+typedef struct direntry_t {
+ uint8_t name[8];
+ uint8_t extension[3];
+ uint8_t attributes;
+ uint8_t reserved[2];
+ uint16_t ctime;
+ uint16_t cdate;
+ uint16_t adate;
+ uint16_t begin_hi;
+ uint16_t mtime;
+ uint16_t mdate;
+ uint16_t begin;
+ uint32_t size;
+} __attribute__((packed)) direntry_t;
+
+/* this structure are used to transparently access the files */
+
+typedef struct mapping_t {
+ /* begin is the first cluster, end is the last+1 */
+ uint32_t begin,end;
+ /* as s->directory is growable, no pointer may be used here */
+ unsigned int dir_index;
+ /* the clusters of a file may be in any order; this points to the first */
+ int first_mapping_index;
+ union {
+ /* offset is
+ * - the offset in the file (in clusters) for a file, or
+ * - the next cluster of the directory for a directory, and
+ * - the address of the buffer for a faked entry
+ */
+ struct {
+ uint32_t offset;
+ } file;
+ struct {
+ int parent_mapping_index;
+ int first_dir_index;
+ } dir;
+ } info;
+ /* path contains the full path, i.e. it always starts with s->path */
+ char* path;
+
+ enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
+ MODE_DIRECTORY = 4, MODE_FAKED = 8,
+ MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
+ int read_only;
+} mapping_t;
+
+#ifdef DEBUG
+static void print_direntry(const struct direntry_t*);
+static void print_mapping(const struct mapping_t* mapping);
+#endif
+
+/* here begins the real VVFAT driver */
+
+typedef struct BDRVVVFATState {
+ BlockDriverState* bs; /* pointer to parent */
+ unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
+ unsigned char first_sectors[0x40*0x200];
+
+ int fat_type; /* 16 or 32 */
+ array_t fat,directory,mapping;
+
+ unsigned int cluster_size;
+ unsigned int sectors_per_cluster;
+ unsigned int sectors_per_fat;
+ unsigned int sectors_of_root_directory;
+ uint32_t last_cluster_of_root_directory;
+ unsigned int faked_sectors; /* how many sectors are faked before file data */
+ uint32_t sector_count; /* total number of sectors of the partition */
+ uint32_t cluster_count; /* total number of clusters of this partition */
+ uint32_t max_fat_value;
+
+ int current_fd;
+ mapping_t* current_mapping;
+ unsigned char* cluster; /* points to current cluster */
+ unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
+ unsigned int current_cluster;
+
+ /* write support */
+ BlockDriverState* write_target;
+ char* qcow_filename;
+ BlockDriverState* qcow;
+ void* fat2;
+ char* used_clusters;
+ array_t commits;
+ const char* path;
+ int downcase_short_names;
+} BDRVVVFATState;
+
+
+static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ if (strstart(filename, "fat:", NULL))
+ return 100;
+ return 0;
+}
+
+static void init_mbr(BDRVVVFATState* s)
+{
+ /* TODO: if the files mbr.img and bootsect.img exist, use them */
+ mbr_t* real_mbr=(mbr_t*)s->first_sectors;
+ partition_t* partition=&(real_mbr->partition[0]);
+
+ memset(s->first_sectors,0,512);
+
+ partition->attributes=0x80; /* bootable */
+ partition->start_head=1;
+ partition->start_sector=1;
+ partition->start_cylinder=0;
+ /* FAT12/FAT16/FAT32 */
+ partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
+ partition->end_head=s->bs->heads-1;
+ partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
+ partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
+ partition->start_sector_long=cpu_to_le32(s->bs->secs);
+ partition->end_sector_long=cpu_to_le32(s->sector_count);
+
+ real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
+}
+
+/* direntry functions */
+
+/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
+static inline int short2long_name(unsigned char* dest,const char* src)
+{
+ int i;
+ for(i=0;i<129 && src[i];i++) {
+ dest[2*i]=src[i];
+ dest[2*i+1]=0;
+ }
+ dest[2*i]=dest[2*i+1]=0;
+ for(i=2*i+2;(i%26);i++)
+ dest[i]=0xff;
+ return i;
+}
+
+static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
+{
+ char buffer[258];
+ int length=short2long_name(buffer,filename),
+ number_of_entries=(length+25)/26,i;
+ direntry_t* entry;
+
+ for(i=0;i<number_of_entries;i++) {
+ entry=array_get_next(&(s->directory));
+ entry->attributes=0xf;
+ entry->reserved[0]=0;
+ entry->begin=0;
+ entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
+ }
+ for(i=0;i<length;i++) {
+ int offset=(i%26);
+ if(offset<10) offset=1+offset;
+ else if(offset<22) offset=14+offset-10;
+ else offset=28+offset-22;
+ entry=array_get(&(s->directory),s->directory.next-1-(i/26));
+ entry->name[offset]=buffer[i];
+ }
+ return array_get(&(s->directory),s->directory.next-number_of_entries);
+}
+
+static char is_free(const direntry_t* direntry)
+{
+ /* return direntry->name[0]==0 ; */
+ return direntry->attributes == 0 || direntry->name[0]==0xe5;
+}
+
+static char is_volume_label(const direntry_t* direntry)
+{
+ return direntry->attributes == 0x28;
+}
+
+static char is_long_name(const direntry_t* direntry)
+{
+ return direntry->attributes == 0xf;
+}
+
+static char is_short_name(const direntry_t* direntry)
+{
+ return !is_volume_label(direntry) && !is_long_name(direntry)
+ && !is_free(direntry);
+}
+
+static char is_directory(const direntry_t* direntry)
+{
+ return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
+}
+
+static inline char is_dot(const direntry_t* direntry)
+{
+ return is_short_name(direntry) && direntry->name[0] == '.';
+}
+
+static char is_file(const direntry_t* direntry)
+{
+ return is_short_name(direntry) && !is_directory(direntry);
+}
+
+static inline uint32_t begin_of_direntry(const direntry_t* direntry)
+{
+ return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
+}
+
+static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
+{
+ return le32_to_cpu(direntry->size);
+}
+
+static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
+{
+ direntry->begin = cpu_to_le16(begin & 0xffff);
+ direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
+}
+
+/* fat functions */
+
+static inline uint8_t fat_chksum(const direntry_t* entry)
+{
+ uint8_t chksum=0;
+ int i;
+
+ for(i=0;i<11;i++)
+ chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
+ +(unsigned char)entry->name[i];
+
+ return chksum;
+}
+
+/* if return_time==0, this returns the fat_date, else the fat_time */
+static uint16_t fat_datetime(time_t time,int return_time) {
+ struct tm* t;
+#ifdef _WIN32
+ t=localtime(&time); /* this is not thread safe */
+#else
+ struct tm t1;
+ t=&t1;
+ localtime_r(&time,t);
+#endif
+ if(return_time)
+ return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
+ return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
+}
+
+static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
+{
+ if(s->fat_type==32) {
+ uint32_t* entry=array_get(&(s->fat),cluster);
+ *entry=cpu_to_le32(value);
+ } else if(s->fat_type==16) {
+ uint16_t* entry=array_get(&(s->fat),cluster);
+ *entry=cpu_to_le16(value&0xffff);
+ } else {
+ int offset = (cluster*3/2);
+ unsigned char* p = array_get(&(s->fat), offset);
+ switch (cluster&1) {
+ case 0:
+ p[0] = value&0xff;
+ p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
+ break;
+ case 1:
+ p[0] = (p[0]&0xf) | ((value&0xf)<<4);
+ p[1] = (value>>4);
+ break;
+ }
+ }
+}
+
+static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
+{
+ if(s->fat_type==32) {
+ uint32_t* entry=array_get(&(s->fat),cluster);
+ return le32_to_cpu(*entry);
+ } else if(s->fat_type==16) {
+ uint16_t* entry=array_get(&(s->fat),cluster);
+ return le16_to_cpu(*entry);
+ } else {
+ const uint8_t* x=s->fat.pointer+cluster*3/2;
+ return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+ }
+}
+
+static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
+{
+ if(fat_entry>s->max_fat_value-8)
+ return -1;
+ return 0;
+}
+
+static inline void init_fat(BDRVVVFATState* s)
+{
+ if (s->fat_type == 12) {
+ array_init(&(s->fat),1);
+ array_ensure_allocated(&(s->fat),
+ s->sectors_per_fat * 0x200 * 3 / 2 - 1);
+ } else {
+ array_init(&(s->fat),(s->fat_type==32?4:2));
+ array_ensure_allocated(&(s->fat),
+ s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
+ }
+ memset(s->fat.pointer,0,s->fat.size);
+
+ switch(s->fat_type) {
+ case 12: s->max_fat_value=0xfff; break;
+ case 16: s->max_fat_value=0xffff; break;
+ case 32: s->max_fat_value=0x0fffffff; break;
+ default: s->max_fat_value=0; /* error... */
+ }
+
+}
+
+/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
+/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
+static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
+ unsigned int directory_start, const char* filename, int is_dot)
+{
+ int i,j,long_index=s->directory.next;
+ direntry_t* entry=0;
+ direntry_t* entry_long=0;
+
+ if(is_dot) {
+ entry=array_get_next(&(s->directory));
+ memset(entry->name,0x20,11);
+ memcpy(entry->name,filename,strlen(filename));
+ return entry;
+ }
+
+ entry_long=create_long_filename(s,filename);
+
+ i = strlen(filename);
+ for(j = i - 1; j>0 && filename[j]!='.';j--);
+ if (j > 0)
+ i = (j > 8 ? 8 : j);
+ else if (i > 8)
+ i = 8;
+
+ entry=array_get_next(&(s->directory));
+ memset(entry->name,0x20,11);
+ strncpy(entry->name,filename,i);
+
+ if(j > 0)
+ for (i = 0; i < 3 && filename[j+1+i]; i++)
+ entry->extension[i] = filename[j+1+i];
+
+ /* upcase & remove unwanted characters */
+ for(i=10;i>=0;i--) {
+ if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
+ if(entry->name[i]<=' ' || entry->name[i]>0x7f
+ || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
+ entry->name[i]='_';
+ else if(entry->name[i]>='a' && entry->name[i]<='z')
+ entry->name[i]+='A'-'a';
+ }
+
+ /* mangle duplicates */
+ while(1) {
+ direntry_t* entry1=array_get(&(s->directory),directory_start);
+ int j;
+
+ for(;entry1<entry;entry1++)
+ if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
+ break; /* found dupe */
+ if(entry1==entry) /* no dupe found */
+ break;
+
+ /* use all 8 characters of name */
+ if(entry->name[7]==' ') {
+ int j;
+ for(j=6;j>0 && entry->name[j]==' ';j--)
+ entry->name[j]='~';
+ }
+
+ /* increment number */
+ for(j=7;j>0 && entry->name[j]=='9';j--)
+ entry->name[j]='0';
+ if(j>0) {
+ if(entry->name[j]<'0' || entry->name[j]>'9')
+ entry->name[j]='0';
+ else
+ entry->name[j]++;
+ }
+ }
+
+ /* calculate checksum; propagate to long name */
+ if(entry_long) {
+ uint8_t chksum=fat_chksum(entry);
+
+ /* calculate anew, because realloc could have taken place */
+ entry_long=array_get(&(s->directory),long_index);
+ while(entry_long<entry && is_long_name(entry_long)) {
+ entry_long->reserved[1]=chksum;
+ entry_long++;
+ }
+ }
+
+ return entry;
+}
+
+/*
+ * Read a directory. (the index of the corresponding mapping must be passed).
+ */
+static int read_directory(BDRVVVFATState* s, int mapping_index)
+{
+ mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+ direntry_t* direntry;
+ const char* dirname = mapping->path;
+ int first_cluster = mapping->begin;
+ int parent_index = mapping->info.dir.parent_mapping_index;
+ mapping_t* parent_mapping = (mapping_t*)
+ (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
+ int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
+
+ DIR* dir=opendir(dirname);
+ struct dirent* entry;
+ int i;
+
+ assert(mapping->mode & MODE_DIRECTORY);
+
+ if(!dir) {
+ mapping->end = mapping->begin;
+ return -1;
+ }
+
+ i = mapping->info.dir.first_dir_index =
+ first_cluster == 0 ? 0 : s->directory.next;
+
+ /* actually read the directory, and allocate the mappings */
+ while((entry=readdir(dir))) {
+ unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
+ char* buffer;
+ direntry_t* direntry;
+ struct stat st;
+ int is_dot=!strcmp(entry->d_name,".");
+ int is_dotdot=!strcmp(entry->d_name,"..");
+
+ if(first_cluster == 0 && (is_dotdot || is_dot))
+ continue;
+
+ buffer=(char*)malloc(length);
+ assert(buffer);
+ snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
+
+ if(stat(buffer,&st)<0) {
+ free(buffer);
+ continue;
+ }
+
+ /* create directory entry for this file */
+ direntry=create_short_and_long_name(s, i, entry->d_name,
+ is_dot || is_dotdot);
+ direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
+ direntry->reserved[0]=direntry->reserved[1]=0;
+ direntry->ctime=fat_datetime(st.st_ctime,1);
+ direntry->cdate=fat_datetime(st.st_ctime,0);
+ direntry->adate=fat_datetime(st.st_atime,0);
+ direntry->begin_hi=0;
+ direntry->mtime=fat_datetime(st.st_mtime,1);
+ direntry->mdate=fat_datetime(st.st_mtime,0);
+ if(is_dotdot)
+ set_begin_of_direntry(direntry, first_cluster_of_parent);
+ else if(is_dot)
+ set_begin_of_direntry(direntry, first_cluster);
+ else
+ direntry->begin=0; /* do that later */
+ if (st.st_size > 0x7fffffff) {
+ fprintf(stderr, "File %s is larger than 2GB\n", buffer);
+ free(buffer);
+ return -2;
+ }
+ direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
+
+ /* create mapping for this file */
+ if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
+ s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
+ s->current_mapping->begin=0;
+ s->current_mapping->end=st.st_size;
+ /*
+ * we get the direntry of the most recent direntry, which
+ * contains the short name and all the relevant information.
+ */
+ s->current_mapping->dir_index=s->directory.next-1;
+ s->current_mapping->first_mapping_index = -1;
+ if (S_ISDIR(st.st_mode)) {
+ s->current_mapping->mode = MODE_DIRECTORY;
+ s->current_mapping->info.dir.parent_mapping_index =
+ mapping_index;
+ } else {
+ s->current_mapping->mode = MODE_UNDEFINED;
+ s->current_mapping->info.file.offset = 0;
+ }
+ s->current_mapping->path=buffer;
+ s->current_mapping->read_only =
+ (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
+ }
+ }
+ closedir(dir);
+
+ /* fill with zeroes up to the end of the cluster */
+ while(s->directory.next%(0x10*s->sectors_per_cluster)) {
+ direntry_t* direntry=array_get_next(&(s->directory));
+ memset(direntry,0,sizeof(direntry_t));
+ }
+
+/* TODO: if there are more entries, bootsector has to be adjusted! */
+#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
+ if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
+ /* root directory */
+ int cur = s->directory.next;
+ array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
+ memset(array_get(&(s->directory), cur), 0,
+ (ROOT_ENTRIES - cur) * sizeof(direntry_t));
+ }
+
+ /* reget the mapping, since s->mapping was possibly realloc()ed */
+ mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
+ first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
+ * 0x20 / s->cluster_size;
+ mapping->end = first_cluster;
+
+ direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
+ set_begin_of_direntry(direntry, mapping->begin);
+
+ return 0;
+}
+
+static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
+{
+ return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+}
+
+static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
+{
+ return s->faked_sectors + s->sectors_per_cluster * cluster_num;
+}
+
+static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
+{
+ return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
+}
+
+#ifdef DBG
+static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
+{
+ if(mapping->mode==MODE_UNDEFINED)
+ return 0;
+ return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
+}
+#endif
+
+static int init_directories(BDRVVVFATState* s,
+ const char* dirname)
+{
+ bootsector_t* bootsector;
+ mapping_t* mapping;
+ unsigned int i;
+ unsigned int cluster;
+
+ memset(&(s->first_sectors[0]),0,0x40*0x200);
+
+ s->cluster_size=s->sectors_per_cluster*0x200;
+ s->cluster_buffer=malloc(s->cluster_size);
+ assert(s->cluster_buffer);
+
+ /*
+ * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
+ * where sc is sector_count,
+ * spf is sectors_per_fat,
+ * spc is sectors_per_clusters, and
+ * fat_type = 12, 16 or 32.
+ */
+ i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
+ s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
+
+ array_init(&(s->mapping),sizeof(mapping_t));
+ array_init(&(s->directory),sizeof(direntry_t));
+
+ /* add volume label */
+ {
+ direntry_t* entry=array_get_next(&(s->directory));
+ entry->attributes=0x28; /* archive | volume label */
+ snprintf(entry->name,11,"QEMU VVFAT");
+ }
+
+ /* Now build FAT, and write back information into directory */
+ init_fat(s);
+
+ s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
+ s->cluster_count=sector2cluster(s, s->sector_count);
+
+ mapping = array_get_next(&(s->mapping));
+ mapping->begin = 0;
+ mapping->dir_index = 0;
+ mapping->info.dir.parent_mapping_index = -1;
+ mapping->first_mapping_index = -1;
+ mapping->path = strdup(dirname);
+ i = strlen(mapping->path);
+ if (i > 0 && mapping->path[i - 1] == '/')
+ mapping->path[i - 1] = '\0';
+ mapping->mode = MODE_DIRECTORY;
+ mapping->read_only = 0;
+ s->path = mapping->path;
+
+ for (i = 0, cluster = 0; i < s->mapping.next; i++) {
+ int j;
+ /* MS-DOS expects the FAT to be 0 for the root directory
+ * (except for the media byte). */
+ /* LATER TODO: still true for FAT32? */
+ int fix_fat = (i != 0);
+ mapping = array_get(&(s->mapping), i);
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ mapping->begin = cluster;
+ if(read_directory(s, i)) {
+ fprintf(stderr, "Could not read directory %s\n",
+ mapping->path);
+ return -1;
+ }
+ mapping = array_get(&(s->mapping), i);
+ } else {
+ assert(mapping->mode == MODE_UNDEFINED);
+ mapping->mode=MODE_NORMAL;
+ mapping->begin = cluster;
+ if (mapping->end > 0) {
+ direntry_t* direntry = array_get(&(s->directory),
+ mapping->dir_index);
+
+ mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
+ set_begin_of_direntry(direntry, mapping->begin);
+ } else {
+ mapping->end = cluster + 1;
+ fix_fat = 0;
+ }
+ }
+
+ assert(mapping->begin < mapping->end);
+
+ /* fix fat for entry */
+ if (fix_fat) {
+ for(j = mapping->begin; j < mapping->end - 1; j++)
+ fat_set(s, j, j+1);
+ fat_set(s, mapping->end - 1, s->max_fat_value);
+ }
+
+ /* next free cluster */
+ cluster = mapping->end;
+
+ if(cluster > s->cluster_count) {
+ fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
+ return -1;
+ }
+ }
+
+ mapping = array_get(&(s->mapping), 0);
+ s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
+ s->last_cluster_of_root_directory = mapping->end;
+
+ /* the FAT signature */
+ fat_set(s,0,s->max_fat_value);
+ fat_set(s,1,s->max_fat_value);
+
+ s->current_mapping = NULL;
+
+ bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
+ bootsector->jump[0]=0xeb;
+ bootsector->jump[1]=0x3e;
+ bootsector->jump[2]=0x90;
+ memcpy(bootsector->name,"QEMU ",8);
+ bootsector->sector_size=cpu_to_le16(0x200);
+ bootsector->sectors_per_cluster=s->sectors_per_cluster;
+ bootsector->reserved_sectors=cpu_to_le16(1);
+ bootsector->number_of_fats=0x2; /* number of FATs */
+ bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
+ bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
+ bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
+ s->fat.pointer[0] = bootsector->media_type;
+ bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
+ bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
+ bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
+ bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
+ bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
+
+ /* LATER TODO: if FAT32, this is wrong */
+ bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
+ bootsector->u.fat16.current_head=0;
+ bootsector->u.fat16.signature=0x29;
+ bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
+
+ memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
+ memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
+ bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
+
+ return 0;
+}
+
+static BDRVVVFATState *vvv = NULL;
+
+static int enable_write_target(BDRVVVFATState *s);
+static int is_consistent(BDRVVVFATState *s);
+
+static int vvfat_open(BlockDriverState *bs, const char* dirname)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int floppy = 0;
+ int i;
+
+ vvv = s;
+
+DLOG(if (stderr == NULL) {
+ stderr = fopen("vvfat.log", "a");
+ setbuf(stderr, NULL);
+})
+
+ s->bs = bs;
+
+ s->fat_type=16;
+ /* LATER TODO: if FAT32, adjust */
+ s->sector_count=0xec04f;
+ s->sectors_per_cluster=0x10;
+ /* LATER TODO: this could be wrong for FAT32 */
+ bs->cyls=1023; bs->heads=15; bs->secs=63;
+
+ s->current_cluster=0xffffffff;
+
+ s->first_sectors_number=0x40;
+ /* read only is the default for safety */
+ bs->read_only = 1;
+ s->qcow = s->write_target = NULL;
+ s->qcow_filename = NULL;
+ s->fat2 = NULL;
+ s->downcase_short_names = 1;
+
+ if (!strstart(dirname, "fat:", NULL))
+ return -1;
+
+ if (strstr(dirname, ":rw:")) {
+ if (enable_write_target(s))
+ return -1;
+ bs->read_only = 0;
+ }
+
+ if (strstr(dirname, ":floppy:")) {
+ floppy = 1;
+ s->fat_type = 12;
+ s->first_sectors_number = 1;
+ s->sectors_per_cluster=2;
+ bs->cyls = 80; bs->heads = 2; bs->secs = 36;
+ }
+
+ if (strstr(dirname, ":32:")) {
+ fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
+ s->fat_type = 32;
+ } else if (strstr(dirname, ":16:")) {
+ s->fat_type = 16;
+ } else if (strstr(dirname, ":12:")) {
+ s->fat_type = 12;
+ s->sector_count=2880;
+ }
+
+ i = strrchr(dirname, ':') - dirname;
+ assert(i >= 3);
+ if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
+ /* workaround for DOS drive names */
+ dirname += i-1;
+ else
+ dirname += i+1;
+
+ bs->total_sectors=bs->cyls*bs->heads*bs->secs;
+ if (s->sector_count > bs->total_sectors)
+ s->sector_count = bs->total_sectors;
+ if(init_directories(s, dirname))
+ return -1;
+
+ if(s->first_sectors_number==0x40)
+ init_mbr(s);
+
+ /* for some reason or other, MS-DOS does not like to know about CHS... */
+ if (floppy)
+ bs->heads = bs->cyls = bs->secs = 0;
+
+ // assert(is_consistent(s));
+
+ return 0;
+}
+
+static inline void vvfat_close_current_file(BDRVVVFATState *s)
+{
+ if(s->current_mapping) {
+ s->current_mapping = NULL;
+ if (s->current_fd) {
+ close(s->current_fd);
+ s->current_fd = 0;
+ }
+ }
+ s->current_cluster = -1;
+}
+
+/* mappings between index1 and index2-1 are supposed to be ordered
+ * return value is the index of the last mapping for which end>cluster_num
+ */
+static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
+{
+ int index3=index1+1;
+ while(1) {
+ mapping_t* mapping;
+ index3=(index1+index2)/2;
+ mapping=array_get(&(s->mapping),index3);
+ assert(mapping->begin < mapping->end);
+ if(mapping->begin>=cluster_num) {
+ assert(index2!=index3 || index2==0);
+ if(index2==index3)
+ return index1;
+ index2=index3;
+ } else {
+ if(index1==index3)
+ return mapping->end<=cluster_num ? index2 : index1;
+ index1=index3;
+ }
+ assert(index1<=index2);
+ DLOG(mapping=array_get(&(s->mapping),index1);
+ assert(mapping->begin<=cluster_num);
+ assert(index2 >= s->mapping.next ||
+ ((mapping = array_get(&(s->mapping),index2)) &&
+ mapping->end>cluster_num)));
+ }
+}
+
+static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
+{
+ int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
+ mapping_t* mapping;
+ if(index>=s->mapping.next)
+ return 0;
+ mapping=array_get(&(s->mapping),index);
+ if(mapping->begin>cluster_num)
+ return 0;
+ assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
+ return mapping;
+}
+
+/*
+ * This function simply compares path == mapping->path. Since the mappings
+ * are sorted by cluster, this is expensive: O(n).
+ */
+static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
+ const char* path)
+{
+ int i;
+
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->first_mapping_index < 0 &&
+ !strcmp(path, mapping->path))
+ return mapping;
+ }
+
+ return NULL;
+}
+
+static int open_file(BDRVVVFATState* s,mapping_t* mapping)
+{
+ if(!mapping)
+ return -1;
+ if(!s->current_mapping ||
+ strcmp(s->current_mapping->path,mapping->path)) {
+ /* open file */
+ int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if(fd<0)
+ return -1;
+ vvfat_close_current_file(s);
+ s->current_fd = fd;
+ s->current_mapping = mapping;
+ }
+ return 0;
+}
+
+static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
+{
+ if(s->current_cluster != cluster_num) {
+ int result=0;
+ off_t offset;
+ assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
+ if(!s->current_mapping
+ || s->current_mapping->begin>cluster_num
+ || s->current_mapping->end<=cluster_num) {
+ /* binary search of mappings for file */
+ mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
+
+ assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
+
+ if (mapping && mapping->mode & MODE_DIRECTORY) {
+ vvfat_close_current_file(s);
+ s->current_mapping = mapping;
+read_cluster_directory:
+ offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
+ s->cluster = s->directory.pointer+offset
+ + 0x20*s->current_mapping->info.dir.first_dir_index;
+ assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
+ assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
+ s->current_cluster = cluster_num;
+ return 0;
+ }
+
+ if(open_file(s,mapping))
+ return -2;
+ } else if (s->current_mapping->mode & MODE_DIRECTORY)
+ goto read_cluster_directory;
+
+ assert(s->current_fd);
+
+ offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
+ if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
+ return -3;
+ s->cluster=s->cluster_buffer;
+ result=read(s->current_fd,s->cluster,s->cluster_size);
+ if(result<0) {
+ s->current_cluster = -1;
+ return -1;
+ }
+ s->current_cluster = cluster_num;
+ }
+ return 0;
+}
+
+#ifdef DEBUG
+static void hexdump(const void* address, uint32_t len)
+{
+ const unsigned char* p = address;
+ int i, j;
+
+ for (i = 0; i < len; i += 16) {
+ for (j = 0; j < 16 && i + j < len; j++)
+ fprintf(stderr, "%02x ", p[i + j]);
+ for (; j < 16; j++)
+ fprintf(stderr, " ");
+ fprintf(stderr, " ");
+ for (j = 0; j < 16 && i + j < len; j++)
+ fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
+ fprintf(stderr, "\n");
+ }
+}
+
+static void print_direntry(const direntry_t* direntry)
+{
+ int j = 0;
+ char buffer[1024];
+
+ fprintf(stderr, "direntry 0x%x: ", (int)direntry);
+ if(!direntry)
+ return;
+ if(is_long_name(direntry)) {
+ unsigned char* c=(unsigned char*)direntry;
+ int i;
+ for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
+#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
+ ADD_CHAR(c[i]);
+ for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
+ ADD_CHAR(c[i]);
+ for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
+ ADD_CHAR(c[i]);
+ buffer[j] = 0;
+ fprintf(stderr, "%s\n", buffer);
+ } else {
+ int i;
+ for(i=0;i<11;i++)
+ ADD_CHAR(direntry->name[i]);
+ buffer[j] = 0;
+ fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
+ buffer,
+ direntry->attributes,
+ begin_of_direntry(direntry),le32_to_cpu(direntry->size));
+ }
+}
+
+static void print_mapping(const mapping_t* mapping)
+{
+ fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode);
+ if (mapping->mode & MODE_DIRECTORY)
+ fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
+ else
+ fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
+}
+#endif
+
+static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++,sector_num++) {
+ if (sector_num >= s->sector_count)
+ return -1;
+ if (s->qcow) {
+ int n;
+ if (s->qcow->drv->bdrv_is_allocated(s->qcow,
+ sector_num, nb_sectors-i, &n)) {
+DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
+ if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
+ return -1;
+ i += n - 1;
+ sector_num += n - 1;
+ continue;
+ }
+DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
+ }
+ if(sector_num<s->faked_sectors) {
+ if(sector_num<s->first_sectors_number)
+ memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
+ else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
+ memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
+ else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
+ memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
+ } else {
+ uint32_t sector=sector_num-s->faked_sectors,
+ sector_offset_in_cluster=(sector%s->sectors_per_cluster),
+ cluster_num=sector/s->sectors_per_cluster;
+ if(read_cluster(s, cluster_num) != 0) {
+ /* LATER TODO: strict: return -1; */
+ memset(buf+i*0x200,0,0x200);
+ continue;
+ }
+ memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
+ }
+ }
+ return 0;
+}
+
+/* LATER TODO: statify all functions */
+
+/*
+ * Idea of the write support (use snapshot):
+ *
+ * 1. check if all data is consistent, recording renames, modifications,
+ * new files and directories (in s->commits).
+ *
+ * 2. if the data is not consistent, stop committing
+ *
+ * 3. handle renames, and create new files and directories (do not yet
+ * write their contents)
+ *
+ * 4. walk the directories, fixing the mapping and direntries, and marking
+ * the handled mappings as not deleted
+ *
+ * 5. commit the contents of the files
+ *
+ * 6. handle deleted files and directories
+ *
+ */
+
+typedef struct commit_t {
+ char* path;
+ union {
+ struct { uint32_t cluster; } rename;
+ struct { int dir_index; uint32_t modified_offset; } writeout;
+ struct { uint32_t first_cluster; } new_file;
+ struct { uint32_t cluster; } mkdir;
+ } param;
+ /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
+ enum {
+ ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
+ } action;
+} commit_t;
+
+static void clear_commits(BDRVVVFATState* s)
+{
+ int i;
+DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
+ for (i = 0; i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ assert(commit->path || commit->action == ACTION_WRITEOUT);
+ if (commit->action != ACTION_WRITEOUT) {
+ assert(commit->path);
+ free(commit->path);
+ } else
+ assert(commit->path == NULL);
+ }
+ s->commits.next = 0;
+}
+
+static void schedule_rename(BDRVVVFATState* s,
+ uint32_t cluster, char* new_path)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = new_path;
+ commit->param.rename.cluster = cluster;
+ commit->action = ACTION_RENAME;
+}
+
+static void schedule_writeout(BDRVVVFATState* s,
+ int dir_index, uint32_t modified_offset)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = NULL;
+ commit->param.writeout.dir_index = dir_index;
+ commit->param.writeout.modified_offset = modified_offset;
+ commit->action = ACTION_WRITEOUT;
+}
+
+static void schedule_new_file(BDRVVVFATState* s,
+ char* path, uint32_t first_cluster)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = path;
+ commit->param.new_file.first_cluster = first_cluster;
+ commit->action = ACTION_NEW_FILE;
+}
+
+static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = path;
+ commit->param.mkdir.cluster = cluster;
+ commit->action = ACTION_MKDIR;
+}
+
+typedef struct {
+ unsigned char name[1024];
+ int checksum, len;
+ int sequence_number;
+} long_file_name;
+
+static void lfn_init(long_file_name* lfn)
+{
+ lfn->sequence_number = lfn->len = 0;
+ lfn->checksum = 0x100;
+}
+
+/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
+static int parse_long_name(long_file_name* lfn,
+ const direntry_t* direntry)
+{
+ int i, j, offset;
+ const unsigned char* pointer = (const unsigned char*)direntry;
+
+ if (!is_long_name(direntry))
+ return 1;
+
+ if (pointer[0] & 0x40) {
+ lfn->sequence_number = pointer[0] & 0x3f;
+ lfn->checksum = pointer[13];
+ lfn->name[0] = 0;
+ } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
+ return -1;
+ else if (pointer[13] != lfn->checksum)
+ return -2;
+ else if (pointer[12] || pointer[26] || pointer[27])
+ return -3;
+
+ offset = 13 * (lfn->sequence_number - 1);
+ for (i = 0, j = 1; i < 13; i++, j+=2) {
+ if (j == 11)
+ j = 14;
+ else if (j == 26)
+ j = 28;
+
+ if (pointer[j+1] == 0)
+ lfn->name[offset + i] = pointer[j];
+ else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
+ return -4;
+ else
+ lfn->name[offset + i] = 0;
+ }
+
+ if (pointer[0] & 0x40)
+ lfn->len = offset + strlen(lfn->name + offset);
+
+ return 0;
+}
+
+/* returns 0 if successful, >0 if no short_name, and <0 on error */
+static int parse_short_name(BDRVVVFATState* s,
+ long_file_name* lfn, direntry_t* direntry)
+{
+ int i, j;
+
+ if (!is_short_name(direntry))
+ return 1;
+
+ for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
+ for (i = 0; i <= j; i++) {
+ if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
+ return -1;
+ else if (s->downcase_short_names)
+ lfn->name[i] = tolower(direntry->name[i]);
+ else
+ lfn->name[i] = direntry->name[i];
+ }
+
+ for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
+ if (j >= 0) {
+ lfn->name[i++] = '.';
+ lfn->name[i + j + 1] = '\0';
+ for (;j >= 0; j--) {
+ if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
+ return -2;
+ else if (s->downcase_short_names)
+ lfn->name[i + j] = tolower(direntry->extension[j]);
+ else
+ lfn->name[i + j] = direntry->extension[j];
+ }
+ } else
+ lfn->name[i + j + 1] = '\0';
+
+ lfn->len = strlen(lfn->name);
+
+ return 0;
+}
+
+static inline uint32_t modified_fat_get(BDRVVVFATState* s,
+ unsigned int cluster)
+{
+ if (cluster < s->last_cluster_of_root_directory) {
+ if (cluster + 1 == s->last_cluster_of_root_directory)
+ return s->max_fat_value;
+ else
+ return cluster + 1;
+ }
+
+ if (s->fat_type==32) {
+ uint32_t* entry=((uint32_t*)s->fat2)+cluster;
+ return le32_to_cpu(*entry);
+ } else if (s->fat_type==16) {
+ uint16_t* entry=((uint16_t*)s->fat2)+cluster;
+ return le16_to_cpu(*entry);
+ } else {
+ const uint8_t* x=s->fat2+cluster*3/2;
+ return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+ }
+}
+
+static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
+{
+ int was_modified = 0;
+ int i, dummy;
+
+ if (s->qcow == NULL)
+ return 0;
+
+ for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
+ was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
+ cluster2sector(s, cluster_num) + i, 1, &dummy);
+
+ return was_modified;
+}
+
+static const char* get_basename(const char* path)
+{
+ char* basename = strrchr(path, '/');
+ if (basename == NULL)
+ return path;
+ else
+ return basename + 1; /* strip '/' */
+}
+
+/*
+ * The array s->used_clusters holds the states of the clusters. If it is
+ * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
+ * was modified, bit 3 is set.
+ * If any cluster is allocated, but not part of a file or directory, this
+ * driver refuses to commit.
+ */
+typedef enum {
+ USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
+} used_t;
+
+/*
+ * get_cluster_count_for_direntry() not only determines how many clusters
+ * are occupied by direntry, but also if it was renamed or modified.
+ *
+ * A file is thought to be renamed *only* if there already was a file with
+ * exactly the same first cluster, but a different name.
+ *
+ * Further, the files/directories handled by this function are
+ * assumed to be *not* deleted (and *only* those).
+ */
+static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
+ direntry_t* direntry, const char* path)
+{
+ /*
+ * This is a little bit tricky:
+ * IF the guest OS just inserts a cluster into the file chain,
+ * and leaves the rest alone, (i.e. the original file had clusters
+ * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
+ *
+ * - do_commit will write the cluster into the file at the given
+ * offset, but
+ *
+ * - the cluster which is overwritten should be moved to a later
+ * position in the file.
+ *
+ * I am not aware that any OS does something as braindead, but this
+ * situation could happen anyway when not committing for a long time.
+ * Just to be sure that this does not bite us, detect it, and copy the
+ * contents of the clusters to-be-overwritten into the qcow.
+ */
+ int copy_it = 0;
+ int was_modified = 0;
+ int32_t ret = 0;
+
+ uint32_t cluster_num = begin_of_direntry(direntry);
+ uint32_t offset = 0;
+ int first_mapping_index = -1;
+ mapping_t* mapping = NULL;
+ const char* basename2 = NULL;
+
+ vvfat_close_current_file(s);
+
+ /* the root directory */
+ if (cluster_num == 0)
+ return 0;
+
+ /* write support */
+ if (s->qcow) {
+ basename2 = get_basename(path);
+
+ mapping = find_mapping_for_cluster(s, cluster_num);
+
+ if (mapping) {
+ const char* basename;
+
+ assert(mapping->mode & MODE_DELETED);
+ mapping->mode &= ~MODE_DELETED;
+
+ basename = get_basename(mapping->path);
+
+ assert(mapping->mode & MODE_NORMAL);
+
+ /* rename */
+ if (strcmp(basename, basename2))
+ schedule_rename(s, cluster_num, strdup(path));
+ } else if (is_file(direntry))
+ /* new file */
+ schedule_new_file(s, strdup(path), cluster_num);
+ else {
+ assert(0);
+ return 0;
+ }
+ }
+
+ while(1) {
+ if (s->qcow) {
+ if (!copy_it && cluster_was_modified(s, cluster_num)) {
+ if (mapping == NULL ||
+ mapping->begin > cluster_num ||
+ mapping->end <= cluster_num)
+ mapping = find_mapping_for_cluster(s, cluster_num);
+
+
+ if (mapping &&
+ (mapping->mode & MODE_DIRECTORY) == 0) {
+
+ /* was modified in qcow */
+ if (offset != mapping->info.file.offset + s->cluster_size
+ * (cluster_num - mapping->begin)) {
+ /* offset of this cluster in file chain has changed */
+ assert(0);
+ copy_it = 1;
+ } else if (offset == 0) {
+ const char* basename = get_basename(mapping->path);
+
+ if (strcmp(basename, basename2))
+ copy_it = 1;
+ first_mapping_index = array_index(&(s->mapping), mapping);
+ }
+
+ if (mapping->first_mapping_index != first_mapping_index
+ && mapping->info.file.offset > 0) {
+ assert(0);
+ copy_it = 1;
+ }
+
+ /* need to write out? */
+ if (!was_modified && is_file(direntry)) {
+ was_modified = 1;
+ schedule_writeout(s, mapping->dir_index, offset);
+ }
+ }
+ }
+
+ if (copy_it) {
+ int i, dummy;
+ /*
+ * This is horribly inefficient, but that is okay, since
+ * it is rarely executed, if at all.
+ */
+ int64_t offset = cluster2sector(s, cluster_num);
+
+ vvfat_close_current_file(s);
+ for (i = 0; i < s->sectors_per_cluster; i++)
+ if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
+ offset + i, 1, &dummy)) {
+ if (vvfat_read(s->bs,
+ offset, s->cluster_buffer, 1))
+ return -1;
+ if (s->qcow->drv->bdrv_write(s->qcow,
+ offset, s->cluster_buffer, 1))
+ return -2;
+ }
+ }
+ }
+
+ ret++;
+ if (s->used_clusters[cluster_num] & USED_ANY)
+ return 0;
+ s->used_clusters[cluster_num] = USED_FILE;
+
+ cluster_num = modified_fat_get(s, cluster_num);
+
+ if (fat_eof(s, cluster_num))
+ return ret;
+ else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
+ return -1;
+
+ offset += s->cluster_size;
+ }
+}
+
+/*
+ * This function looks at the modified data (qcow).
+ * It returns 0 upon inconsistency or error, and the number of clusters
+ * used by the directory, its subdirectories and their files.
+ */
+static int check_directory_consistency(BDRVVVFATState *s,
+ int cluster_num, const char* path)
+{
+ int ret = 0;
+ unsigned char* cluster = malloc(s->cluster_size);
+ direntry_t* direntries = (direntry_t*)cluster;
+ mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
+
+ long_file_name lfn;
+ int path_len = strlen(path);
+ char path2[PATH_MAX];
+
+ assert(path_len < PATH_MAX); /* len was tested before! */
+ strcpy(path2, path);
+ path2[path_len] = '/';
+ path2[path_len + 1] = '\0';
+
+ if (mapping) {
+ const char* basename = get_basename(mapping->path);
+ const char* basename2 = get_basename(path);
+
+ assert(mapping->mode & MODE_DIRECTORY);
+
+ assert(mapping->mode & MODE_DELETED);
+ mapping->mode &= ~MODE_DELETED;
+
+ if (strcmp(basename, basename2))
+ schedule_rename(s, cluster_num, strdup(path));
+ } else
+ /* new directory */
+ schedule_mkdir(s, cluster_num, strdup(path));
+
+ lfn_init(&lfn);
+ do {
+ int i;
+ int subret = 0;
+
+ ret++;
+
+ if (s->used_clusters[cluster_num] & USED_ANY) {
+ fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
+ return 0;
+ }
+ s->used_clusters[cluster_num] = USED_DIRECTORY;
+
+DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
+ subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
+ s->sectors_per_cluster);
+ if (subret) {
+ fprintf(stderr, "Error fetching direntries\n");
+ fail:
+ free(cluster);
+ return 0;
+ }
+
+ for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
+ int cluster_count;
+
+DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
+ if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
+ is_free(direntries + i))
+ continue;
+
+ subret = parse_long_name(&lfn, direntries + i);
+ if (subret < 0) {
+ fprintf(stderr, "Error in long name\n");
+ goto fail;
+ }
+ if (subret == 0 || is_free(direntries + i))
+ continue;
+
+ if (fat_chksum(direntries+i) != lfn.checksum) {
+ subret = parse_short_name(s, &lfn, direntries + i);
+ if (subret < 0) {
+ fprintf(stderr, "Error in short name (%d)\n", subret);
+ goto fail;
+ }
+ if (subret > 0 || !strcmp(lfn.name, ".")
+ || !strcmp(lfn.name, ".."))
+ continue;
+ }
+ lfn.checksum = 0x100; /* cannot use long name twice */
+
+ if (path_len + 1 + lfn.len >= PATH_MAX) {
+ fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
+ goto fail;
+ }
+ strcpy(path2 + path_len + 1, lfn.name);
+
+ if (is_directory(direntries + i)) {
+ if (begin_of_direntry(direntries + i) == 0) {
+ DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
+ goto fail;
+ }
+ cluster_count = check_directory_consistency(s,
+ begin_of_direntry(direntries + i), path2);
+ if (cluster_count == 0) {
+ DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
+ goto fail;
+ }
+ } else if (is_file(direntries + i)) {
+ /* check file size with FAT */
+ cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
+ if (cluster_count !=
+ (le32_to_cpu(direntries[i].size) + s->cluster_size
+ - 1) / s->cluster_size) {
+ DLOG(fprintf(stderr, "Cluster count mismatch\n"));
+ goto fail;
+ }
+ } else
+ assert(0); /* cluster_count = 0; */
+
+ ret += cluster_count;
+ }
+
+ cluster_num = modified_fat_get(s, cluster_num);
+ } while(!fat_eof(s, cluster_num));
+
+ free(cluster);
+ return ret;
+}
+
+/* returns 1 on success */
+static int is_consistent(BDRVVVFATState* s)
+{
+ int i, check;
+ int used_clusters_count = 0;
+
+DLOG(checkpoint());
+ /*
+ * - get modified FAT
+ * - compare the two FATs (TODO)
+ * - get buffer for marking used clusters
+ * - recurse direntries from root (using bs->bdrv_read to make
+ * sure to get the new data)
+ * - check that the FAT agrees with the size
+ * - count the number of clusters occupied by this directory and
+ * its files
+ * - check that the cumulative used cluster count agrees with the
+ * FAT
+ * - if all is fine, return number of used clusters
+ */
+ if (s->fat2 == NULL) {
+ int size = 0x200 * s->sectors_per_fat;
+ s->fat2 = malloc(size);
+ memcpy(s->fat2, s->fat.pointer, size);
+ }
+ check = vvfat_read(s->bs,
+ s->first_sectors_number, s->fat2, s->sectors_per_fat);
+ if (check) {
+ fprintf(stderr, "Could not copy fat\n");
+ return 0;
+ }
+ assert (s->used_clusters);
+ for (i = 0; i < sector2cluster(s, s->sector_count); i++)
+ s->used_clusters[i] &= ~USED_ANY;
+
+ clear_commits(s);
+
+ /* mark every mapped file/directory as deleted.
+ * (check_directory_consistency() will unmark those still present). */
+ if (s->qcow)
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->first_mapping_index < 0)
+ mapping->mode |= MODE_DELETED;
+ }
+
+ used_clusters_count = check_directory_consistency(s, 0, s->path);
+ if (used_clusters_count <= 0) {
+ DLOG(fprintf(stderr, "problem in directory\n"));
+ return 0;
+ }
+
+ check = s->last_cluster_of_root_directory;
+ for (i = check; i < sector2cluster(s, s->sector_count); i++) {
+ if (modified_fat_get(s, i)) {
+ if(!s->used_clusters[i]) {
+ DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
+ return 0;
+ }
+ check++;
+ }
+
+ if (s->used_clusters[i] == USED_ALLOCATED) {
+ /* allocated, but not used... */
+ DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
+ return 0;
+ }
+ }
+
+ if (check != used_clusters_count)
+ return 0;
+
+ return used_clusters_count;
+}
+
+static inline void adjust_mapping_indices(BDRVVVFATState* s,
+ int offset, int adjust)
+{
+ int i;
+
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+
+#define ADJUST_MAPPING_INDEX(name) \
+ if (mapping->name >= offset) \
+ mapping->name += adjust
+
+ ADJUST_MAPPING_INDEX(first_mapping_index);
+ if (mapping->mode & MODE_DIRECTORY)
+ ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
+ }
+}
+
+/* insert or update mapping */
+static mapping_t* insert_mapping(BDRVVVFATState* s,
+ uint32_t begin, uint32_t end)
+{
+ /*
+ * - find mapping where mapping->begin >= begin,
+ * - if mapping->begin > begin: insert
+ * - adjust all references to mappings!
+ * - else: adjust
+ * - replace name
+ */
+ int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
+ mapping_t* mapping = NULL;
+ mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+ if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
+ && mapping->begin < begin) {
+ mapping->end = begin;
+ index++;
+ mapping = array_get(&(s->mapping), index);
+ }
+ if (index >= s->mapping.next || mapping->begin > begin) {
+ mapping = array_insert(&(s->mapping), index, 1);
+ mapping->path = NULL;
+ adjust_mapping_indices(s, index, +1);
+ }
+
+ mapping->begin = begin;
+ mapping->end = end;
+
+DLOG(mapping_t* next_mapping;
+assert(index + 1 >= s->mapping.next ||
+((next_mapping = array_get(&(s->mapping), index + 1)) &&
+ next_mapping->begin >= end)));
+
+ if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+ s->current_mapping = array_get(&(s->mapping),
+ s->current_mapping - first_mapping);
+
+ return mapping;
+}
+
+static int remove_mapping(BDRVVVFATState* s, int mapping_index)
+{
+ mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+ mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+ /* free mapping */
+ if (mapping->first_mapping_index < 0)
+ free(mapping->path);
+
+ /* remove from s->mapping */
+ array_remove(&(s->mapping), mapping_index);
+
+ /* adjust all references to mappings */
+ adjust_mapping_indices(s, mapping_index, -1);
+
+ if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+ s->current_mapping = array_get(&(s->mapping),
+ s->current_mapping - first_mapping);
+
+ return 0;
+}
+
+static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
+{
+ int i;
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->dir_index >= offset)
+ mapping->dir_index += adjust;
+ if ((mapping->mode & MODE_DIRECTORY) &&
+ mapping->info.dir.first_dir_index >= offset)
+ mapping->info.dir.first_dir_index += adjust;
+ }
+}
+
+static direntry_t* insert_direntries(BDRVVVFATState* s,
+ int dir_index, int count)
+{
+ /*
+ * make room in s->directory,
+ * adjust_dirindices
+ */
+ direntry_t* result = array_insert(&(s->directory), dir_index, count);
+ if (result == NULL)
+ return NULL;
+ adjust_dirindices(s, dir_index, count);
+ return result;
+}
+
+static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
+{
+ int ret = array_remove_slice(&(s->directory), dir_index, count);
+ if (ret)
+ return ret;
+ adjust_dirindices(s, dir_index, -count);
+ return 0;
+}
+
+/*
+ * Adapt the mappings of the cluster chain starting at first cluster
+ * (i.e. if a file starts at first_cluster, the chain is followed according
+ * to the modified fat, and the corresponding entries in s->mapping are
+ * adjusted)
+ */
+static int commit_mappings(BDRVVVFATState* s,
+ uint32_t first_cluster, int dir_index)
+{
+ mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t cluster = first_cluster;
+
+ vvfat_close_current_file(s);
+
+ assert(mapping);
+ assert(mapping->begin == first_cluster);
+ mapping->first_mapping_index = -1;
+ mapping->dir_index = dir_index;
+ mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
+ MODE_DIRECTORY : MODE_NORMAL;
+
+ while (!fat_eof(s, cluster)) {
+ uint32_t c, c1;
+
+ for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
+ c = c1, c1 = modified_fat_get(s, c1));
+
+ c++;
+ if (c > mapping->end) {
+ int index = array_index(&(s->mapping), mapping);
+ int i, max_i = s->mapping.next - index;
+ for (i = 1; i < max_i && mapping[i].begin < c; i++);
+ while (--i > 0)
+ remove_mapping(s, index + 1);
+ }
+ assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
+ || mapping[1].begin >= c);
+ mapping->end = c;
+
+ if (!fat_eof(s, c1)) {
+ int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
+ mapping_t* next_mapping = i >= s->mapping.next ? NULL :
+ array_get(&(s->mapping), i);
+
+ if (next_mapping == NULL || next_mapping->begin > c1) {
+ int i1 = array_index(&(s->mapping), mapping);
+
+ next_mapping = insert_mapping(s, c1, c1+1);
+
+ if (c1 < c)
+ i1++;
+ mapping = array_get(&(s->mapping), i1);
+ }
+
+ next_mapping->dir_index = mapping->dir_index;
+ next_mapping->first_mapping_index =
+ mapping->first_mapping_index < 0 ?
+ array_index(&(s->mapping), mapping) :
+ mapping->first_mapping_index;
+ next_mapping->path = mapping->path;
+ next_mapping->mode = mapping->mode;
+ next_mapping->read_only = mapping->read_only;
+ if (mapping->mode & MODE_DIRECTORY) {
+ next_mapping->info.dir.parent_mapping_index =
+ mapping->info.dir.parent_mapping_index;
+ next_mapping->info.dir.first_dir_index =
+ mapping->info.dir.first_dir_index +
+ 0x10 * s->sectors_per_cluster *
+ (mapping->end - mapping->begin);
+ } else
+ next_mapping->info.file.offset = mapping->info.file.offset +
+ mapping->end - mapping->begin;
+
+ mapping = next_mapping;
+ }
+
+ cluster = c1;
+ }
+
+ return 0;
+}
+
+static int commit_direntries(BDRVVVFATState* s,
+ int dir_index, int parent_mapping_index)
+{
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
+ mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+
+ int factor = 0x10 * s->sectors_per_cluster;
+ int old_cluster_count, new_cluster_count;
+ int current_dir_index = mapping->info.dir.first_dir_index;
+ int first_dir_index = current_dir_index;
+ int ret, i;
+ uint32_t c;
+
+DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
+
+ assert(direntry);
+ assert(mapping);
+ assert(mapping->begin == first_cluster);
+ assert(mapping->info.dir.first_dir_index < s->directory.next);
+ assert(mapping->mode & MODE_DIRECTORY);
+ assert(dir_index == 0 || is_directory(direntry));
+
+ mapping->info.dir.parent_mapping_index = parent_mapping_index;
+
+ if (first_cluster == 0) {
+ old_cluster_count = new_cluster_count =
+ s->last_cluster_of_root_directory;
+ } else {
+ for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+ c = fat_get(s, c))
+ old_cluster_count++;
+
+ for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+ c = modified_fat_get(s, c))
+ new_cluster_count++;
+ }
+
+ if (new_cluster_count > old_cluster_count) {
+ if (insert_direntries(s,
+ current_dir_index + factor * old_cluster_count,
+ factor * (new_cluster_count - old_cluster_count)) == NULL)
+ return -1;
+ } else if (new_cluster_count < old_cluster_count)
+ remove_direntries(s,
+ current_dir_index + factor * new_cluster_count,
+ factor * (old_cluster_count - new_cluster_count));
+
+ for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
+ void* direntry = array_get(&(s->directory), current_dir_index);
+ int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
+ s->sectors_per_cluster);
+ if (ret)
+ return ret;
+ assert(!strncmp(s->directory.pointer, "QEMU", 4));
+ current_dir_index += factor;
+ }
+
+ ret = commit_mappings(s, first_cluster, dir_index);
+ if (ret)
+ return ret;
+
+ /* recurse */
+ for (i = 0; i < factor * new_cluster_count; i++) {
+ direntry = array_get(&(s->directory), first_dir_index + i);
+ if (is_directory(direntry) && !is_dot(direntry)) {
+ mapping = find_mapping_for_cluster(s, first_cluster);
+ assert(mapping->mode & MODE_DIRECTORY);
+ ret = commit_direntries(s, first_dir_index + i,
+ array_index(&(s->mapping), mapping));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* commit one file (adjust contents, adjust mapping),
+ return first_mapping_index */
+static int commit_one_file(BDRVVVFATState* s,
+ int dir_index, uint32_t offset)
+{
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t c = begin_of_direntry(direntry);
+ uint32_t first_cluster = c;
+ mapping_t* mapping = find_mapping_for_cluster(s, c);
+ uint32_t size = filesize_of_direntry(direntry);
+ char* cluster = malloc(s->cluster_size);
+ uint32_t i;
+ int fd = 0;
+
+ assert(offset < size);
+ assert((offset % s->cluster_size) == 0);
+
+ for (i = s->cluster_size; i < offset; i += s->cluster_size)
+ c = modified_fat_get(s, c);
+
+ fd = open(mapping->path, O_RDWR | O_CREAT, 0666);
+ if (fd < 0) {
+ fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
+ strerror(errno), errno);
+ return fd;
+ }
+ if (offset > 0)
+ if (lseek(fd, offset, SEEK_SET) != offset)
+ return -3;
+
+ while (offset < size) {
+ uint32_t c1;
+ int rest_size = (size - offset > s->cluster_size ?
+ s->cluster_size : size - offset);
+ int ret;
+
+ c1 = modified_fat_get(s, c);
+
+ assert((size - offset == 0 && fat_eof(s, c)) ||
+ (size > offset && c >=2 && !fat_eof(s, c)));
+ assert(size >= 0);
+
+ ret = vvfat_read(s->bs, cluster2sector(s, c),
+ cluster, (rest_size + 0x1ff) / 0x200);
+
+ if (ret < 0)
+ return ret;
+
+ if (write(fd, cluster, rest_size) < 0)
+ return -2;
+
+ offset += rest_size;
+ c = c1;
+ }
+
+ ftruncate(fd, size);
+ close(fd);
+
+ return commit_mappings(s, first_cluster, dir_index);
+}
+
+#ifdef DEBUG
+/* test, if all mappings point to valid direntries */
+static void check1(BDRVVVFATState* s)
+{
+ int i;
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->mode & MODE_DELETED) {
+ fprintf(stderr, "deleted\n");
+ continue;
+ }
+ assert(mapping->dir_index >= 0);
+ assert(mapping->dir_index < s->directory.next);
+ direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
+ assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
+ if (mapping->mode & MODE_DIRECTORY) {
+ assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
+ assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
+ }
+ }
+}
+
+/* test, if all direntries have mappings */
+static void check2(BDRVVVFATState* s)
+{
+ int i;
+ int first_mapping = -1;
+
+ for (i = 0; i < s->directory.next; i++) {
+ direntry_t* direntry = array_get(&(s->directory), i);
+
+ if (is_short_name(direntry) && begin_of_direntry(direntry)) {
+ mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
+ assert(mapping);
+ assert(mapping->dir_index == i || is_dot(direntry));
+ assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
+ }
+
+ if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
+ /* cluster start */
+ int j, count = 0;
+
+ for (j = 0; j < s->mapping.next; j++) {
+ mapping_t* mapping = array_get(&(s->mapping), j);
+ if (mapping->mode & MODE_DELETED)
+ continue;
+ if (mapping->mode & MODE_DIRECTORY) {
+ if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
+ assert(++count == 1);
+ if (mapping->first_mapping_index == -1)
+ first_mapping = array_index(&(s->mapping), mapping);
+ else
+ assert(first_mapping == mapping->first_mapping_index);
+ if (mapping->info.dir.parent_mapping_index < 0)
+ assert(j == 0);
+ else {
+ mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
+ assert(parent->mode & MODE_DIRECTORY);
+ assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
+ }
+ }
+ }
+ }
+ if (count == 0)
+ first_mapping = -1;
+ }
+ }
+}
+#endif
+
+static int handle_renames_and_mkdirs(BDRVVVFATState* s)
+{
+ int i;
+
+#ifdef DEBUG
+ fprintf(stderr, "handle_renames\n");
+ for (i = 0; i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
+ }
+#endif
+
+ for (i = 0; i < s->commits.next;) {
+ commit_t* commit = array_get(&(s->commits), i);
+ if (commit->action == ACTION_RENAME) {
+ mapping_t* mapping = find_mapping_for_cluster(s,
+ commit->param.rename.cluster);
+ char* old_path = mapping->path;
+
+ assert(commit->path);
+ mapping->path = commit->path;
+ if (rename(old_path, mapping->path))
+ return -2;
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ int l1 = strlen(mapping->path);
+ int l2 = strlen(old_path);
+ int diff = l1 - l2;
+ direntry_t* direntry = array_get(&(s->directory),
+ mapping->info.dir.first_dir_index);
+ uint32_t c = mapping->begin;
+ int i = 0;
+
+ /* recurse */
+ while (!fat_eof(s, c)) {
+ do {
+ direntry_t* d = direntry + i;
+
+ if (is_file(d) || (is_directory(d) && !is_dot(d))) {
+ mapping_t* m = find_mapping_for_cluster(s,
+ begin_of_direntry(d));
+ int l = strlen(m->path);
+ char* new_path = malloc(l + diff + 1);
+
+ assert(!strncmp(m->path, mapping->path, l2));
+
+ strcpy(new_path, mapping->path);
+ strcpy(new_path + l1, m->path + l2);
+
+ schedule_rename(s, m->begin, new_path);
+ }
+ i++;
+ } while((i % (0x10 * s->sectors_per_cluster)) != 0);
+ c = fat_get(s, c);
+ }
+ }
+
+ free(old_path);
+ array_remove(&(s->commits), i);
+ continue;
+ } else if (commit->action == ACTION_MKDIR) {
+ mapping_t* mapping;
+ int j, parent_path_len;
+
+#ifdef __MINGW32__
+ if (mkdir(commit->path))
+ return -5;
+#else
+ if (mkdir(commit->path, 0755))
+ return -5;
+#endif
+
+ mapping = insert_mapping(s, commit->param.mkdir.cluster,
+ commit->param.mkdir.cluster + 1);
+ if (mapping == NULL)
+ return -6;
+
+ mapping->mode = MODE_DIRECTORY;
+ mapping->read_only = 0;
+ mapping->path = commit->path;
+ j = s->directory.next;
+ assert(j);
+ insert_direntries(s, s->directory.next,
+ 0x10 * s->sectors_per_cluster);
+ mapping->info.dir.first_dir_index = j;
+
+ parent_path_len = strlen(commit->path)
+ - strlen(get_basename(commit->path)) - 1;
+ for (j = 0; j < s->mapping.next; j++) {
+ mapping_t* m = array_get(&(s->mapping), j);
+ if (m->first_mapping_index < 0 && m != mapping &&
+ !strncmp(m->path, mapping->path, parent_path_len) &&
+ strlen(m->path) == parent_path_len)
+ break;
+ }
+ assert(j < s->mapping.next);
+ mapping->info.dir.parent_mapping_index = j;
+
+ array_remove(&(s->commits), i);
+ continue;
+ }
+
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * TODO: make sure that the short name is not matching *another* file
+ */
+static int handle_commits(BDRVVVFATState* s)
+{
+ int i, fail = 0;
+
+ vvfat_close_current_file(s);
+
+ for (i = 0; !fail && i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ switch(commit->action) {
+ case ACTION_RENAME: case ACTION_MKDIR:
+ assert(0);
+ fail = -2;
+ break;
+ case ACTION_WRITEOUT: {
+ direntry_t* entry = array_get(&(s->directory),
+ commit->param.writeout.dir_index);
+ uint32_t begin = begin_of_direntry(entry);
+ mapping_t* mapping = find_mapping_for_cluster(s, begin);
+
+ assert(mapping);
+ assert(mapping->begin == begin);
+ assert(commit->path == NULL);
+
+ if (commit_one_file(s, commit->param.writeout.dir_index,
+ commit->param.writeout.modified_offset))
+ fail = -3;
+
+ break;
+ }
+ case ACTION_NEW_FILE: {
+ int begin = commit->param.new_file.first_cluster;
+ mapping_t* mapping = find_mapping_for_cluster(s, begin);
+ direntry_t* entry;
+ int i;
+
+ /* find direntry */
+ for (i = 0; i < s->directory.next; i++) {
+ entry = array_get(&(s->directory), i);
+ if (is_file(entry) && begin_of_direntry(entry) == begin)
+ break;
+ }
+
+ if (i >= s->directory.next) {
+ fail = -6;
+ continue;
+ }
+
+ /* make sure there exists an initial mapping */
+ if (mapping && mapping->begin != begin) {
+ mapping->end = begin;
+ mapping = NULL;
+ }
+ if (mapping == NULL) {
+ mapping = insert_mapping(s, begin, begin+1);
+ }
+ /* most members will be fixed in commit_mappings() */
+ assert(commit->path);
+ mapping->path = commit->path;
+ mapping->read_only = 0;
+ mapping->mode = MODE_NORMAL;
+ mapping->info.file.offset = 0;
+
+ if (commit_one_file(s, i, 0))
+ fail = -7;
+
+ break;
+ }
+ default:
+ assert(0);
+ }
+ }
+ if (i > 0 && array_remove_slice(&(s->commits), 0, i))
+ return -1;
+ return fail;
+}
+
+static int handle_deletes(BDRVVVFATState* s)
+{
+ int i, deferred = 1, deleted = 1;
+
+ /* delete files corresponding to mappings marked as deleted */
+ /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
+ while (deferred && deleted) {
+ deferred = 0;
+ deleted = 0;
+
+ for (i = 1; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->mode & MODE_DELETED) {
+ direntry_t* entry = array_get(&(s->directory),
+ mapping->dir_index);
+
+ if (is_free(entry)) {
+ /* remove file/directory */
+ if (mapping->mode & MODE_DIRECTORY) {
+ int j, next_dir_index = s->directory.next,
+ first_dir_index = mapping->info.dir.first_dir_index;
+
+ if (rmdir(mapping->path) < 0) {
+ if (errno == ENOTEMPTY) {
+ deferred++;
+ continue;
+ } else
+ return -5;
+ }
+
+ for (j = 1; j < s->mapping.next; j++) {
+ mapping_t* m = array_get(&(s->mapping), j);
+ if (m->mode & MODE_DIRECTORY &&
+ m->info.dir.first_dir_index >
+ first_dir_index &&
+ m->info.dir.first_dir_index <
+ next_dir_index)
+ next_dir_index =
+ m->info.dir.first_dir_index;
+ }
+ remove_direntries(s, first_dir_index,
+ next_dir_index - first_dir_index);
+
+ deleted++;
+ }
+ } else {
+ if (unlink(mapping->path))
+ return -4;
+ deleted++;
+ }
+ DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
+ remove_mapping(s, i);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * synchronize mapping with new state:
+ *
+ * - copy FAT (with bdrv_read)
+ * - mark all filenames corresponding to mappings as deleted
+ * - recurse direntries from root (using bs->bdrv_read)
+ * - delete files corresponding to mappings marked as deleted
+ */
+static int do_commit(BDRVVVFATState* s)
+{
+ int ret = 0;
+
+ /* the real meat are the commits. Nothing to do? Move along! */
+ if (s->commits.next == 0)
+ return 0;
+
+ vvfat_close_current_file(s);
+
+ ret = handle_renames_and_mkdirs(s);
+ if (ret) {
+ fprintf(stderr, "Error handling renames (%d)\n", ret);
+ assert(0);
+ return ret;
+ }
+
+ /* copy FAT (with bdrv_read) */
+ memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
+
+ /* recurse direntries from root (using bs->bdrv_read) */
+ ret = commit_direntries(s, 0, -1);
+ if (ret) {
+ fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
+ assert(0);
+ return ret;
+ }
+
+ ret = handle_commits(s);
+ if (ret) {
+ fprintf(stderr, "Error handling commits (%d)\n", ret);
+ assert(0);
+ return ret;
+ }
+
+ ret = handle_deletes(s);
+ if (ret) {
+ fprintf(stderr, "Error deleting\n");
+ assert(0);
+ return ret;
+ }
+
+ s->qcow->drv->bdrv_make_empty(s->qcow);
+
+ memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
+
+DLOG(checkpoint());
+ return 0;
+}
+
+static int try_commit(BDRVVVFATState* s)
+{
+ vvfat_close_current_file(s);
+DLOG(checkpoint());
+ if(!is_consistent(s))
+ return -1;
+ return do_commit(s);
+}
+
+static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int i, ret;
+
+DLOG(checkpoint());
+
+ vvfat_close_current_file(s);
+
+ /*
+ * Some sanity checks:
+ * - do not allow writing to the boot sector
+ * - do not allow to write non-ASCII filenames
+ */
+
+ if (sector_num < s->first_sectors_number)
+ return -1;
+
+ for (i = sector2cluster(s, sector_num);
+ i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
+ mapping_t* mapping = find_mapping_for_cluster(s, i);
+ if (mapping) {
+ if (mapping->read_only) {
+ fprintf(stderr, "Tried to write to write-protected file %s\n",
+ mapping->path);
+ return -1;
+ }
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ int begin = cluster2sector(s, i);
+ int end = begin + s->sectors_per_cluster, k;
+ int dir_index;
+ const direntry_t* direntries;
+ long_file_name lfn;
+
+ lfn_init(&lfn);
+
+ if (begin < sector_num)
+ begin = sector_num;
+ if (end > sector_num + nb_sectors)
+ end = sector_num + nb_sectors;
+ dir_index = mapping->dir_index +
+ 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
+ direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
+
+ for (k = 0; k < (end - begin) * 0x10; k++) {
+ /* do not allow non-ASCII filenames */
+ if (parse_long_name(&lfn, direntries + k) < 0) {
+ fprintf(stderr, "Warning: non-ASCII filename\n");
+ return -1;
+ }
+ /* no access to the direntry of a read-only file */
+ else if (is_short_name(direntries+k) &&
+ (direntries[k].attributes & 1)) {
+ if (memcmp(direntries + k,
+ array_get(&(s->directory), dir_index + k),
+ sizeof(direntry_t))) {
+ fprintf(stderr, "Warning: tried to write to write-protected file\n");
+ return -1;
+ }
+ }
+ }
+ }
+ i = mapping->end;
+ } else
+ i++;
+ }
+
+ /*
+ * Use qcow backend. Commit later.
+ */
+DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
+ ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
+ if (ret < 0) {
+ fprintf(stderr, "Error writing to qcow backend\n");
+ return ret;
+ }
+
+ for (i = sector2cluster(s, sector_num);
+ i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
+ if (i >= 0)
+ s->used_clusters[i] |= USED_ALLOCATED;
+
+DLOG(checkpoint());
+ /* TODO: add timeout */
+ try_commit(s);
+
+DLOG(checkpoint());
+ return 0;
+}
+
+static int vvfat_is_allocated(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, int* n)
+{
+ BDRVVVFATState* s = bs->opaque;
+ *n = s->sector_count - sector_num;
+ if (*n > nb_sectors)
+ *n = nb_sectors;
+ else if (*n < 0)
+ return 0;
+ return 1;
+}
+
+static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t* buffer, int nb_sectors) {
+ BDRVVVFATState* s = bs->opaque;
+ return try_commit(s);
+}
+
+static void write_target_close(BlockDriverState *bs) {
+ BDRVVVFATState* s = bs->opaque;
+ bdrv_delete(s->qcow);
+ free(s->qcow_filename);
+}
+
+static BlockDriver vvfat_write_target = {
+ "vvfat_write_target", 0, NULL, NULL, NULL,
+ write_target_commit,
+ write_target_close,
+ NULL, NULL, NULL
+};
+
+static int enable_write_target(BDRVVVFATState *s)
+{
+ int size = sector2cluster(s, s->sector_count);
+ s->used_clusters = calloc(size, 1);
+
+ array_init(&(s->commits), sizeof(commit_t));
+
+ s->qcow_filename = malloc(1024);
+ strcpy(s->qcow_filename, "/tmp/vl.XXXXXX");
+ get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1);
+ if (bdrv_create(&bdrv_qcow,
+ s->qcow_filename, s->sector_count, "fat:", 0) < 0)
+ return -1;
+ s->qcow = bdrv_new("");
+ if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
+ return -1;
+
+#ifndef _WIN32
+ unlink(s->qcow_filename);
+#endif
+
+ s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
+ s->bs->backing_hd->drv = &vvfat_write_target;
+ s->bs->backing_hd->opaque = s;
+
+ return 0;
+}
+
+static void vvfat_close(BlockDriverState *bs)
+{
+ BDRVVVFATState *s = bs->opaque;
+
+ vvfat_close_current_file(s);
+ array_free(&(s->fat));
+ array_free(&(s->directory));
+ array_free(&(s->mapping));
+ if(s->cluster_buffer)
+ free(s->cluster_buffer);
+}
+
+BlockDriver bdrv_vvfat = {
+ "vvfat",
+ sizeof(BDRVVVFATState),
+ vvfat_probe,
+ vvfat_open,
+ vvfat_read,
+ vvfat_write,
+ vvfat_close,
+ NULL, /* ??? Not sure if we can do any meaningful flushing. */
+ NULL,
+ vvfat_is_allocated
+};
+
+#ifdef DEBUG
+static void checkpoint() {
+ assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
+ check1(vvv);
+ check2(vvv);
+ assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
+#if 0
+ if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
+ fprintf(stderr, "Nonono!\n");
+ mapping_t* mapping;
+ direntry_t* direntry;
+ assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
+ assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
+ if (vvv->mapping.next<47)
+ return;
+ assert((mapping = array_get(&(vvv->mapping), 47)));
+ assert(mapping->dir_index < vvv->directory.next);
+ direntry = array_get(&(vvv->directory), mapping->dir_index);
+ assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
+#endif
+ return;
+ /* avoid compiler warnings: */
+ hexdump(NULL, 100);
+ remove_mapping(vvv, NULL);
+ print_mapping(NULL);
+ print_direntry(NULL);
+}
+#endif
+
diff --git a/block.c b/block.c
new file mode 100644
index 0000000..ceb0532
--- /dev/null
+++ b/block.c
@@ -0,0 +1,858 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "block_int.h"
+
+#ifdef _BSD
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#endif
+
+#ifdef CONFIG_COCOA
+#include <paths.h>
+#include <sys/param.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOMediaBSDClient.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+//#include <IOKit/storage/IOCDTypes.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#ifdef __sun__
+#include <sys/dkio.h>
+#endif
+
+static BlockDriverState *bdrv_first;
+static BlockDriver *first_drv;
+
+#ifdef CONFIG_COCOA
+static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
+static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
+
+kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
+{
+ kern_return_t kernResult;
+ mach_port_t masterPort;
+ CFMutableDictionaryRef classesToMatch;
+
+ kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
+ if ( KERN_SUCCESS != kernResult ) {
+ printf( "IOMasterPort returned %d\n", kernResult );
+ }
+
+ classesToMatch = IOServiceMatching( kIOCDMediaClass );
+ if ( classesToMatch == NULL ) {
+ printf( "IOServiceMatching returned a NULL dictionary.\n" );
+ } else {
+ CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
+ }
+ kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
+ if ( KERN_SUCCESS != kernResult )
+ {
+ printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
+ }
+
+ return kernResult;
+}
+
+kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
+{
+ io_object_t nextMedia;
+ kern_return_t kernResult = KERN_FAILURE;
+ *bsdPath = '\0';
+ nextMedia = IOIteratorNext( mediaIterator );
+ if ( nextMedia )
+ {
+ CFTypeRef bsdPathAsCFString;
+ bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
+ if ( bsdPathAsCFString ) {
+ size_t devPathLength;
+ strcpy( bsdPath, _PATH_DEV );
+ strcat( bsdPath, "r" );
+ devPathLength = strlen( bsdPath );
+ if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
+ kernResult = KERN_SUCCESS;
+ }
+ CFRelease( bsdPathAsCFString );
+ }
+ IOObjectRelease( nextMedia );
+ }
+
+ return kernResult;
+}
+
+#endif
+
+void bdrv_register(BlockDriver *bdrv)
+{
+ bdrv->next = first_drv;
+ first_drv = bdrv;
+}
+
+/* create a new block device (by default it is empty) */
+BlockDriverState *bdrv_new(const char *device_name)
+{
+ BlockDriverState **pbs, *bs;
+
+ bs = qemu_mallocz(sizeof(BlockDriverState));
+ if(!bs)
+ return NULL;
+ pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
+ if (device_name[0] != '\0') {
+ /* insert at the end */
+ pbs = &bdrv_first;
+ while (*pbs != NULL)
+ pbs = &(*pbs)->next;
+ *pbs = bs;
+ }
+ return bs;
+}
+
+BlockDriver *bdrv_find_format(const char *format_name)
+{
+ BlockDriver *drv1;
+ for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+ if (!strcmp(drv1->format_name, format_name))
+ return drv1;
+ }
+ return NULL;
+}
+
+int bdrv_create(BlockDriver *drv,
+ const char *filename, int64_t size_in_sectors,
+ const char *backing_file, int flags)
+{
+ if (!drv->bdrv_create)
+ return -ENOTSUP;
+ return drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
+}
+
+#ifdef _WIN32
+void get_tmp_filename(char *filename, int size)
+{
+ char* p = strrchr(filename, '/');
+
+ if (p == NULL)
+ return;
+
+ /* XXX: find a better function */
+ tmpnam(p);
+ *p = '/';
+}
+#else
+void get_tmp_filename(char *filename, int size)
+{
+ int fd;
+ /* XXX: race condition possible */
+ pstrcpy(filename, size, "/tmp/vl.XXXXXX");
+ fd = mkstemp(filename);
+ close(fd);
+}
+#endif
+
+/* XXX: force raw format if block or character device ? It would
+ simplify the BSD case */
+static BlockDriver *find_image_format(const char *filename)
+{
+ int fd, ret, score, score_max;
+ BlockDriver *drv1, *drv;
+ uint8_t *buf;
+ size_t bufsize = 1024;
+
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ buf = NULL;
+ ret = 0;
+ } else {
+#ifdef DIOCGSECTORSIZE
+ {
+ unsigned int sectorsize = 512;
+ if (!ioctl(fd, DIOCGSECTORSIZE, &sectorsize) &&
+ sectorsize > bufsize)
+ bufsize = sectorsize;
+ }
+#endif
+#ifdef CONFIG_COCOA
+ u_int32_t blockSize = 512;
+ if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
+ bufsize = blockSize;
+ }
+#endif
+ buf = qemu_malloc(bufsize);
+ if (!buf)
+ return NULL;
+ ret = read(fd, buf, bufsize);
+ if (ret < 0) {
+ close(fd);
+ qemu_free(buf);
+ return NULL;
+ }
+ close(fd);
+ }
+
+ drv = NULL;
+ score_max = 0;
+ for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+ score = drv1->bdrv_probe(buf, ret, filename);
+ if (score > score_max) {
+ score_max = score;
+ drv = drv1;
+ }
+ }
+ qemu_free(buf);
+ return drv;
+}
+
+int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot)
+{
+#ifdef CONFIG_COCOA
+ if ( strncmp( filename, "/dev/cdrom", 10 ) == 0 ) {
+ kern_return_t kernResult;
+ io_iterator_t mediaIterator;
+ char bsdPath[ MAXPATHLEN ];
+ int fd;
+
+ kernResult = FindEjectableCDMedia( &mediaIterator );
+ kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
+
+ if ( bsdPath[ 0 ] != '\0' ) {
+ strcat(bsdPath,"s0");
+ /* some CDs don't have a partition 0 */
+ fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ bsdPath[strlen(bsdPath)-1] = '1';
+ } else {
+ close(fd);
+ }
+ filename = bsdPath;
+ }
+
+ if ( mediaIterator )
+ IOObjectRelease( mediaIterator );
+ }
+#endif
+ return bdrv_open2(bs, filename, snapshot, NULL);
+}
+
+int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot,
+ BlockDriver *drv)
+{
+ int ret;
+ char tmp_filename[1024];
+
+ bs->read_only = 0;
+ bs->is_temporary = 0;
+ bs->encrypted = 0;
+
+ if (snapshot) {
+ BlockDriverState *bs1;
+ int64_t total_size;
+
+ /* if snapshot, we create a temporary backing file and open it
+ instead of opening 'filename' directly */
+
+ /* if there is a backing file, use it */
+ bs1 = bdrv_new("");
+ if (!bs1) {
+ return -1;
+ }
+ if (bdrv_open(bs1, filename, 0) < 0) {
+ bdrv_delete(bs1);
+ return -1;
+ }
+ total_size = bs1->total_sectors;
+ bdrv_delete(bs1);
+
+ get_tmp_filename(tmp_filename, sizeof(tmp_filename));
+ /* XXX: use cow for linux as it is more efficient ? */
+ if (bdrv_create(&bdrv_qcow, tmp_filename,
+ total_size, filename, 0) < 0) {
+ return -1;
+ }
+ filename = tmp_filename;
+ bs->is_temporary = 1;
+ }
+
+ pstrcpy(bs->filename, sizeof(bs->filename), filename);
+ if (!drv) {
+ drv = find_image_format(filename);
+ if (!drv)
+ return -1;
+ }
+ bs->drv = drv;
+ bs->opaque = qemu_mallocz(drv->instance_size);
+ if (bs->opaque == NULL && drv->instance_size > 0)
+ return -1;
+
+ ret = drv->bdrv_open(bs, filename);
+ if (ret < 0) {
+ qemu_free(bs->opaque);
+ return -1;
+ }
+#ifndef _WIN32
+ if (bs->is_temporary) {
+ unlink(filename);
+ }
+#endif
+ if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) {
+ /* if there is a backing file, use it */
+ bs->backing_hd = bdrv_new("");
+ if (!bs->backing_hd) {
+ fail:
+ bdrv_close(bs);
+ return -1;
+ }
+ if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0)
+ goto fail;
+ }
+
+ bs->inserted = 1;
+
+ /* call the change callback */
+ if (bs->change_cb)
+ bs->change_cb(bs->change_opaque);
+
+ return 0;
+}
+
+void bdrv_close(BlockDriverState *bs)
+{
+ if (bs->inserted) {
+ if (bs->backing_hd)
+ bdrv_delete(bs->backing_hd);
+ bs->drv->bdrv_close(bs);
+ qemu_free(bs->opaque);
+#ifdef _WIN32
+ if (bs->is_temporary) {
+ unlink(bs->filename);
+ }
+#endif
+ bs->opaque = NULL;
+ bs->drv = NULL;
+ bs->inserted = 0;
+
+ /* call the change callback */
+ if (bs->change_cb)
+ bs->change_cb(bs->change_opaque);
+ }
+}
+
+void bdrv_delete(BlockDriverState *bs)
+{
+ /* XXX: remove the driver list */
+ bdrv_close(bs);
+ qemu_free(bs);
+}
+
+/* commit COW file into the raw image */
+int bdrv_commit(BlockDriverState *bs)
+{
+ int64_t i;
+ int n, j;
+ unsigned char sector[512];
+
+ if (!bs->inserted)
+ return -ENOENT;
+
+ if (bs->read_only) {
+ return -EACCES;
+ }
+
+ if (!bs->backing_hd) {
+ return -ENOTSUP;
+ }
+
+ for (i = 0; i < bs->total_sectors;) {
+ if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) {
+ for(j = 0; j < n; j++) {
+ if (bdrv_read(bs, i, sector, 1) != 0) {
+ return -EIO;
+ }
+
+ if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
+ return -EIO;
+ }
+ i++;
+ }
+ } else {
+ i += n;
+ }
+ }
+
+ if (bs->drv->bdrv_make_empty)
+ return bs->drv->bdrv_make_empty(bs);
+
+ return 0;
+}
+
+/* return -1 if error */
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int ret, n;
+ BlockDriver *drv = bs->drv;
+
+ if (!bs->inserted)
+ return -1;
+
+ while (nb_sectors > 0) {
+ if (sector_num == 0 && bs->boot_sector_enabled) {
+ memcpy(buf, bs->boot_sector_data, 512);
+ n = 1;
+ } else if (bs->backing_hd) {
+ if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) {
+ ret = drv->bdrv_read(bs, sector_num, buf, n);
+ if (ret < 0)
+ return -1;
+ } else {
+ /* read from the base image */
+ ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+ if (ret < 0)
+ return -1;
+ }
+ } else {
+ ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors);
+ if (ret < 0)
+ return -1;
+ /* no need to loop */
+ break;
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+/* return -1 if error */
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ if (!bs->inserted)
+ return -1;
+ if (bs->read_only)
+ return -1;
+ if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
+ memcpy(bs->boot_sector_data, buf, 512);
+ }
+ return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors);
+}
+
+void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
+{
+ *nb_sectors_ptr = bs->total_sectors;
+}
+
+/* force a given boot sector. */
+void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size)
+{
+ bs->boot_sector_enabled = 1;
+ if (size > 512)
+ size = 512;
+ memcpy(bs->boot_sector_data, data, size);
+ memset(bs->boot_sector_data + size, 0, 512 - size);
+}
+
+void bdrv_set_geometry_hint(BlockDriverState *bs,
+ int cyls, int heads, int secs)
+{
+ bs->cyls = cyls;
+ bs->heads = heads;
+ bs->secs = secs;
+}
+
+void bdrv_set_type_hint(BlockDriverState *bs, int type)
+{
+ bs->type = type;
+ bs->removable = ((type == BDRV_TYPE_CDROM ||
+ type == BDRV_TYPE_FLOPPY));
+}
+
+void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
+{
+ bs->translation = translation;
+}
+
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+ int *pcyls, int *pheads, int *psecs)
+{
+ *pcyls = bs->cyls;
+ *pheads = bs->heads;
+ *psecs = bs->secs;
+}
+
+int bdrv_get_type_hint(BlockDriverState *bs)
+{
+ return bs->type;
+}
+
+int bdrv_get_translation_hint(BlockDriverState *bs)
+{
+ return bs->translation;
+}
+
+int bdrv_is_removable(BlockDriverState *bs)
+{
+ return bs->removable;
+}
+
+int bdrv_is_read_only(BlockDriverState *bs)
+{
+ return bs->read_only;
+}
+
+int bdrv_is_inserted(BlockDriverState *bs)
+{
+ return bs->inserted;
+}
+
+int bdrv_is_locked(BlockDriverState *bs)
+{
+ return bs->locked;
+}
+
+void bdrv_set_locked(BlockDriverState *bs, int locked)
+{
+ bs->locked = locked;
+}
+
+void bdrv_set_change_cb(BlockDriverState *bs,
+ void (*change_cb)(void *opaque), void *opaque)
+{
+ bs->change_cb = change_cb;
+ bs->change_opaque = opaque;
+}
+
+int bdrv_is_encrypted(BlockDriverState *bs)
+{
+ if (bs->backing_hd && bs->backing_hd->encrypted)
+ return 1;
+ return bs->encrypted;
+}
+
+int bdrv_set_key(BlockDriverState *bs, const char *key)
+{
+ int ret;
+ if (bs->backing_hd && bs->backing_hd->encrypted) {
+ ret = bdrv_set_key(bs->backing_hd, key);
+ if (ret < 0)
+ return ret;
+ if (!bs->encrypted)
+ return 0;
+ }
+ if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key)
+ return -1;
+ return bs->drv->bdrv_set_key(bs, key);
+}
+
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
+{
+ if (!bs->inserted || !bs->drv) {
+ buf[0] = '\0';
+ } else {
+ pstrcpy(buf, buf_size, bs->drv->format_name);
+ }
+}
+
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+ void *opaque)
+{
+ BlockDriver *drv;
+
+ for (drv = first_drv; drv != NULL; drv = drv->next) {
+ it(opaque, drv->format_name);
+ }
+}
+
+BlockDriverState *bdrv_find(const char *name)
+{
+ BlockDriverState *bs;
+
+ for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ if (!strcmp(name, bs->device_name))
+ return bs;
+ }
+ return NULL;
+}
+
+void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque)
+{
+ BlockDriverState *bs;
+
+ for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ it(opaque, bs->device_name);
+ }
+}
+
+const char *bdrv_get_device_name(BlockDriverState *bs)
+{
+ return bs->device_name;
+}
+
+void bdrv_flush(BlockDriverState *bs)
+{
+ if (bs->drv->bdrv_flush)
+ bs->drv->bdrv_flush(bs);
+ if (bs->backing_hd)
+ bdrv_flush(bs->backing_hd);
+}
+
+void bdrv_info(void)
+{
+ BlockDriverState *bs;
+
+ for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+ term_printf("%s:", bs->device_name);
+ term_printf(" type=");
+ switch(bs->type) {
+ case BDRV_TYPE_HD:
+ term_printf("hd");
+ break;
+ case BDRV_TYPE_CDROM:
+ term_printf("cdrom");
+ break;
+ case BDRV_TYPE_FLOPPY:
+ term_printf("floppy");
+ break;
+ }
+ term_printf(" removable=%d", bs->removable);
+ if (bs->removable) {
+ term_printf(" locked=%d", bs->locked);
+ }
+ if (bs->inserted) {
+ term_printf(" file=%s", bs->filename);
+ if (bs->backing_file[0] != '\0')
+ term_printf(" backing_file=%s", bs->backing_file);
+ term_printf(" ro=%d", bs->read_only);
+ term_printf(" drv=%s", bs->drv->format_name);
+ if (bs->encrypted)
+ term_printf(" encrypted");
+ } else {
+ term_printf(" [not inserted]");
+ }
+ term_printf("\n");
+ }
+}
+
+/**************************************************************/
+/* RAW block driver */
+
+typedef struct BDRVRawState {
+ int fd;
+} BDRVRawState;
+
+static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ return 1; /* maybe */
+}
+
+static int raw_open(BlockDriverState *bs, const char *filename)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd;
+ int64_t size;
+#ifdef _BSD
+ struct stat sb;
+#endif
+#ifdef __sun__
+ struct dk_minfo minfo;
+ int rv;
+#endif
+
+ fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0)
+ return -1;
+ bs->read_only = 1;
+ }
+#ifdef _BSD
+ if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
+#ifdef DIOCGMEDIASIZE
+ if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
+#endif
+#ifdef CONFIG_COCOA
+ size = LONG_LONG_MAX;
+#else
+ size = lseek(fd, 0LL, SEEK_END);
+#endif
+ } else
+#endif
+#ifdef __sun__
+ /*
+ * use the DKIOCGMEDIAINFO ioctl to read the size.
+ */
+ rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo );
+ if ( rv != -1 ) {
+ size = minfo.dki_lbsize * minfo.dki_capacity;
+ } else /* there are reports that lseek on some devices
+ fails, but irc discussion said that contingency
+ on contingency was overkill */
+#endif
+ {
+ size = lseek(fd, 0, SEEK_END);
+ }
+#ifdef _WIN32
+ /* On Windows hosts it can happen that we're unable to get file size
+ for CD-ROM raw device (it's inherent limitation of the CDFS driver). */
+ if (size == -1)
+ size = LONG_LONG_MAX;
+#endif
+ bs->total_sectors = size / 512;
+ s->fd = fd;
+ return 0;
+}
+
+static int raw_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ lseek(s->fd, sector_num * 512, SEEK_SET);
+ ret = read(s->fd, buf, nb_sectors * 512);
+ if (ret != nb_sectors * 512)
+ return -1;
+ return 0;
+}
+
+static int raw_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ lseek(s->fd, sector_num * 512, SEEK_SET);
+ ret = write(s->fd, buf, nb_sectors * 512);
+ if (ret != nb_sectors * 512)
+ return -1;
+ return 0;
+}
+
+static void raw_close(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ close(s->fd);
+}
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winioctl.h>
+
+int qemu_ftruncate64(int fd, int64_t length)
+{
+ LARGE_INTEGER li;
+ LONG high;
+ HANDLE h;
+ BOOL res;
+
+ if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
+ return -1;
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ /* get current position, ftruncate do not change position */
+ li.HighPart = 0;
+ li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
+ if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
+ return -1;
+
+ high = length >> 32;
+ if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN))
+ return -1;
+ res = SetEndOfFile(h);
+
+ /* back to old position */
+ SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN);
+ return res ? 0 : -1;
+}
+
+static int set_sparse(int fd)
+{
+ DWORD returned;
+ return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
+ NULL, 0, NULL, 0, &returned, NULL);
+}
+#else
+static inline int set_sparse(int fd)
+{
+ return 1;
+}
+#endif
+
+static int raw_create(const char *filename, int64_t total_size,
+ const char *backing_file, int flags)
+{
+ int fd;
+
+ if (flags || backing_file)
+ return -ENOTSUP;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0)
+ return -EIO;
+ set_sparse(fd);
+ ftruncate(fd, total_size * 512);
+ close(fd);
+ return 0;
+}
+
+static void raw_flush(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ fsync(s->fd);
+}
+
+BlockDriver bdrv_raw = {
+ "raw",
+ sizeof(BDRVRawState),
+ raw_probe,
+ raw_open,
+ raw_read,
+ raw_write,
+ raw_close,
+ raw_create,
+ raw_flush,
+};
+
+void bdrv_init(void)
+{
+ bdrv_register(&bdrv_raw);
+#ifndef _WIN32
+ bdrv_register(&bdrv_cow);
+#endif
+ bdrv_register(&bdrv_qcow);
+ bdrv_register(&bdrv_vmdk);
+ bdrv_register(&bdrv_cloop);
+ bdrv_register(&bdrv_dmg);
+ bdrv_register(&bdrv_bochs);
+ bdrv_register(&bdrv_vpc);
+ bdrv_register(&bdrv_vvfat);
+}
diff --git a/block_int.h b/block_int.h
new file mode 100644
index 0000000..c2a2e30
--- /dev/null
+++ b/block_int.h
@@ -0,0 +1,81 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLOCK_INT_H
+#define BLOCK_INT_H
+
+struct BlockDriver {
+ const char *format_name;
+ int instance_size;
+ int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
+ int (*bdrv_open)(BlockDriverState *bs, const char *filename);
+ int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors);
+ int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+ void (*bdrv_close)(BlockDriverState *bs);
+ int (*bdrv_create)(const char *filename, int64_t total_sectors,
+ const char *backing_file, int flags);
+ void (*bdrv_flush)(BlockDriverState *bs);
+ int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum);
+ int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
+ int (*bdrv_make_empty)(BlockDriverState *bs);
+ struct BlockDriver *next;
+};
+
+struct BlockDriverState {
+ int64_t total_sectors;
+ int read_only; /* if true, the media is read only */
+ int inserted; /* if true, the media is present */
+ int removable; /* if true, the media can be removed */
+ int locked; /* if true, the media cannot temporarily be ejected */
+ int encrypted; /* if true, the media is encrypted */
+ /* event callback when inserting/removing */
+ void (*change_cb)(void *opaque);
+ void *change_opaque;
+
+ BlockDriver *drv;
+ void *opaque;
+
+ int boot_sector_enabled;
+ uint8_t boot_sector_data[512];
+
+ char filename[1024];
+ char backing_file[1024]; /* if non zero, the image is a diff of
+ this file image */
+ int is_temporary;
+
+ BlockDriverState *backing_hd;
+
+ /* NOTE: the following infos are only hints for real hardware
+ drivers. They are not used by the block driver */
+ int cyls, heads, secs, translation;
+ int type;
+ char device_name[32];
+ BlockDriverState *next;
+};
+
+void get_tmp_filename(char *filename, int size);
+
+#endif /* BLOCK_INT_H */
diff --git a/bswap.h b/bswap.h
new file mode 100644
index 0000000..37fb04e
--- /dev/null
+++ b/bswap.h
@@ -0,0 +1,202 @@
+#ifndef BSWAP_H
+#define BSWAP_H
+
+#include "config-host.h"
+
+#include <inttypes.h>
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#else
+
+#define bswap_16(x) \
+({ \
+ uint16_t __x = (x); \
+ ((uint16_t)( \
+ (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
+ (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
+})
+
+#define bswap_32(x) \
+({ \
+ uint32_t __x = (x); \
+ ((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
+})
+
+#define bswap_64(x) \
+({ \
+ uint64_t __x = (x); \
+ ((uint64_t)( \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
+})
+
+#endif /* !HAVE_BYTESWAP_H */
+
+static inline uint16_t bswap16(uint16_t x)
+{
+ return bswap_16(x);
+}
+
+static inline uint32_t bswap32(uint32_t x)
+{
+ return bswap_32(x);
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+ return bswap_64(x);
+}
+
+static inline void bswap16s(uint16_t *s)
+{
+ *s = bswap16(*s);
+}
+
+static inline void bswap32s(uint32_t *s)
+{
+ *s = bswap32(*s);
+}
+
+static inline void bswap64s(uint64_t *s)
+{
+ *s = bswap64(*s);
+}
+
+#if defined(WORDS_BIGENDIAN)
+#define be_bswap(v, size) (v)
+#define le_bswap(v, size) bswap ## size(v)
+#define be_bswaps(v, size)
+#define le_bswaps(p, size) *p = bswap ## size(*p);
+#else
+#define le_bswap(v, size) (v)
+#define be_bswap(v, size) bswap ## size(v)
+#define le_bswaps(v, size)
+#define be_bswaps(p, size) *p = bswap ## size(*p);
+#endif
+
+#define CPU_CONVERT(endian, size, type)\
+static inline type endian ## size ## _to_cpu(type v)\
+{\
+ return endian ## _bswap(v, size);\
+}\
+\
+static inline type cpu_to_ ## endian ## size(type v)\
+{\
+ return endian ## _bswap(v, size);\
+}\
+\
+static inline void endian ## size ## _to_cpus(type *p)\
+{\
+ endian ## _bswaps(p, size)\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## s(type *p)\
+{\
+ endian ## _bswaps(p, size)\
+}\
+\
+static inline type endian ## size ## _to_cpup(const type *p)\
+{\
+ return endian ## size ## _to_cpu(*p);\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
+{\
+ *p = cpu_to_ ## endian ## size(v);\
+}
+
+CPU_CONVERT(be, 16, uint16_t)
+CPU_CONVERT(be, 32, uint32_t)
+CPU_CONVERT(be, 64, uint64_t)
+
+CPU_CONVERT(le, 16, uint16_t)
+CPU_CONVERT(le, 32, uint32_t)
+CPU_CONVERT(le, 64, uint64_t)
+
+/* unaligned versions (optimized for frequent unaligned accesses)*/
+
+#if defined(__i386__) || defined(__powerpc__)
+
+#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
+#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
+#define le16_to_cpupu(p) le16_to_cpup(p)
+#define le32_to_cpupu(p) le32_to_cpup(p)
+
+#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
+#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
+
+#else
+
+static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v;
+ p1[1] = v >> 8;
+}
+
+static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v;
+ p1[1] = v >> 8;
+ p1[2] = v >> 16;
+ p1[3] = v >> 24;
+}
+
+static inline uint16_t le16_to_cpupu(const uint16_t *p)
+{
+ const uint8_t *p1 = (const uint8_t *)p;
+ return p1[0] | (p1[1] << 8);
+}
+
+static inline uint32_t le32_to_cpupu(const uint32_t *p)
+{
+ const uint8_t *p1 = (const uint8_t *)p;
+ return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
+}
+
+static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v >> 8;
+ p1[1] = v;
+}
+
+static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v >> 24;
+ p1[1] = v >> 16;
+ p1[2] = v >> 8;
+ p1[3] = v;
+}
+
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define cpu_to_32wu cpu_to_be32wu
+#else
+#define cpu_to_32wu cpu_to_le32wu
+#endif
+
+#undef le_bswap
+#undef be_bswap
+#undef le_bswaps
+#undef be_bswaps
+
+#endif /* BSWAP_H */
diff --git a/cocoa.m b/cocoa.m
new file mode 100644
index 0000000..9551aff
--- /dev/null
+++ b/cocoa.m
@@ -0,0 +1,927 @@
+/*
+ * QEMU Cocoa display driver
+ *
+ * Copyright (c) 2005 Pierre d'Herbemont
+ * many code/inspiration from SDL 1.2 code (LGPL)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ Todo : x miniaturize window
+ x center the window
+ - save window position
+ - handle keyboard event
+ - handle mouse event
+ - non 32 bpp support
+ - full screen
+ - mouse focus
+ x simple graphical prompt to demo
+ - better graphical prompt
+*/
+
+#import <Cocoa/Cocoa.h>
+
+#include "vl.h"
+
+NSWindow *window = NULL;
+NSQuickDrawView *qd_view = NULL;
+
+
+int gArgc;
+char **gArgv;
+DisplayState current_ds;
+
+int grab = 0;
+int modifiers_state[256];
+
+/* main defined in qemu/vl.c */
+int qemu_main(int argc, char **argv);
+
+/* To deal with miniaturization */
+@interface QemuWindow : NSWindow
+{ }
+@end
+
+
+/*
+ ------------------------------------------------------
+ Qemu Video Driver
+ ------------------------------------------------------
+*/
+
+/*
+ ------------------------------------------------------
+ cocoa_update
+ ------------------------------------------------------
+*/
+static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
+
+ /* Use QDFlushPortBuffer() to flush content to display */
+ RgnHandle dirty = NewRgn ();
+ RgnHandle temp = NewRgn ();
+
+ SetEmptyRgn (dirty);
+
+ /* Build the region of dirty rectangles */
+ MacSetRectRgn (temp, x, y,
+ x + w, y + h);
+ MacUnionRgn (dirty, temp, dirty);
+
+ /* Flush the dirty region */
+ QDFlushPortBuffer ( [ qd_view qdPort ], dirty );
+ DisposeRgn (dirty);
+ DisposeRgn (temp);
+}
+
+/*
+ ------------------------------------------------------
+ cocoa_resize
+ ------------------------------------------------------
+*/
+static void cocoa_resize(DisplayState *ds, int w, int h)
+{
+ const int device_bpp = 32;
+ static void *screen_pixels;
+ static int screen_pitch;
+ NSRect contentRect;
+
+ //printf("resizing to %d %d\n", w, h);
+
+ contentRect = NSMakeRect (0, 0, w, h);
+ if(window)
+ {
+ [window close];
+ [window release];
+ }
+ window = [ [ QemuWindow alloc ] initWithContentRect:contentRect
+ styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
+ backing:NSBackingStoreBuffered defer:NO];
+ if(!window)
+ {
+ fprintf(stderr, "(cocoa) can't create window\n");
+ exit(1);
+ }
+
+ if(qd_view)
+ [qd_view release];
+
+ qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
+
+ if(!qd_view)
+ {
+ fprintf(stderr, "(cocoa) can't create qd_view\n");
+ exit(1);
+ }
+
+ [ window setAcceptsMouseMovedEvents:YES ];
+ [ window setTitle:@"Qemu" ];
+ [ window setReleasedWhenClosed:NO ];
+
+ /* Set screen to black */
+ [ window setBackgroundColor: [NSColor blackColor] ];
+
+ /* set window position */
+ [ window center ];
+
+ [ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+ [ [ window contentView ] addSubview:qd_view ];
+ [ qd_view release ];
+ [ window makeKeyAndOrderFront:nil ];
+
+ /* Careful here, the window seems to have to be onscreen to do that */
+ LockPortBits ( [ qd_view qdPort ] );
+ screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) );
+ screen_pitch = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) );
+ UnlockPortBits ( [ qd_view qdPort ] );
+ {
+ int vOffset = [ window frame ].size.height -
+ [ qd_view frame ].size.height - [ qd_view frame ].origin.y;
+
+ int hOffset = [ qd_view frame ].origin.x;
+
+ screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8);
+ }
+ ds->data = screen_pixels;
+ ds->linesize = screen_pitch;
+ ds->depth = device_bpp;
+ ds->width = w;
+ ds->height = h;
+
+ current_ds = *ds;
+}
+
+/*
+ ------------------------------------------------------
+ keymap conversion
+ ------------------------------------------------------
+*/
+
+int keymap[] =
+{
+// SdlI macI macH SdlH 104xtH 104xtC sdl
+ 30, // 0 0x00 0x1e A QZ_a
+ 31, // 1 0x01 0x1f S QZ_s
+ 32, // 2 0x02 0x20 D QZ_d
+ 33, // 3 0x03 0x21 F QZ_f
+ 35, // 4 0x04 0x23 H QZ_h
+ 34, // 5 0x05 0x22 G QZ_g
+ 44, // 6 0x06 0x2c Z QZ_z
+ 45, // 7 0x07 0x2d X QZ_x
+ 46, // 8 0x08 0x2e C QZ_c
+ 47, // 9 0x09 0x2f V QZ_v
+ 0, // 10 0x0A Undefined
+ 48, // 11 0x0B 0x30 B QZ_b
+ 16, // 12 0x0C 0x10 Q QZ_q
+ 17, // 13 0x0D 0x11 W QZ_w
+ 18, // 14 0x0E 0x12 E QZ_e
+ 19, // 15 0x0F 0x13 R QZ_r
+ 21, // 16 0x10 0x15 Y QZ_y
+ 20, // 17 0x11 0x14 T QZ_t
+ 2, // 18 0x12 0x02 1 QZ_1
+ 3, // 19 0x13 0x03 2 QZ_2
+ 4, // 20 0x14 0x04 3 QZ_3
+ 5, // 21 0x15 0x05 4 QZ_4
+ 7, // 22 0x16 0x07 6 QZ_6
+ 6, // 23 0x17 0x06 5 QZ_5
+ 13, // 24 0x18 0x0d = QZ_EQUALS
+ 10, // 25 0x19 0x0a 9 QZ_9
+ 8, // 26 0x1A 0x08 7 QZ_7
+ 12, // 27 0x1B 0x0c - QZ_MINUS
+ 9, // 28 0x1C 0x09 8 QZ_8
+ 11, // 29 0x1D 0x0b 0 QZ_0
+ 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
+ 24, // 31 0x1F 0x18 O QZ_o
+ 22, // 32 0x20 0x16 U QZ_u
+ 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
+ 23, // 34 0x22 0x17 I QZ_i
+ 25, // 35 0x23 0x19 P QZ_p
+ 28, // 36 0x24 0x1c ENTER QZ_RETURN
+ 38, // 37 0x25 0x26 L QZ_l
+ 36, // 38 0x26 0x24 J QZ_j
+ 40, // 39 0x27 0x28 ' QZ_QUOTE
+ 37, // 40 0x28 0x25 K QZ_k
+ 39, // 41 0x29 0x27 ; QZ_SEMICOLON
+ 43, // 42 0x2A 0x2b \ QZ_BACKSLASH
+ 51, // 43 0x2B 0x33 , QZ_COMMA
+ 53, // 44 0x2C 0x35 / QZ_SLASH
+ 49, // 45 0x2D 0x31 N QZ_n
+ 50, // 46 0x2E 0x32 M QZ_m
+ 52, // 47 0x2F 0x34 . QZ_PERIOD
+ 15, // 48 0x30 0x0f TAB QZ_TAB
+ 57, // 49 0x31 0x39 SPACE QZ_SPACE
+ 41, // 50 0x32 0x29 ` QZ_BACKQUOTE
+ 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
+ 0, // 52 0x34 Undefined
+ 1, // 53 0x35 0x01 ESC QZ_ESCAPE
+ 0, // 54 0x36 QZ_RMETA
+ 0, // 55 0x37 QZ_LMETA
+ 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
+ 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
+ 56, // 58 0x3A 0x38 L ALT QZ_LALT
+ 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
+ 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
+ 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
+ 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
+ 0, // 63 0x3F Undefined
+ 0, // 64 0x40 Undefined
+ 0, // 65 0x41 Undefined
+ 0, // 66 0x42 Undefined
+ 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
+ 0, // 68 0x44 Undefined
+ 78, // 69 0x45 0x4e KP + QZ_KP_PLUS
+ 0, // 70 0x46 Undefined
+ 69, // 71 0x47 0x45 NUM QZ_NUMLOCK
+ 0, // 72 0x48 Undefined
+ 0, // 73 0x49 Undefined
+ 0, // 74 0x4A Undefined
+ 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
+ 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
+ 0, // 77 0x4D undefined
+ 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
+ 0, // 79 0x4F Undefined
+ 0, // 80 0x50 Undefined
+ 0, // 81 0x51 QZ_KP_EQUALS
+ 82, // 82 0x52 0x52 KP 0 QZ_KP0
+ 79, // 83 0x53 0x4f KP 1 QZ_KP1
+ 80, // 84 0x54 0x50 KP 2 QZ_KP2
+ 81, // 85 0x55 0x51 KP 3 QZ_KP3
+ 75, // 86 0x56 0x4b KP 4 QZ_KP4
+ 76, // 87 0x57 0x4c KP 5 QZ_KP5
+ 77, // 88 0x58 0x4d KP 6 QZ_KP6
+ 71, // 89 0x59 0x47 KP 7 QZ_KP7
+ 0, // 90 0x5A Undefined
+ 72, // 91 0x5B 0x48 KP 8 QZ_KP8
+ 73, // 92 0x5C 0x49 KP 9 QZ_KP9
+ 0, // 93 0x5D Undefined
+ 0, // 94 0x5E Undefined
+ 0, // 95 0x5F Undefined
+ 63, // 96 0x60 0x3f F5 QZ_F5
+ 64, // 97 0x61 0x40 F6 QZ_F6
+ 65, // 98 0x62 0x41 F7 QZ_F7
+ 61, // 99 0x63 0x3d F3 QZ_F3
+ 66, // 100 0x64 0x42 F8 QZ_F8
+ 67, // 101 0x65 0x43 F9 QZ_F9
+ 0, // 102 0x66 Undefined
+ 87, // 103 0x67 0x57 F11 QZ_F11
+ 0, // 104 0x68 Undefined
+ 183,// 105 0x69 0xb7 QZ_PRINT
+ 0, // 106 0x6A Undefined
+ 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
+ 0, // 108 0x6C Undefined
+ 68, // 109 0x6D 0x44 F10 QZ_F10
+ 0, // 110 0x6E Undefined
+ 88, // 111 0x6F 0x58 F12 QZ_F12
+ 0, // 112 0x70 Undefined
+ 110,// 113 0x71 0x0 QZ_PAUSE
+ 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
+ 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
+ 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
+ 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
+ 62, // 118 0x76 0x3e F4 QZ_F4
+ 207,// 119 0x77 0xcf E0,4f END QZ_END
+ 60, // 120 0x78 0x3c F2 QZ_F2
+ 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
+ 59, // 122 0x7A 0x3b F1 QZ_F1
+ 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
+ 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
+ 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
+ 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
+/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
+
+/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
+/*
+ 219 // 0xdb e0,5b L GUI
+ 220 // 0xdc e0,5c R GUI
+ 221 // 0xdd e0,5d APPS
+ // E0,2A,E0,37 PRNT SCRN
+ // E1,1D,45,E1,9D,C5 PAUSE
+ 83 // 0x53 0x53 KP .
+// ACPI Scan Codes
+ 222 // 0xde E0, 5E Power
+ 223 // 0xdf E0, 5F Sleep
+ 227 // 0xe3 E0, 63 Wake
+// Windows Multimedia Scan Codes
+ 153 // 0x99 E0, 19 Next Track
+ 144 // 0x90 E0, 10 Previous Track
+ 164 // 0xa4 E0, 24 Stop
+ 162 // 0xa2 E0, 22 Play/Pause
+ 160 // 0xa0 E0, 20 Mute
+ 176 // 0xb0 E0, 30 Volume Up
+ 174 // 0xae E0, 2E Volume Down
+ 237 // 0xed E0, 6D Media Select
+ 236 // 0xec E0, 6C E-Mail
+ 161 // 0xa1 E0, 21 Calculator
+ 235 // 0xeb E0, 6B My Computer
+ 229 // 0xe5 E0, 65 WWW Search
+ 178 // 0xb2 E0, 32 WWW Home
+ 234 // 0xea E0, 6A WWW Back
+ 233 // 0xe9 E0, 69 WWW Forward
+ 232 // 0xe8 E0, 68 WWW Stop
+ 231 // 0xe7 E0, 67 WWW Refresh
+ 230 // 0xe6 E0, 66 WWW Favorites
+*/
+};
+
+int cocoa_keycode_to_qemu(int keycode)
+{
+ if((sizeof(keymap)/sizeof(int)) <= keycode)
+ {
+ printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
+ return 0;
+ }
+ return keymap[keycode];
+}
+
+/*
+ ------------------------------------------------------
+ cocoa_refresh
+ ------------------------------------------------------
+*/
+static void cocoa_refresh(DisplayState *ds)
+{
+ //printf("cocoa_refresh \n");
+ NSDate *distantPast;
+ NSEvent *event;
+ NSAutoreleasePool *pool;
+
+ pool = [ [ NSAutoreleasePool alloc ] init ];
+ distantPast = [ NSDate distantPast ];
+
+ vga_hw_update();
+
+ do {
+ event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
+ inMode: NSDefaultRunLoopMode dequeue:YES ];
+ if (event != nil) {
+ switch ([event type]) {
+ case NSFlagsChanged:
+ {
+ int keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ if (keycode)
+ {
+ if (keycode == 58 || keycode == 69) {
+ /* emulate caps lock and num lock keydown and keyup */
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | 0x80);
+ } else if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (modifiers_state[keycode] == 0) {
+ /* keydown */
+ kbd_put_keycode(keycode & 0x7f);
+ modifiers_state[keycode] = 1;
+ } else {
+ /* keyup */
+ kbd_put_keycode(keycode | 0x80);
+ modifiers_state[keycode] = 0;
+ }
+ }
+ }
+
+ /* release Mouse grab when pressing ctrl+alt */
+ if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask))
+ {
+ [window setTitle: @"QEMU"];
+ [NSCursor unhide];
+ CGAssociateMouseAndMouseCursorPosition ( TRUE );
+ grab = 0;
+ }
+ }
+ break;
+
+ case NSKeyDown:
+ {
+ int keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ /* handle command Key Combos */
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ switch ([event keyCode]) {
+ /* quit */
+ case 12: /* q key */
+ /* switch to windowed View */
+ exit(0);
+ return;
+ }
+ }
+
+ /* handle control + alt Key Combos */
+ if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+ switch (keycode) {
+ /* toggle Monitor */
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ console_select(keycode - 0x02);
+ break;
+ }
+ } else {
+ /* handle standard key events */
+ if (is_graphic_console()) {
+ if (keycode & 0x80) //check bit for e0 in front
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+ /* handle monitor key events */
+ } else {
+ int keysym = 0;
+
+ switch([event keyCode]) {
+ case 115:
+ keysym = QEMU_KEY_HOME;
+ break;
+ case 117:
+ keysym = QEMU_KEY_DELETE;
+ break;
+ case 119:
+ keysym = QEMU_KEY_END;
+ break;
+ case 123:
+ keysym = QEMU_KEY_LEFT;
+ break;
+ case 124:
+ keysym = QEMU_KEY_RIGHT;
+ break;
+ case 125:
+ keysym = QEMU_KEY_DOWN;
+ break;
+ case 126:
+ keysym = QEMU_KEY_UP;
+ break;
+ default:
+ {
+ NSString *ks = [event characters];
+
+ if ([ks length] > 0)
+ keysym = [ks characterAtIndex:0];
+ }
+ }
+ if (keysym)
+ kbd_put_keysym(keysym);
+ }
+ }
+ }
+ break;
+
+ case NSKeyUp:
+ {
+ int keycode = cocoa_keycode_to_qemu([event keyCode]);
+ if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+ }
+ }
+ break;
+
+ case NSMouseMoved:
+ if (grab) {
+ int dx = [event deltaX];
+ int dy = [event deltaY];
+ int dz = [event deltaZ];
+ int buttons = 0;
+ kbd_mouse_event(dx, dy, dz, buttons);
+ }
+ break;
+
+ case NSLeftMouseDown:
+ if (grab) {
+ int buttons = 0;
+
+ /* leftclick+command simulates rightclick */
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ kbd_mouse_event(0, 0, 0, buttons);
+ } else {
+ [NSApp sendEvent: event];
+ }
+ break;
+
+ case NSLeftMouseDragged:
+ if (grab) {
+ int dx = [event deltaX];
+ int dy = [event deltaY];
+ int dz = [event deltaZ];
+ int buttons = 0;
+ if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ kbd_mouse_event(dx, dy, dz, buttons);
+ }
+ break;
+
+ case NSLeftMouseUp:
+ if (grab) {
+ kbd_mouse_event(0, 0, 0, 0);
+ } else {
+ [window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"];
+ [NSCursor hide];
+ CGAssociateMouseAndMouseCursorPosition ( FALSE );
+ grab = 1;
+ //[NSApp sendEvent: event];
+ }
+ break;
+
+ case NSRightMouseDown:
+ if (grab) {
+ int buttons = 0;
+
+ buttons |= MOUSE_EVENT_RBUTTON;
+ kbd_mouse_event(0, 0, 0, buttons);
+ } else {
+ [NSApp sendEvent: event];
+ }
+ break;
+
+ case NSRightMouseDragged:
+ if (grab) {
+ int dx = [event deltaX];
+ int dy = [event deltaY];
+ int dz = [event deltaZ];
+ int buttons = 0;
+ buttons |= MOUSE_EVENT_RBUTTON;
+ kbd_mouse_event(dx, dy, dz, buttons);
+ }
+ break;
+
+ case NSRightMouseUp:
+ if (grab) {
+ kbd_mouse_event(0, 0, 0, 0);
+ } else {
+ [NSApp sendEvent: event];
+ }
+ break;
+
+ case NSOtherMouseDragged:
+ if (grab) {
+ int dx = [event deltaX];
+ int dy = [event deltaY];
+ int dz = [event deltaZ];
+ int buttons = 0;
+ buttons |= MOUSE_EVENT_MBUTTON;
+ kbd_mouse_event(dx, dy, dz, buttons);
+ }
+ break;
+
+ case NSOtherMouseDown:
+ if (grab) {
+ int buttons = 0;
+ buttons |= MOUSE_EVENT_MBUTTON;
+ kbd_mouse_event(0, 0, 0, buttons);
+ } else {
+ [NSApp sendEvent:event];
+ }
+ break;
+
+ case NSOtherMouseUp:
+ if (grab) {
+ kbd_mouse_event(0, 0, 0, 0);
+ } else {
+ [NSApp sendEvent: event];
+ }
+ break;
+
+ case NSScrollWheel:
+ if (grab) {
+ int dz = [event deltaY];
+ kbd_mouse_event(0, 0, -dz, 0);
+ }
+ break;
+
+ default: [NSApp sendEvent:event];
+ }
+ }
+ } while(event != nil);
+}
+
+/*
+ ------------------------------------------------------
+ cocoa_cleanup
+ ------------------------------------------------------
+*/
+
+static void cocoa_cleanup(void)
+{
+
+}
+
+/*
+ ------------------------------------------------------
+ cocoa_display_init
+ ------------------------------------------------------
+*/
+
+void cocoa_display_init(DisplayState *ds, int full_screen)
+{
+ ds->dpy_update = cocoa_update;
+ ds->dpy_resize = cocoa_resize;
+ ds->dpy_refresh = cocoa_refresh;
+
+ cocoa_resize(ds, 640, 400);
+
+ atexit(cocoa_cleanup);
+}
+
+/*
+ ------------------------------------------------------
+ Interface with Cocoa
+ ------------------------------------------------------
+*/
+
+
+/*
+ ------------------------------------------------------
+ QemuWindow
+ Some trick from SDL to use miniwindow
+ ------------------------------------------------------
+*/
+static void QZ_SetPortAlphaOpaque ()
+{
+ /* Assume 32 bit if( bpp == 32 )*/
+ if ( 1 ) {
+
+ uint32_t *pixels = (uint32_t*) current_ds.data;
+ uint32_t rowPixels = current_ds.linesize / 4;
+ uint32_t i, j;
+
+ for (i = 0; i < current_ds.height; i++)
+ for (j = 0; j < current_ds.width; j++) {
+
+ pixels[ (i * rowPixels) + j ] |= 0xFF000000;
+ }
+ }
+}
+
+@implementation QemuWindow
+- (void)miniaturize:(id)sender
+{
+
+ /* make the alpha channel opaque so anim won't have holes in it */
+ QZ_SetPortAlphaOpaque ();
+
+ [ super miniaturize:sender ];
+
+}
+- (void)display
+{
+ /*
+ This method fires just before the window deminaturizes from the Dock.
+
+ We'll save the current visible surface, let the window manager redraw any
+ UI elements, and restore the SDL surface. This way, no expose event
+ is required, and the deminiaturize works perfectly.
+ */
+
+ /* make sure pixels are fully opaque */
+ QZ_SetPortAlphaOpaque ();
+
+ /* save current visible SDL surface */
+ [ self cacheImageInRect:[ qd_view frame ] ];
+
+ /* let the window manager redraw controls, border, etc */
+ [ super display ];
+
+ /* restore visible SDL surface */
+ [ self restoreCachedImage ];
+}
+
+@end
+
+
+/*
+ ------------------------------------------------------
+ QemuCocoaGUIController
+ NSApp's delegate - indeed main object
+ ------------------------------------------------------
+*/
+
+@interface QemuCocoaGUIController : NSObject
+{
+}
+- (void)applicationDidFinishLaunching: (NSNotification *) note;
+- (void)applicationWillTerminate:(NSNotification *)aNotification;
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
+@end
+
+@implementation QemuCocoaGUIController
+/* Called when the internal event loop has just started running */
+- (void)applicationDidFinishLaunching: (NSNotification *) note
+{
+
+ /* Display an open dialog box if no argument were passed or
+ if qemu was launched from the finder ( the Finder passes "-psn" ) */
+
+ if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
+ {
+ NSOpenPanel *op = [[NSOpenPanel alloc] init];
+
+ cocoa_resize(&current_ds, 640, 400);
+
+ [op setPrompt:@"Boot image"];
+
+ [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
+
+ [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
+ modalForWindow:window modalDelegate:self
+ didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
+ }
+ else
+ {
+ /* or Launch Qemu, with the global args */
+ [self startEmulationWithArgc:gArgc argv:gArgv];
+ }
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification
+{
+ printf("Application will terminate\n");
+ qemu_system_shutdown_request();
+ /* In order to avoid a crash */
+ exit(0);
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ if(returnCode == NSCancelButton)
+ {
+ exit(0);
+ }
+
+ if(returnCode == NSOKButton)
+ {
+ char *bin = "qemu";
+ char *img = (char*)[ [ sheet filename ] cString];
+
+ char **argv = (char**)malloc( sizeof(char*)*3 );
+
+ asprintf(&argv[0], "%s", bin);
+ asprintf(&argv[1], "-hda");
+ asprintf(&argv[2], "%s", img);
+
+ printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
+
+ [self startEmulationWithArgc:3 argv:(char**)argv];
+ }
+}
+
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+{
+ int status;
+ /* Launch Qemu */
+ printf("starting qemu...\n");
+ status = qemu_main (argc, argv);
+ exit(status);
+}
+@end
+
+/*
+ ------------------------------------------------------
+ Application Creation
+ ------------------------------------------------------
+*/
+
+/* Dock Connection */
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+/* Menu Creation */
+static void setApplicationMenu(void)
+{
+ /* warning: this code is very odd */
+ NSMenu *appleMenu;
+ NSMenuItem *menuItem;
+ NSString *title;
+ NSString *appName;
+
+ appName = @"Qemu";
+ appleMenu = [[NSMenu alloc] initWithTitle:@""];
+
+ /* Add menu items */
+ title = [@"About " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Hide " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+ menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+
+ [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Quit " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+
+ /* Put menu into the menubar */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:appleMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Tell the application object that this is now the application menu */
+ [NSApp setAppleMenu:appleMenu];
+
+ /* Finally give up our references to the objects */
+ [appleMenu release];
+ [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu(void)
+{
+ NSMenu *windowMenu;
+ NSMenuItem *windowMenuItem;
+ NSMenuItem *menuItem;
+
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ /* "Minimize" item */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [windowMenu addItem:menuItem];
+ [menuItem release];
+
+ /* Put menu into the menubar */
+ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+ [windowMenuItem setSubmenu:windowMenu];
+ [[NSApp mainMenu] addItem:windowMenuItem];
+
+ /* Tell the application object that this is now the window menu */
+ [NSApp setWindowsMenu:windowMenu];
+
+ /* Finally give up our references to the objects */
+ [windowMenu release];
+ [windowMenuItem release];
+}
+
+static void CustomApplicationMain(void)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ QemuCocoaGUIController *gui_controller;
+ CPSProcessSerNum PSN;
+
+ [NSApplication sharedApplication];
+
+ if (!CPSGetCurrentProcess(&PSN))
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [NSApplication sharedApplication];
+
+ /* Set up the menubar */
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+ setApplicationMenu();
+ setupWindowMenu();
+
+ /* Create SDLMain and make it the app delegate */
+ gui_controller = [[QemuCocoaGUIController alloc] init];
+ [NSApp setDelegate:gui_controller];
+
+ /* Start the main event loop */
+ [NSApp run];
+
+ [gui_controller release];
+ [pool release];
+}
+
+/* Real main of qemu-cocoa */
+int main(int argc, char **argv)
+{
+ gArgc = argc;
+ gArgv = argv;
+
+ CustomApplicationMain();
+
+ return 0;
+}
diff --git a/configure b/configure
new file mode 100755
index 0000000..e6c74e4
--- /dev/null
+++ b/configure
@@ -0,0 +1,906 @@
+#!/bin/sh
+#
+# qemu configure script (c) 2003 Fabrice Bellard
+#
+# set temporary file name
+if test ! -z "$TMPDIR" ; then
+ TMPDIR1="${TMPDIR}"
+elif test ! -z "$TEMPDIR" ; then
+ TMPDIR1="${TEMPDIR}"
+else
+ TMPDIR1="/tmp"
+fi
+
+TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
+TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
+TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}"
+TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S"
+
+# default parameters
+prefix=""
+interp_prefix="/usr/gnemul/qemu-%M"
+static="no"
+cross_prefix=""
+cc="gcc"
+host_cc="gcc"
+ar="ar"
+make="make"
+install="install"
+strip="strip"
+cpu=`uname -m`
+target_list=""
+case "$cpu" in
+ i386|i486|i586|i686|i86pc|BePC)
+ cpu="i386"
+ ;;
+ armv*b)
+ cpu="armv4b"
+ ;;
+ armv*l)
+ cpu="armv4l"
+ ;;
+ alpha)
+ cpu="alpha"
+ ;;
+ "Power Macintosh"|ppc|ppc64)
+ cpu="powerpc"
+ ;;
+ mips)
+ cpu="mips"
+ ;;
+ s390)
+ cpu="s390"
+ ;;
+ sparc|sun4[muv])
+ cpu="sparc"
+ ;;
+ sparc64)
+ cpu="sparc64"
+ ;;
+ ia64)
+ cpu="ia64"
+ ;;
+ m68k)
+ cpu="m68k"
+ ;;
+ x86_64|amd64)
+ cpu="x86_64"
+ ;;
+ *)
+ cpu="unknown"
+ ;;
+esac
+gprof="no"
+bigendian="no"
+mingw32="no"
+EXESUF=""
+gdbstub="yes"
+slirp="yes"
+adlib="no"
+oss="no"
+dsound="no"
+coreaudio="no"
+alsa="no"
+fmod="no"
+fmod_lib=""
+fmod_inc=""
+bsd="no"
+linux="no"
+kqemu="no"
+profiler="no"
+kernel_path=""
+cocoa="no"
+check_gfx="yes"
+check_gcc="yes"
+softmmu="yes"
+user="no"
+build_docs="no"
+build_acpi_tables="no"
+uname_release=""
+
+# OS specific
+targetos=`uname -s`
+case $targetos in
+CYGWIN*)
+mingw32="yes"
+CFLAGS="-O2 -mno-cygwin"
+;;
+MINGW32*)
+mingw32="yes"
+;;
+FreeBSD)
+bsd="yes"
+oss="yes"
+if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
+ kqemu="yes"
+fi
+;;
+NetBSD)
+bsd="yes"
+oss="yes"
+;;
+OpenBSD)
+bsd="yes"
+oss="yes"
+;;
+Darwin)
+bsd="yes"
+darwin="yes"
+;;
+SunOS)
+solaris="yes"
+;;
+*)
+oss="yes"
+linux="yes"
+user="yes"
+if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
+ kqemu="yes"
+fi
+;;
+esac
+
+if [ "$bsd" = "yes" ] ; then
+ if [ "$darwin" != "yes" ] ; then
+ make="gmake"
+ fi
+fi
+
+if [ "$solaris" = "yes" ] ; then
+ make="gmake"
+ install="ginstall"
+ solarisrev=`uname -r | cut -f2 -d.`
+fi
+
+# find source path
+source_path=`dirname "$0"`
+if [ -z "$source_path" ]; then
+ source_path=`pwd`
+else
+ source_path=`cd "$source_path"; pwd`
+fi
+if test "$source_path" = `pwd` ; then
+ source_path_used="no"
+else
+ source_path_used="yes"
+fi
+
+for opt do
+ optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
+ case "$opt" in
+ --help|-h) show_help=yes
+ ;;
+ --prefix=*) prefix="$optarg"
+ ;;
+ --interp-prefix=*) interp_prefix="$optarg"
+ ;;
+ --source-path=*) source_path="$optarg"
+ source_path_used="yes"
+ ;;
+ --cross-prefix=*) cross_prefix="$optarg"
+ ;;
+ --cc=*) cc="$optarg"
+ ;;
+ --host-cc=*) host_cc="$optarg"
+ ;;
+ --make=*) make="$optarg"
+ ;;
+ --install=*) install="$optarg"
+ ;;
+ --extra-cflags=*) CFLAGS="$optarg"
+ ;;
+ --extra-ldflags=*) LDFLAGS="$optarg"
+ ;;
+ --cpu=*) cpu="$optarg"
+ ;;
+ --target-list=*) target_list="$optarg"
+ ;;
+ --enable-gprof) gprof="yes"
+ ;;
+ --static) static="yes"
+ ;;
+ --disable-sdl) sdl="no"
+ ;;
+ --enable-coreaudio) coreaudio="yes"
+ ;;
+ --enable-alsa) alsa="yes"
+ ;;
+ --enable-dsound) dsound="yes"
+ ;;
+ --enable-fmod) fmod="yes"
+ ;;
+ --fmod-lib=*) fmod_lib="$optarg"
+ ;;
+ --fmod-inc=*) fmod_inc="$optarg"
+ ;;
+ --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; user="no"
+ ;;
+ --disable-slirp) slirp="no"
+ ;;
+ --enable-adlib) adlib="yes"
+ ;;
+ --disable-kqemu) kqemu="no"
+ ;;
+ --enable-profiler) profiler="yes"
+ ;;
+ --kernel-path=*) kernel_path="$optarg"
+ ;;
+ --enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no"
+ ;;
+ --disable-gfx-check) check_gfx="no"
+ ;;
+ --disable-gcc-check) check_gcc="no"
+ ;;
+ --disable-system) softmmu="no"
+ ;;
+ --enable-system) softmmu="yes"
+ ;;
+ --disable-user) user="no"
+ ;;
+ --enable-user) user="yes"
+ ;;
+ --enable-uname-release=*) uname_release="$optarg"
+ ;;
+ --enable-iasl) build_acpi_tables="yes"
+ ;;
+ esac
+done
+
+# Checking for CFLAGS
+if test -z "$CFLAGS"; then
+ CFLAGS="-O2"
+fi
+
+if test x"$show_help" = x"yes" ; then
+cat << EOF
+
+Usage: configure [options]
+Options: [defaults in brackets after descriptions]
+
+EOF
+echo "Standard options:"
+echo " --help print this message"
+echo " --prefix=PREFIX install in PREFIX [$prefix]"
+echo " --interp-prefix=PREFIX where to find shared libraries, etc."
+echo " use %M for cpu name [$interp_prefix]"
+echo " --target-list=LIST set target list [$target_list]"
+echo ""
+echo "kqemu kernel acceleration support:"
+echo " --disable-kqemu disable kqemu support"
+echo " --kernel-path=PATH set the kernel path (configure probes it)"
+echo ""
+echo "Advanced options (experts only):"
+echo " --source-path=PATH path of source code [$source_path]"
+echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
+echo " --cc=CC use C compiler CC [$cc]"
+echo " --host-cc=CC use C compiler CC [$host_cc] for dyngen etc."
+echo " --make=MAKE use specified make [$make]"
+echo " --install=INSTALL use specified install [$install]"
+echo " --static enable static build [$static]"
+echo " --enable-cocoa enable COCOA (Mac OS X only)"
+echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
+echo " --enable-adlib enable Adlib emulation"
+echo " --enable-coreaudio enable Coreaudio audio driver"
+echo " --enable-alsa enable ALSA audio driver"
+echo " --enable-fmod enable FMOD audio driver"
+echo " --enabled-dsound enable DirectSound audio driver"
+echo " --enable-system enable all system emulation targets"
+echo " --disable-system disable all system emulation targets"
+echo " --enable-user enable all linux usermode emulation targets"
+echo " --disable-user disable all linux usermode emulation targets"
+echo " --fmod-lib path to FMOD library"
+echo " --fmod-inc path to FMOD includes"
+echo " --enable-uname-release=R Return R for uname -r in usermode emulation"
+echo " --enable-iasl compilation of ACPI tables with the IASL compiler"
+echo ""
+echo "NOTE: The object files are build at the place where configure is launched"
+exit 1
+fi
+
+cc="${cross_prefix}${cc}"
+ar="${cross_prefix}${ar}"
+strip="${cross_prefix}${strip}"
+
+# check that the C compiler works.
+cat > $TMPC <<EOF
+int main(void) {}
+EOF
+
+if $cc -c -o $TMPO $TMPC 2>/dev/null ; then
+ : C compiler works ok
+else
+ echo "ERROR: \"$cc\" either does not exist or does not work"
+ exit 1
+fi
+
+if test "$mingw32" = "yes" ; then
+ linux="no"
+ EXESUF=".exe"
+ oss="no"
+ if [ "$cpu" = "i386" ] ; then
+ kqemu="yes"
+ fi
+fi
+
+#
+# Solaris specific configure tool chain decisions
+#
+if test "$solaris" = "yes" ; then
+ #
+ # gcc for solaris 10/fcs in /usr/sfw/bin doesn't compile qemu correctly
+ # override the check with --disable-gcc-check
+ #
+ if test "$solarisrev" -eq 10 -a "$check_gcc" = "yes" ; then
+ solgcc=`which $cc`
+ if test "$solgcc" = "/usr/sfw/bin/gcc" ; then
+ echo "Solaris 10/FCS gcc in /usr/sfw/bin will not compiled qemu correctly."
+ echo "please get gcc-3.4.3 or later, from www.blastwave.org using pkg-get -i gcc3"
+ echo "or get the latest patch from SunSolve for gcc"
+ exit 1
+ fi
+ fi
+ solinst=`which $install 2> /dev/null | /usr/bin/grep -v "no $install in"`
+ if test -z "$solinst" ; then
+ echo "Solaris install program not found. Use --install=/usr/ucb/install or"
+ echo "install fileutils from www.blastwave.org using pkg-get -i fileutils"
+ echo "to get ginstall which is used by default (which lives in /opt/csw/bin)"
+ exit 1
+ fi
+ if test "$solinst" = "/usr/sbin/install" ; then
+ echo "Error: Solaris /usr/sbin/install is not an appropriate install program."
+ echo "try ginstall from the GNU fileutils available from www.blastwave.org"
+ echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install"
+ exit 1
+ fi
+ sol_ar=`which ar 2> /dev/null | /usr/bin/grep -v "no ar in"`
+ if test -z "$sol_ar" ; then
+ echo "Error: No path includes ar"
+ if test -f /usr/ccs/bin/ar ; then
+ echo "Add /usr/ccs/bin to your path and rerun configure"
+ fi
+ exit 1
+ fi
+fi
+
+
+if test -z "$target_list" ; then
+# these targets are portable
+ if [ "$softmmu" = "yes" ] ; then
+ target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu arm-softmmu"
+ fi
+# the following are Linux specific
+ if [ "$user" = "yes" ] ; then
+ target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list"
+ fi
+else
+ target_list=`echo "$target_list" | sed -e 's/,/ /g'`
+fi
+if test -z "$target_list" ; then
+ echo "No targets enabled"
+ exit 1
+fi
+
+if test -z "$cross_prefix" ; then
+
+# ---
+# big/little endian test
+cat > $TMPC << EOF
+#include <inttypes.h>
+int main(int argc, char ** argv){
+ volatile uint32_t i=0x01234567;
+ return (*((uint8_t*)(&i))) == 0x67;
+}
+EOF
+
+if $cc -o $TMPE $TMPC 2>/dev/null ; then
+$TMPE && bigendian="yes"
+else
+echo big/little test failed
+fi
+
+else
+
+# if cross compiling, cannot launch a program, so make a static guess
+if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64" -o "$cpu" = "m68k" -o "$cpu" = "armv4b"; then
+ bigendian="yes"
+fi
+
+fi
+
+# host long bits test
+hostlongbits="32"
+if test "$cpu" = "sparc64" -o "$cpu" = "ia64" -o "$cpu" = "x86_64" -o "$cpu" = "alpha"; then
+ hostlongbits="64"
+fi
+
+# check gcc options support
+cat > $TMPC <<EOF
+int main(void) {
+}
+EOF
+
+have_gcc3_options="no"
+if $cc -fno-reorder-blocks -fno-optimize-sibling-calls -o $TMPO $TMPC 2> /dev/null ; then
+ have_gcc3_options="yes"
+fi
+
+# Check for gcc4, error if pre-gcc4
+if test "$check_gcc" = "yes" ; then
+ cat > $TMPC <<EOF
+#if __GNUC__ < 4
+#error gcc3
+#endif
+int main(){return 0;}
+EOF
+ if $cc -o $TMPO $TMPC 2>/dev/null ; then
+ echo "ERROR: \"$cc\" looks like gcc 4.x"
+ echo "QEMU is known to have problems when compiled with gcc 4.x"
+ echo "It is recommended that you use gcc 3.x to build QEMU"
+ echo "To use this compiler anyway, configure with --disable-gcc-check"
+ exit 1;
+ fi
+fi
+
+##########################################
+# SDL probe
+
+sdl_too_old=no
+
+if test -z "$sdl" ; then
+
+sdl_config="sdl-config"
+sdl=no
+sdl_static=no
+
+if test "$mingw32" = "yes" -a ! -z "$cross_prefix" ; then
+# win32 cross compilation case
+ sdl_config="i386-mingw32msvc-sdl-config"
+ sdl=yes
+else
+# normal SDL probe
+cat > $TMPC << EOF
+#include <SDL.h>
+#undef main /* We don't want SDL to override our main() */
+int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
+EOF
+
+if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC `$sdl_config --libs 2> /dev/null` 2> /dev/null ; then
+_sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'`
+if test "$_sdlversion" -lt 121 ; then
+sdl_too_old=yes
+else
+sdl=yes
+fi
+
+# static link with sdl ?
+if test "$sdl" = "yes" ; then
+aa="no"
+`$sdl_config --static-libs | grep \\\-laa > /dev/null` && aa="yes"
+sdl_static_libs=`$sdl_config --static-libs`
+if [ "$aa" = "yes" ] ; then
+ sdl_static_libs="$sdl_static_libs `aalib-config --static-libs`"
+fi
+
+if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC $sdl_static_libs 2> /dev/null; then
+ sdl_static=yes
+fi
+
+fi # static link
+
+fi # sdl compile test
+
+fi # cross compilation
+fi # -z $sdl
+
+# Check if tools are available to build documentation.
+if [ -x "`which texi2html`" ] && [ -x "`which pod2man`" ]; then
+ build_docs="yes"
+fi
+
+if test "$mingw32" = "yes" ; then
+if test -z "$prefix" ; then
+ prefix="/c/Program Files/Qemu"
+fi
+mandir="$prefix"
+datadir="$prefix"
+docdir="$prefix"
+bindir="$prefix"
+else
+if test -z "$prefix" ; then
+ prefix="/usr/local"
+fi
+mandir="$prefix/share/man"
+datadir="$prefix/share/qemu"
+docdir="$prefix/share/doc/qemu"
+bindir="$prefix/bin"
+fi
+
+echo "Install prefix $prefix"
+echo "BIOS directory $datadir"
+echo "binary directory $bindir"
+if test "$mingw32" = "no" ; then
+echo "Manual directory $mandir"
+echo "ELF interp prefix $interp_prefix"
+fi
+echo "Source path $source_path"
+echo "C compiler $cc"
+echo "Host C compiler $host_cc"
+echo "make $make"
+echo "install $install"
+echo "host CPU $cpu"
+echo "host big endian $bigendian"
+echo "target list $target_list"
+echo "gprof enabled $gprof"
+echo "profiler $profiler"
+echo "static build $static"
+if test "$darwin" = "yes" ; then
+ echo "Cocoa support $cocoa"
+fi
+echo "SDL support $sdl"
+if test "$sdl" != "no" ; then
+ echo "SDL static link $sdl_static"
+fi
+echo "mingw32 support $mingw32"
+echo "Adlib support $adlib"
+echo "CoreAudio support $coreaudio"
+echo "ALSA support $alsa"
+echo "DSound support $dsound"
+if test "$fmod" = "yes"; then
+ if test -z $fmod_lib || test -z $fmod_inc; then
+ echo
+ echo "Error: You must specify path to FMOD library and headers"
+ echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
+ echo
+ exit 1
+ fi
+ fmod_support=" (lib='$fmod_lib' include='$fmod_inc')"
+else
+ fmod_support=""
+fi
+echo "FMOD support $fmod $fmod_support"
+echo "kqemu support $kqemu"
+echo "Documentation $build_docs"
+[ ! -z "$uname_release" ] && \
+echo "uname -r $uname_release"
+
+if test $sdl_too_old = "yes"; then
+echo "-> Your SDL version is too old - please upgrade to have SDL support"
+fi
+#if test "$sdl_static" = "no"; then
+# echo "WARNING: cannot compile statically with SDL - qemu-fast won't have a graphical output"
+#fi
+config_mak="config-host.mak"
+config_h="config-host.h"
+
+#echo "Creating $config_mak and $config_h"
+
+echo "# Automatically generated by configure - do not modify" > $config_mak
+echo "# Configured with: $0 $@" >> $config_mak
+echo "/* Automatically generated by configure - do not modify */" > $config_h
+
+echo "prefix=$prefix" >> $config_mak
+echo "bindir=$bindir" >> $config_mak
+echo "mandir=$mandir" >> $config_mak
+echo "datadir=$datadir" >> $config_mak
+echo "docdir=$docdir" >> $config_mak
+echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h
+echo "MAKE=$make" >> $config_mak
+echo "INSTALL=$install" >> $config_mak
+echo "CC=$cc" >> $config_mak
+if test "$have_gcc3_options" = "yes" ; then
+ echo "HAVE_GCC3_OPTIONS=yes" >> $config_mak
+fi
+echo "HOST_CC=$host_cc" >> $config_mak
+echo "AR=$ar" >> $config_mak
+echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak
+echo "CFLAGS=$CFLAGS" >> $config_mak
+echo "LDFLAGS=$LDFLAGS" >> $config_mak
+echo "EXESUF=$EXESUF" >> $config_mak
+if test "$cpu" = "i386" ; then
+ echo "ARCH=i386" >> $config_mak
+ echo "#define HOST_I386 1" >> $config_h
+elif test "$cpu" = "x86_64" ; then
+ echo "ARCH=x86_64" >> $config_mak
+ echo "#define HOST_X86_64 1" >> $config_h
+elif test "$cpu" = "armv4b" ; then
+ echo "ARCH=arm" >> $config_mak
+ echo "#define HOST_ARM 1" >> $config_h
+elif test "$cpu" = "armv4l" ; then
+ echo "ARCH=arm" >> $config_mak
+ echo "#define HOST_ARM 1" >> $config_h
+elif test "$cpu" = "powerpc" ; then
+ echo "ARCH=ppc" >> $config_mak
+ echo "#define HOST_PPC 1" >> $config_h
+elif test "$cpu" = "mips" ; then
+ echo "ARCH=mips" >> $config_mak
+ echo "#define HOST_MIPS 1" >> $config_h
+elif test "$cpu" = "s390" ; then
+ echo "ARCH=s390" >> $config_mak
+ echo "#define HOST_S390 1" >> $config_h
+elif test "$cpu" = "alpha" ; then
+ echo "ARCH=alpha" >> $config_mak
+ echo "#define HOST_ALPHA 1" >> $config_h
+elif test "$cpu" = "sparc" ; then
+ echo "ARCH=sparc" >> $config_mak
+ echo "#define HOST_SPARC 1" >> $config_h
+elif test "$cpu" = "sparc64" ; then
+ echo "ARCH=sparc64" >> $config_mak
+ echo "#define HOST_SPARC64 1" >> $config_h
+elif test "$cpu" = "ia64" ; then
+ echo "ARCH=ia64" >> $config_mak
+ echo "#define HOST_IA64 1" >> $config_h
+elif test "$cpu" = "m68k" ; then
+ echo "ARCH=m68k" >> $config_mak
+ echo "#define HOST_M68K 1" >> $config_h
+else
+ echo "Unsupported CPU"
+ exit 1
+fi
+if test "$bigendian" = "yes" ; then
+ echo "WORDS_BIGENDIAN=yes" >> $config_mak
+ echo "#define WORDS_BIGENDIAN 1" >> $config_h
+fi
+echo "#define HOST_LONG_BITS $hostlongbits" >> $config_h
+if test "$mingw32" = "yes" ; then
+ echo "CONFIG_WIN32=yes" >> $config_mak
+ echo "#define CONFIG_WIN32 1" >> $config_h
+elif test -f "/usr/include/byteswap.h" ; then
+ echo "#define HAVE_BYTESWAP_H 1" >> $config_h
+fi
+if test "$darwin" = "yes" ; then
+ echo "CONFIG_DARWIN=yes" >> $config_mak
+ echo "#define CONFIG_DARWIN 1" >> $config_h
+fi
+if test "$solaris" = "yes" ; then
+ echo "CONFIG_SOLARIS=yes" >> $config_mak
+ echo "#define HOST_SOLARIS $solarisrev" >> $config_h
+fi
+if test "$gdbstub" = "yes" ; then
+ echo "CONFIG_GDBSTUB=yes" >> $config_mak
+ echo "#define CONFIG_GDBSTUB 1" >> $config_h
+fi
+if test "$gprof" = "yes" ; then
+ echo "TARGET_GPROF=yes" >> $config_mak
+ echo "#define HAVE_GPROF 1" >> $config_h
+fi
+if test "$static" = "yes" ; then
+ echo "CONFIG_STATIC=yes" >> $config_mak
+ echo "#define CONFIG_STATIC 1" >> $config_h
+fi
+if test $profiler = "yes" ; then
+ echo "#define CONFIG_PROFILER 1" >> $config_h
+fi
+if test "$slirp" = "yes" ; then
+ echo "CONFIG_SLIRP=yes" >> $config_mak
+ echo "#define CONFIG_SLIRP 1" >> $config_h
+fi
+if test "$adlib" = "yes" ; then
+ echo "CONFIG_ADLIB=yes" >> $config_mak
+ echo "#define CONFIG_ADLIB 1" >> $config_h
+fi
+if test "$oss" = "yes" ; then
+ echo "CONFIG_OSS=yes" >> $config_mak
+ echo "#define CONFIG_OSS 1" >> $config_h
+fi
+if test "$coreaudio" = "yes" ; then
+ echo "CONFIG_COREAUDIO=yes" >> $config_mak
+ echo "#define CONFIG_COREAUDIO 1" >> $config_h
+fi
+if test "$alsa" = "yes" ; then
+ echo "CONFIG_ALSA=yes" >> $config_mak
+ echo "#define CONFIG_ALSA 1" >> $config_h
+fi
+if test "$dsound" = "yes" ; then
+ echo "CONFIG_DSOUND=yes" >> $config_mak
+ echo "#define CONFIG_DSOUND 1" >> $config_h
+fi
+if test "$fmod" = "yes" ; then
+ echo "CONFIG_FMOD=yes" >> $config_mak
+ echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak
+ echo "CONFIG_FMOD_INC=$fmod_inc" >> $config_mak
+ echo "#define CONFIG_FMOD 1" >> $config_h
+fi
+qemu_version=`head $source_path/VERSION`
+echo "VERSION=$qemu_version" >>$config_mak
+echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
+
+echo "SRC_PATH=$source_path" >> $config_mak
+if [ "$source_path_used" = "yes" ]; then
+ echo "VPATH=$source_path" >> $config_mak
+fi
+echo "TARGET_DIRS=$target_list" >> $config_mak
+if [ "$build_docs" = "yes" ] ; then
+ echo "BUILD_DOCS=yes" >> $config_mak
+fi
+if [ "$build_acpi_tables" = "yes" ] ; then
+ echo "BUILD_ACPI_TABLES=yes" >> $config_mak
+fi
+
+# XXX: suppress that
+if [ "$bsd" = "yes" ] ; then
+ echo "#define O_LARGEFILE 0" >> $config_h
+ echo "#define MAP_ANONYMOUS MAP_ANON" >> $config_h
+ echo "#define _BSD 1" >> $config_h
+fi
+
+echo "#define CONFIG_UNAME_RELEASE \"$uname_release\"" >> $config_h
+
+for target in $target_list; do
+target_dir="$target"
+config_mak=$target_dir/config.mak
+config_h=$target_dir/config.h
+target_cpu=`echo $target | cut -d '-' -f 1`
+target_bigendian="no"
+[ "$target_cpu" = "armeb" ] && target_bigendian=yes
+[ "$target_cpu" = "sparc" ] && target_bigendian=yes
+[ "$target_cpu" = "sparc64" ] && target_bigendian=yes
+[ "$target_cpu" = "ppc" ] && target_bigendian=yes
+[ "$target_cpu" = "ppc64" ] && target_bigendian=yes
+[ "$target_cpu" = "mips" ] && target_bigendian=yes
+[ "$target_cpu" = "sh4eb" ] && target_bigendian=yes
+target_softmmu="no"
+if expr $target : '.*-softmmu' > /dev/null ; then
+ target_softmmu="yes"
+fi
+target_user_only="no"
+if expr $target : '.*-user' > /dev/null ; then
+ target_user_only="yes"
+fi
+
+if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \
+ -a "$sdl" = "no" -a "$cocoa" = "no" ; then
+ echo "ERROR: QEMU requires SDL or Cocoa for graphical output"
+ echo "To build QEMU without graphical output configure with --disable-gfx-check"
+ echo "Note that this will disable all output from the virtual graphics card."
+ exit 1;
+fi
+
+#echo "Creating $config_mak, $config_h and $target_dir/Makefile"
+
+mkdir -p $target_dir
+mkdir -p $target_dir/fpu
+if test "$target" = "arm-user" -o "$target" = "armeb-user" ; then
+ mkdir -p $target_dir/nwfpe
+fi
+if test "$target_user_only" = "no" ; then
+ mkdir -p $target_dir/slirp
+fi
+
+#
+# don't use ln -sf as not all "ln -sf" over write the file/link
+#
+rm -f $target_dir/Makefile
+ln -s $source_path/Makefile.target $target_dir/Makefile
+
+
+echo "# Automatically generated by configure - do not modify" > $config_mak
+echo "/* Automatically generated by configure - do not modify */" > $config_h
+
+
+echo "include ../config-host.mak" >> $config_mak
+echo "#include \"../config-host.h\"" >> $config_h
+
+bflt="no"
+interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
+echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
+
+if test "$target_cpu" = "i386" ; then
+ echo "TARGET_ARCH=i386" >> $config_mak
+ echo "#define TARGET_ARCH \"i386\"" >> $config_h
+ echo "#define TARGET_I386 1" >> $config_h
+ if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "i386" ; then
+ echo "#define USE_KQEMU 1" >> $config_h
+ fi
+elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
+ echo "TARGET_ARCH=arm" >> $config_mak
+ echo "#define TARGET_ARCH \"arm\"" >> $config_h
+ echo "#define TARGET_ARM 1" >> $config_h
+ bflt="yes"
+elif test "$target_cpu" = "sparc" ; then
+ echo "TARGET_ARCH=sparc" >> $config_mak
+ echo "#define TARGET_ARCH \"sparc\"" >> $config_h
+ echo "#define TARGET_SPARC 1" >> $config_h
+elif test "$target_cpu" = "sparc64" ; then
+ echo "TARGET_ARCH=sparc64" >> $config_mak
+ echo "#define TARGET_ARCH \"sparc64\"" >> $config_h
+ echo "#define TARGET_SPARC 1" >> $config_h
+ echo "#define TARGET_SPARC64 1" >> $config_h
+elif test "$target_cpu" = "ppc" ; then
+ echo "TARGET_ARCH=ppc" >> $config_mak
+ echo "#define TARGET_ARCH \"ppc\"" >> $config_h
+ echo "#define TARGET_PPC 1" >> $config_h
+elif test "$target_cpu" = "ppc64" ; then
+ echo "TARGET_ARCH=ppc64" >> $config_mak
+ echo "#define TARGET_ARCH \"ppc64\"" >> $config_h
+ echo "#define TARGET_PPC 1" >> $config_h
+ echo "#define TARGET_PPC64 1" >> $config_h
+elif test "$target_cpu" = "x86_64" ; then
+ echo "TARGET_ARCH=x86_64" >> $config_mak
+ echo "#define TARGET_ARCH \"x86_64\"" >> $config_h
+ echo "#define TARGET_I386 1" >> $config_h
+ echo "#define TARGET_X86_64 1" >> $config_h
+ if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then
+ echo "#define USE_KQEMU 1" >> $config_h
+ fi
+elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
+ echo "TARGET_ARCH=mips" >> $config_mak
+ echo "#define TARGET_ARCH \"mips\"" >> $config_h
+ echo "#define TARGET_MIPS 1" >> $config_h
+ echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
+ echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
+elif test "$target_cpu" = "sh4" -o "$target_cpu" = "sh4eb" ; then
+ echo "TARGET_ARCH=sh4" >> $config_mak
+ echo "#define TARGET_ARCH \"sh4\"" >> $config_h
+ echo "#define TARGET_SH4 1" >> $config_h
+ bflt="yes"
+else
+ echo "Unsupported target CPU"
+ exit 1
+fi
+if test "$target_bigendian" = "yes" ; then
+ echo "TARGET_WORDS_BIGENDIAN=yes" >> $config_mak
+ echo "#define TARGET_WORDS_BIGENDIAN 1" >> $config_h
+fi
+if test "$target_softmmu" = "yes" ; then
+ echo "CONFIG_SOFTMMU=yes" >> $config_mak
+ echo "#define CONFIG_SOFTMMU 1" >> $config_h
+fi
+if test "$target_user_only" = "yes" ; then
+ echo "CONFIG_USER_ONLY=yes" >> $config_mak
+ echo "#define CONFIG_USER_ONLY 1" >> $config_h
+fi
+
+if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" -o "$target_cpu" = "sparc" -o "$target_cpu" = "sparc64"; then
+ echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
+ echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
+fi
+if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then
+ echo "TARGET_HAS_BFLT=yes" >> $config_mak
+ echo "#define TARGET_HAS_BFLT 1" >> $config_h
+fi
+# sdl defines
+
+if test "$target_user_only" = "no"; then
+ if test "$target_softmmu" = "no" -o "$static" = "yes"; then
+ sdl1=$sdl_static
+ else
+ sdl1=$sdl
+ fi
+ if test "$sdl1" = "yes" ; then
+ echo "#define CONFIG_SDL 1" >> $config_h
+ echo "CONFIG_SDL=yes" >> $config_mak
+ if test "$target_softmmu" = "no" -o "$static" = "yes"; then
+ echo "SDL_LIBS=$sdl_static_libs" >> $config_mak
+ else
+ echo "SDL_LIBS=`$sdl_config --libs`" >> $config_mak
+ fi
+ if [ "${aa}" = "yes" ] ; then
+ echo "SDL_CFLAGS=`$sdl_config --cflags` `aalib-config --cflags`" >> $config_mak
+ else
+ echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
+ fi
+ fi
+fi
+
+if test "$cocoa" = "yes" ; then
+ echo "#define CONFIG_COCOA 1" >> $config_h
+ echo "CONFIG_COCOA=yes" >> $config_mak
+fi
+
+done # for target in $targets
+
+# build tree in object directory if source path is different from current one
+if test "$source_path_used" = "yes" ; then
+ DIRS="tests"
+ FILES="Makefile tests/Makefile"
+ for dir in $DIRS ; do
+ mkdir -p $dir
+ done
+ # remove the link and recreate it, as not all "ln -sf" overwrite the link
+ for f in $FILES ; do
+ rm -f $f
+ ln -s $source_path/$f $f
+ done
+fi
+
+rm -f $TMPO $TMPC $TMPE $TMPS
diff --git a/console.c b/console.c
new file mode 100644
index 0000000..f039dfc
--- /dev/null
+++ b/console.c
@@ -0,0 +1,1090 @@
+/*
+ * QEMU graphical console
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_CONSOLE
+#define DEFAULT_BACKSCROLL 512
+#define MAX_CONSOLES 12
+
+#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
+
+typedef struct TextAttributes {
+ uint8_t fgcol:4;
+ uint8_t bgcol:4;
+ uint8_t bold:1;
+ uint8_t uline:1;
+ uint8_t blink:1;
+ uint8_t invers:1;
+ uint8_t unvisible:1;
+} TextAttributes;
+
+typedef struct TextCell {
+ uint8_t ch;
+ TextAttributes t_attrib;
+} TextCell;
+
+#define MAX_ESC_PARAMS 3
+
+enum TTYState {
+ TTY_STATE_NORM,
+ TTY_STATE_ESC,
+ TTY_STATE_CSI,
+};
+
+typedef struct QEMUFIFO {
+ uint8_t *buf;
+ int buf_size;
+ int count, wptr, rptr;
+} QEMUFIFO;
+
+int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
+{
+ int l, len;
+
+ l = f->buf_size - f->count;
+ if (len1 > l)
+ len1 = l;
+ len = len1;
+ while (len > 0) {
+ l = f->buf_size - f->wptr;
+ if (l > len)
+ l = len;
+ memcpy(f->buf + f->wptr, buf, l);
+ f->wptr += l;
+ if (f->wptr >= f->buf_size)
+ f->wptr = 0;
+ buf += l;
+ len -= l;
+ }
+ f->count += len1;
+ return len1;
+}
+
+int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
+{
+ int l, len;
+
+ if (len1 > f->count)
+ len1 = f->count;
+ len = len1;
+ while (len > 0) {
+ l = f->buf_size - f->rptr;
+ if (l > len)
+ l = len;
+ memcpy(buf, f->buf + f->rptr, l);
+ f->rptr += l;
+ if (f->rptr >= f->buf_size)
+ f->rptr = 0;
+ buf += l;
+ len -= l;
+ }
+ f->count -= len1;
+ return len1;
+}
+
+/* ??? This is mis-named.
+ It is used for both text and graphical consoles. */
+struct TextConsole {
+ int text_console; /* true if text console */
+ DisplayState *ds;
+ /* Graphic console state. */
+ vga_hw_update_ptr hw_update;
+ vga_hw_invalidate_ptr hw_invalidate;
+ vga_hw_screen_dump_ptr hw_screen_dump;
+ void *hw;
+
+ int g_width, g_height;
+ int width;
+ int height;
+ int total_height;
+ int backscroll_height;
+ int x, y;
+ int y_displayed;
+ int y_base;
+ TextAttributes t_attrib_default; /* default text attributes */
+ TextAttributes t_attrib; /* currently active text attributes */
+ TextCell *cells;
+
+ enum TTYState state;
+ int esc_params[MAX_ESC_PARAMS];
+ int nb_esc_params;
+
+ /* kbd read handler */
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ /* fifo for key pressed */
+ QEMUFIFO out_fifo;
+ uint8_t out_fifo_buf[16];
+ QEMUTimer *kbd_timer;
+};
+
+static TextConsole *active_console;
+static TextConsole *consoles[MAX_CONSOLES];
+static int nb_consoles = 0;
+
+void vga_hw_update(void)
+{
+ if (active_console->hw_update)
+ active_console->hw_update(active_console->hw);
+}
+
+void vga_hw_invalidate(void)
+{
+ if (active_console->hw_invalidate)
+ active_console->hw_invalidate(active_console->hw);
+}
+
+void vga_hw_screen_dump(const char *filename)
+{
+ /* There is currently no was of specifying which screen we want to dump,
+ so always dump the dirst one. */
+ if (consoles[0]->hw_screen_dump)
+ consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
+}
+
+/* convert a RGBA color to a color index usable in graphic primitives */
+static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
+{
+ unsigned int r, g, b, color;
+
+ switch(ds->depth) {
+#if 0
+ case 8:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = (rgb_to_index[r] * 6 * 6) +
+ (rgb_to_index[g] * 6) +
+ (rgb_to_index[b]);
+ break;
+#endif
+ case 15:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+ break;
+ case 16:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+ break;
+ case 32:
+ default:
+ color = rgba;
+ break;
+ }
+ return color;
+}
+
+static void vga_fill_rect (DisplayState *ds,
+ int posx, int posy, int width, int height, uint32_t color)
+{
+ uint8_t *d, *d1;
+ int x, y, bpp;
+
+ bpp = (ds->depth + 7) >> 3;
+ d1 = ds->data +
+ ds->linesize * posy + bpp * posx;
+ for (y = 0; y < height; y++) {
+ d = d1;
+ switch(bpp) {
+ case 1:
+ for (x = 0; x < width; x++) {
+ *((uint8_t *)d) = color;
+ d++;
+ }
+ break;
+ case 2:
+ for (x = 0; x < width; x++) {
+ *((uint16_t *)d) = color;
+ d += 2;
+ }
+ break;
+ case 4:
+ for (x = 0; x < width; x++) {
+ *((uint32_t *)d) = color;
+ d += 4;
+ }
+ break;
+ }
+ d1 += ds->linesize;
+ }
+}
+
+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
+static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
+{
+ const uint8_t *s;
+ uint8_t *d;
+ int wb, y, bpp;
+
+ bpp = (ds->depth + 7) >> 3;
+ wb = w * bpp;
+ if (yd <= ys) {
+ s = ds->data +
+ ds->linesize * ys + bpp * xs;
+ d = ds->data +
+ ds->linesize * yd + bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d += ds->linesize;
+ s += ds->linesize;
+ }
+ } else {
+ s = ds->data +
+ ds->linesize * (ys + h - 1) + bpp * xs;
+ d = ds->data +
+ ds->linesize * (yd + h - 1) + bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d -= ds->linesize;
+ s -= ds->linesize;
+ }
+ }
+}
+
+/***********************************************************/
+/* basic char display */
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+#include "vgafont.h"
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef WORDS_BIGENDIAN
+#define PAT(x) x
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+ PAT(0x00000000),
+ PAT(0x0000ffff),
+ PAT(0xffff0000),
+ PAT(0xffffffff),
+};
+
+static uint32_t color_table[2][8];
+
+enum color_names {
+ COLOR_BLACK = 0,
+ COLOR_RED = 1,
+ COLOR_GREEN = 2,
+ COLOR_YELLOW = 3,
+ COLOR_BLUE = 4,
+ COLOR_MAGENTA = 5,
+ COLOR_CYAN = 6,
+ COLOR_WHITE = 7
+};
+
+static const uint32_t color_table_rgb[2][8] = {
+ { /* dark */
+ QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ QEMU_RGB(0xaa, 0x00, 0x00), /* red */
+ QEMU_RGB(0x00, 0xaa, 0x00), /* green */
+ QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
+ QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
+ QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
+ QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
+ QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
+ },
+ { /* bright */
+ QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ QEMU_RGB(0xff, 0x00, 0x00), /* red */
+ QEMU_RGB(0x00, 0xff, 0x00), /* green */
+ QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
+ QEMU_RGB(0x00, 0x00, 0xff), /* blue */
+ QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
+ QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
+ QEMU_RGB(0xff, 0xff, 0xff), /* white */
+ }
+};
+
+static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
+{
+ switch(ds->depth) {
+ case 8:
+ col |= col << 8;
+ col |= col << 16;
+ break;
+ case 15:
+ case 16:
+ col |= col << 16;
+ break;
+ default:
+ break;
+ }
+
+ return col;
+}
+#ifdef DEBUG_CONSOLE
+static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
+{
+ if (t_attrib->bold) {
+ printf("b");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->uline) {
+ printf("u");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->blink) {
+ printf("l");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->invers) {
+ printf("i");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->unvisible) {
+ printf("n");
+ } else {
+ printf(" ");
+ }
+
+ printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
+}
+#endif
+
+static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
+ TextAttributes *t_attrib)
+{
+ uint8_t *d;
+ const uint8_t *font_ptr;
+ unsigned int font_data, linesize, xorcol, bpp;
+ int i;
+ unsigned int fgcol, bgcol;
+
+#ifdef DEBUG_CONSOLE
+ printf("x: %2i y: %2i", x, y);
+ console_print_text_attributes(t_attrib, ch);
+#endif
+
+ if (t_attrib->invers) {
+ bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+ fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+ } else {
+ fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+ bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+ }
+
+ bpp = (ds->depth + 7) >> 3;
+ d = ds->data +
+ ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
+ linesize = ds->linesize;
+ font_ptr = vgafont16 + FONT_HEIGHT * ch;
+ xorcol = bgcol ^ fgcol;
+ switch(ds->depth) {
+ case 8:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline
+ && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 16:
+ case 15:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline
+ && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 32:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ }
+}
+
+static void text_console_resize(TextConsole *s)
+{
+ TextCell *cells, *c, *c1;
+ int w1, x, y, last_width;
+
+ last_width = s->width;
+ s->width = s->g_width / FONT_WIDTH;
+ s->height = s->g_height / FONT_HEIGHT;
+
+ w1 = last_width;
+ if (s->width < w1)
+ w1 = s->width;
+
+ cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
+ for(y = 0; y < s->total_height; y++) {
+ c = &cells[y * s->width];
+ if (w1 > 0) {
+ c1 = &s->cells[y * last_width];
+ for(x = 0; x < w1; x++) {
+ *c++ = *c1++;
+ }
+ }
+ for(x = w1; x < s->width; x++) {
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ c++;
+ }
+ }
+ free(s->cells);
+ s->cells = cells;
+}
+
+static void update_xy(TextConsole *s, int x, int y)
+{
+ TextCell *c;
+ int y1, y2;
+
+ if (s == active_console) {
+ y1 = (s->y_base + y) % s->total_height;
+ y2 = y1 - s->y_displayed;
+ if (y2 < 0)
+ y2 += s->total_height;
+ if (y2 < s->height) {
+ c = &s->cells[y1 * s->width + x];
+ vga_putcharxy(s->ds, x, y2, c->ch,
+ &(c->t_attrib));
+ dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
+ FONT_WIDTH, FONT_HEIGHT);
+ }
+ }
+}
+
+static void console_show_cursor(TextConsole *s, int show)
+{
+ TextCell *c;
+ int y, y1;
+
+ if (s == active_console) {
+ y1 = (s->y_base + s->y) % s->total_height;
+ y = y1 - s->y_displayed;
+ if (y < 0)
+ y += s->total_height;
+ if (y < s->height) {
+ c = &s->cells[y1 * s->width + s->x];
+ if (show) {
+ TextAttributes t_attrib = s->t_attrib_default;
+ t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
+ vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
+ } else {
+ vga_putcharxy(s->ds, s->x, y, c->ch,
+ &(c->t_attrib));
+ }
+ dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
+ FONT_WIDTH, FONT_HEIGHT);
+ }
+ }
+}
+
+static void console_refresh(TextConsole *s)
+{
+ TextCell *c;
+ int x, y, y1;
+
+ if (s != active_console)
+ return;
+
+ vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
+ color_table[0][COLOR_BLACK]);
+ y1 = s->y_displayed;
+ for(y = 0; y < s->height; y++) {
+ c = s->cells + y1 * s->width;
+ for(x = 0; x < s->width; x++) {
+ vga_putcharxy(s->ds, x, y, c->ch,
+ &(c->t_attrib));
+ c++;
+ }
+ if (++y1 == s->total_height)
+ y1 = 0;
+ }
+ dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
+ console_show_cursor(s, 1);
+}
+
+static void console_scroll(int ydelta)
+{
+ TextConsole *s;
+ int i, y1;
+
+ s = active_console;
+ if (!s || !s->text_console)
+ return;
+
+ if (ydelta > 0) {
+ for(i = 0; i < ydelta; i++) {
+ if (s->y_displayed == s->y_base)
+ break;
+ if (++s->y_displayed == s->total_height)
+ s->y_displayed = 0;
+ }
+ } else {
+ ydelta = -ydelta;
+ i = s->backscroll_height;
+ if (i > s->total_height - s->height)
+ i = s->total_height - s->height;
+ y1 = s->y_base - i;
+ if (y1 < 0)
+ y1 += s->total_height;
+ for(i = 0; i < ydelta; i++) {
+ if (s->y_displayed == y1)
+ break;
+ if (--s->y_displayed < 0)
+ s->y_displayed = s->total_height - 1;
+ }
+ }
+ console_refresh(s);
+}
+
+static void console_put_lf(TextConsole *s)
+{
+ TextCell *c;
+ int x, y1;
+
+ s->y++;
+ if (s->y >= s->height) {
+ s->y = s->height - 1;
+
+ if (s->y_displayed == s->y_base) {
+ if (++s->y_displayed == s->total_height)
+ s->y_displayed = 0;
+ }
+ if (++s->y_base == s->total_height)
+ s->y_base = 0;
+ if (s->backscroll_height < s->total_height)
+ s->backscroll_height++;
+ y1 = (s->y_base + s->height - 1) % s->total_height;
+ c = &s->cells[y1 * s->width];
+ for(x = 0; x < s->width; x++) {
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ c++;
+ }
+ if (s == active_console && s->y_displayed == s->y_base) {
+ vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
+ s->width * FONT_WIDTH,
+ (s->height - 1) * FONT_HEIGHT);
+ vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
+ s->width * FONT_WIDTH, FONT_HEIGHT,
+ color_table[0][s->t_attrib_default.bgcol]);
+ dpy_update(s->ds, 0, 0,
+ s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
+ }
+ }
+}
+
+/* Set console attributes depending on the current escape codes.
+ * NOTE: I know this code is not very efficient (checking every color for it
+ * self) but it is more readable and better maintainable.
+ */
+static void console_handle_escape(TextConsole *s)
+{
+ int i;
+
+ if (s->nb_esc_params == 0) { /* ESC[m sets all attributes to default */
+ s->t_attrib = s->t_attrib_default;
+ return;
+ }
+ for (i=0; i<s->nb_esc_params; i++) {
+ switch (s->esc_params[i]) {
+ case 0: /* reset all console attributes to default */
+ s->t_attrib = s->t_attrib_default;
+ break;
+ case 1:
+ s->t_attrib.bold = 1;
+ break;
+ case 4:
+ s->t_attrib.uline = 1;
+ break;
+ case 5:
+ s->t_attrib.blink = 1;
+ break;
+ case 7:
+ s->t_attrib.invers = 1;
+ break;
+ case 8:
+ s->t_attrib.unvisible = 1;
+ break;
+ case 22:
+ s->t_attrib.bold = 0;
+ break;
+ case 24:
+ s->t_attrib.uline = 0;
+ break;
+ case 25:
+ s->t_attrib.blink = 0;
+ break;
+ case 27:
+ s->t_attrib.invers = 0;
+ break;
+ case 28:
+ s->t_attrib.unvisible = 0;
+ break;
+ /* set foreground color */
+ case 30:
+ s->t_attrib.fgcol=COLOR_BLACK;
+ break;
+ case 31:
+ s->t_attrib.fgcol=COLOR_RED;
+ break;
+ case 32:
+ s->t_attrib.fgcol=COLOR_GREEN;
+ break;
+ case 33:
+ s->t_attrib.fgcol=COLOR_YELLOW;
+ break;
+ case 34:
+ s->t_attrib.fgcol=COLOR_BLUE;
+ break;
+ case 35:
+ s->t_attrib.fgcol=COLOR_MAGENTA;
+ break;
+ case 36:
+ s->t_attrib.fgcol=COLOR_CYAN;
+ break;
+ case 37:
+ s->t_attrib.fgcol=COLOR_WHITE;
+ break;
+ /* set background color */
+ case 40:
+ s->t_attrib.bgcol=COLOR_BLACK;
+ break;
+ case 41:
+ s->t_attrib.bgcol=COLOR_RED;
+ break;
+ case 42:
+ s->t_attrib.bgcol=COLOR_GREEN;
+ break;
+ case 43:
+ s->t_attrib.bgcol=COLOR_YELLOW;
+ break;
+ case 44:
+ s->t_attrib.bgcol=COLOR_BLUE;
+ break;
+ case 45:
+ s->t_attrib.bgcol=COLOR_MAGENTA;
+ break;
+ case 46:
+ s->t_attrib.bgcol=COLOR_CYAN;
+ break;
+ case 47:
+ s->t_attrib.bgcol=COLOR_WHITE;
+ break;
+ }
+ }
+}
+
+static void console_putchar(TextConsole *s, int ch)
+{
+ TextCell *c;
+ int y1, i, x;
+
+ switch(s->state) {
+ case TTY_STATE_NORM:
+ switch(ch) {
+ case '\r': /* carriage return */
+ s->x = 0;
+ break;
+ case '\n': /* newline */
+ console_put_lf(s);
+ break;
+ case '\b': /* backspace */
+ if (s->x > 0)
+ s->x--;
+ break;
+ case '\t': /* tabspace */
+ if (s->x + (8 - (s->x % 8)) > s->width) {
+ s->x = 0;
+ console_put_lf(s);
+ } else {
+ s->x = s->x + (8 - (s->x % 8));
+ }
+ break;
+ case '\a': /* alert aka. bell */
+ /* TODO: has to be implemented */
+ break;
+ case 27: /* esc (introducing an escape sequence) */
+ s->state = TTY_STATE_ESC;
+ break;
+ default:
+ y1 = (s->y_base + s->y) % s->total_height;
+ c = &s->cells[y1 * s->width + s->x];
+ c->ch = ch;
+ c->t_attrib = s->t_attrib;
+ update_xy(s, s->x, s->y);
+ s->x++;
+ if (s->x >= s->width) {
+ s->x = 0;
+ console_put_lf(s);
+ }
+ break;
+ }
+ break;
+ case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
+ if (ch == '[') {
+ for(i=0;i<MAX_ESC_PARAMS;i++)
+ s->esc_params[i] = 0;
+ s->nb_esc_params = 0;
+ s->state = TTY_STATE_CSI;
+ } else {
+ s->state = TTY_STATE_NORM;
+ }
+ break;
+ case TTY_STATE_CSI: /* handle escape sequence parameters */
+ if (ch >= '0' && ch <= '9') {
+ if (s->nb_esc_params < MAX_ESC_PARAMS) {
+ s->esc_params[s->nb_esc_params] =
+ s->esc_params[s->nb_esc_params] * 10 + ch - '0';
+ }
+ } else {
+ s->nb_esc_params++;
+ if (ch == ';')
+ break;
+ s->state = TTY_STATE_NORM;
+ switch(ch) {
+ case 'D':
+ if (s->x > 0)
+ s->x--;
+ break;
+ case 'C':
+ if (s->x < (s->width - 1))
+ s->x++;
+ break;
+ case 'K':
+ /* clear to eol */
+ y1 = (s->y_base + s->y) % s->total_height;
+ for(x = s->x; x < s->width; x++) {
+ c = &s->cells[y1 * s->width + x];
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ c++;
+ update_xy(s, x, s->y);
+ }
+ break;
+ default:
+ break;
+ }
+ console_handle_escape(s);
+ break;
+ }
+ }
+}
+
+void console_select(unsigned int index)
+{
+ TextConsole *s;
+
+ if (index >= MAX_CONSOLES)
+ return;
+ s = consoles[index];
+ if (s) {
+ active_console = s;
+ if (s->text_console) {
+ if (s->g_width != s->ds->width ||
+ s->g_height != s->ds->height) {
+ s->g_width = s->ds->width;
+ s->g_height = s->ds->height;
+ text_console_resize(s);
+ }
+ console_refresh(s);
+ } else {
+ vga_hw_invalidate();
+ }
+ }
+}
+
+static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TextConsole *s = chr->opaque;
+ int i;
+
+ console_show_cursor(s, 0);
+ for(i = 0; i < len; i++) {
+ console_putchar(s, buf[i]);
+ }
+ console_show_cursor(s, 1);
+ return len;
+}
+
+static void console_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ TextConsole *s = chr->opaque;
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+}
+
+static void console_send_event(CharDriverState *chr, int event)
+{
+ TextConsole *s = chr->opaque;
+ int i;
+
+ if (event == CHR_EVENT_FOCUS) {
+ for(i = 0; i < nb_consoles; i++) {
+ if (consoles[i] == s) {
+ console_select(i);
+ break;
+ }
+ }
+ }
+}
+
+static void kbd_send_chars(void *opaque)
+{
+ TextConsole *s = opaque;
+ int len;
+ uint8_t buf[16];
+
+ len = s->fd_can_read(s->fd_opaque);
+ if (len > s->out_fifo.count)
+ len = s->out_fifo.count;
+ if (len > 0) {
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+ qemu_fifo_read(&s->out_fifo, buf, len);
+ s->fd_read(s->fd_opaque, buf, len);
+ }
+ /* characters are pending: we send them a bit later (XXX:
+ horrible, should change char device API) */
+ if (s->out_fifo.count > 0) {
+ qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
+ }
+}
+
+/* called when an ascii key is pressed */
+void kbd_put_keysym(int keysym)
+{
+ TextConsole *s;
+ uint8_t buf[16], *q;
+ int c;
+
+ s = active_console;
+ if (!s || !s->text_console)
+ return;
+
+ switch(keysym) {
+ case QEMU_KEY_CTRL_UP:
+ console_scroll(-1);
+ break;
+ case QEMU_KEY_CTRL_DOWN:
+ console_scroll(1);
+ break;
+ case QEMU_KEY_CTRL_PAGEUP:
+ console_scroll(-10);
+ break;
+ case QEMU_KEY_CTRL_PAGEDOWN:
+ console_scroll(10);
+ break;
+ default:
+ /* convert the QEMU keysym to VT100 key string */
+ q = buf;
+ if (keysym >= 0xe100 && keysym <= 0xe11f) {
+ *q++ = '\033';
+ *q++ = '[';
+ c = keysym - 0xe100;
+ if (c >= 10)
+ *q++ = '0' + (c / 10);
+ *q++ = '0' + (c % 10);
+ *q++ = '~';
+ } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
+ *q++ = '\033';
+ *q++ = '[';
+ *q++ = keysym & 0xff;
+ } else {
+ *q++ = keysym;
+ }
+ if (s->fd_read) {
+ qemu_fifo_write(&s->out_fifo, buf, q - buf);
+ kbd_send_chars(s);
+ }
+ break;
+ }
+}
+
+static TextConsole *new_console(DisplayState *ds, int text)
+{
+ TextConsole *s;
+ int i;
+
+ if (nb_consoles >= MAX_CONSOLES)
+ return NULL;
+ s = qemu_mallocz(sizeof(TextConsole));
+ if (!s) {
+ return NULL;
+ }
+ if (!active_console || (active_console->text_console && !text))
+ active_console = s;
+ s->ds = ds;
+ s->text_console = text;
+ if (text) {
+ consoles[nb_consoles++] = s;
+ } else {
+ /* HACK: Put graphical consoles before text consoles. */
+ for (i = nb_consoles; i > 0; i--) {
+ if (!consoles[i - 1]->text_console)
+ break;
+ consoles[i] = consoles[i - 1];
+ }
+ consoles[i] = s;
+ }
+ return s;
+}
+
+TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
+ vga_hw_invalidate_ptr invalidate,
+ vga_hw_screen_dump_ptr screen_dump,
+ void *opaque)
+{
+ TextConsole *s;
+
+ s = new_console(ds, 0);
+ if (!s)
+ return NULL;
+ s->hw_update = update;
+ s->hw_invalidate = invalidate;
+ s->hw_screen_dump = screen_dump;
+ s->hw = opaque;
+ return s;
+}
+
+int is_graphic_console(void)
+{
+ return !active_console->text_console;
+}
+
+CharDriverState *text_console_init(DisplayState *ds)
+{
+ CharDriverState *chr;
+ TextConsole *s;
+ int i,j;
+ static int color_inited;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = new_console(ds, 1);
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ chr->opaque = s;
+ chr->chr_write = console_puts;
+ chr->chr_add_read_handler = console_chr_add_read_handler;
+ chr->chr_send_event = console_send_event;
+
+ s->out_fifo.buf = s->out_fifo_buf;
+ s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
+ s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
+
+ if (!color_inited) {
+ color_inited = 1;
+ for(j = 0; j < 2; j++) {
+ for(i = 0; i < 8; i++) {
+ color_table[j][i] = col_expand(s->ds,
+ vga_get_color(s->ds, color_table_rgb[j][i]));
+ }
+ }
+ }
+ s->y_displayed = 0;
+ s->y_base = 0;
+ s->total_height = DEFAULT_BACKSCROLL;
+ s->x = 0;
+ s->y = 0;
+ s->g_width = s->ds->width;
+ s->g_height = s->ds->height;
+
+ /* Set text attribute defaults */
+ s->t_attrib_default.bold = 0;
+ s->t_attrib_default.uline = 0;
+ s->t_attrib_default.blink = 0;
+ s->t_attrib_default.invers = 0;
+ s->t_attrib_default.unvisible = 0;
+ s->t_attrib_default.fgcol = COLOR_WHITE;
+ s->t_attrib_default.bgcol = COLOR_BLACK;
+
+ /* set current text attributes to default */
+ s->t_attrib = s->t_attrib_default;
+ text_console_resize(s);
+
+ return chr;
+}
diff --git a/cpu-all.h b/cpu-all.h
new file mode 100644
index 0000000..145d84b
--- /dev/null
+++ b/cpu-all.h
@@ -0,0 +1,1015 @@
+/*
+ * defines common to all virtual CPUs
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_ALL_H
+#define CPU_ALL_H
+
+#if defined(__arm__) || defined(__sparc__)
+#define WORDS_ALIGNED
+#endif
+
+/* some important defines:
+ *
+ * WORDS_ALIGNED : if defined, the host cpu can only make word aligned
+ * memory accesses.
+ *
+ * WORDS_BIGENDIAN : if defined, the host cpu is big endian and
+ * otherwise little endian.
+ *
+ * (TARGET_WORDS_ALIGNED : same for target cpu (not supported yet))
+ *
+ * TARGET_WORDS_BIGENDIAN : same for target cpu
+ */
+
+#include "bswap.h"
+
+#if defined(WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+#define BSWAP_NEEDED
+#endif
+
+#ifdef BSWAP_NEEDED
+
+static inline uint16_t tswap16(uint16_t s)
+{
+ return bswap16(s);
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+ return bswap32(s);
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+ return bswap64(s);
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+ *s = bswap16(*s);
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+ *s = bswap32(*s);
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+ *s = bswap64(*s);
+}
+
+#else
+
+static inline uint16_t tswap16(uint16_t s)
+{
+ return s;
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+ return s;
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+ return s;
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+}
+
+#endif
+
+#if TARGET_LONG_SIZE == 4
+#define tswapl(s) tswap32(s)
+#define tswapls(s) tswap32s((uint32_t *)(s))
+#define bswaptls(s) bswap32s(s)
+#else
+#define tswapl(s) tswap64(s)
+#define tswapls(s) tswap64s((uint64_t *)(s))
+#define bswaptls(s) bswap64s(s)
+#endif
+
+/* NOTE: arm FPA is horrible as double 32 bit words are stored in big
+ endian ! */
+typedef union {
+ float64 d;
+#if defined(WORDS_BIGENDIAN) \
+ || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+ struct {
+ uint32_t upper;
+ uint32_t lower;
+ } l;
+#else
+ struct {
+ uint32_t lower;
+ uint32_t upper;
+ } l;
+#endif
+ uint64_t ll;
+} CPU_DoubleU;
+
+/* CPU memory access without any memory or io remapping */
+
+/*
+ * the generic syntax for the memory accesses is:
+ *
+ * load: ld{type}{sign}{size}{endian}_{access_type}(ptr)
+ *
+ * store: st{type}{size}{endian}_{access_type}(ptr, val)
+ *
+ * type is:
+ * (empty): integer access
+ * f : float access
+ *
+ * sign is:
+ * (empty): for floats or 32 bit size
+ * u : unsigned
+ * s : signed
+ *
+ * size is:
+ * b: 8 bits
+ * w: 16 bits
+ * l: 32 bits
+ * q: 64 bits
+ *
+ * endian is:
+ * (empty): target cpu endianness or 8 bit access
+ * r : reversed target cpu endianness (not implemented yet)
+ * be : big endian (not implemented yet)
+ * le : little endian (not implemented yet)
+ *
+ * access_type is:
+ * raw : host memory access
+ * user : user mode access using soft MMU
+ * kernel : kernel mode access using soft MMU
+ */
+static inline int ldub_p(void *ptr)
+{
+ return *(uint8_t *)ptr;
+}
+
+static inline int ldsb_p(void *ptr)
+{
+ return *(int8_t *)ptr;
+}
+
+static inline void stb_p(void *ptr, int v)
+{
+ *(uint8_t *)ptr = v;
+}
+
+/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
+ kernel handles unaligned load/stores may give better results, but
+ it is a system wide setting : bad */
+#if defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+/* conservative code for little endian unaligned accesses */
+static inline int lduw_le_p(void *ptr)
+{
+#ifdef __powerpc__
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ uint8_t *p = ptr;
+ return p[0] | (p[1] << 8);
+#endif
+}
+
+static inline int ldsw_le_p(void *ptr)
+{
+#ifdef __powerpc__
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return (int16_t)val;
+#else
+ uint8_t *p = ptr;
+ return (int16_t)(p[0] | (p[1] << 8));
+#endif
+}
+
+static inline int ldl_le_p(void *ptr)
+{
+#ifdef __powerpc__
+ int val;
+ __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ uint8_t *p = ptr;
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+#endif
+}
+
+static inline uint64_t ldq_le_p(void *ptr)
+{
+ uint8_t *p = ptr;
+ uint32_t v1, v2;
+ v1 = ldl_le_p(p);
+ v2 = ldl_le_p(p + 4);
+ return v1 | ((uint64_t)v2 << 32);
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+#ifdef __powerpc__
+ __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+#endif
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+#ifdef __powerpc__
+ __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+#endif
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ uint8_t *p = ptr;
+ stl_le_p(p, (uint32_t)v);
+ stl_le_p(p + 4, v >> 32);
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_le_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_le_p(ptr, u.i);
+}
+
+static inline float64 ldfq_le_p(void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.lower = ldl_le_p(ptr);
+ u.l.upper = ldl_le_p(ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_le_p(ptr, u.l.lower);
+ stl_le_p(ptr + 4, u.l.upper);
+}
+
+#else
+
+static inline int lduw_le_p(void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_le_p(void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_le_p(void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_le_p(void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_le_p(void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+#endif
+
+#if !defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+static inline int lduw_be_p(void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return val;
+#else
+ uint8_t *b = (uint8_t *) ptr;
+ return ((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldsw_be_p(void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return (int16_t)val;
+#else
+ uint8_t *b = (uint8_t *) ptr;
+ return (int16_t)((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldl_be_p(void *ptr)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ int val;
+ asm volatile ("movl %1, %0\n"
+ "bswap %0\n"
+ : "=r" (val)
+ : "m" (*(uint32_t *)ptr));
+ return val;
+#else
+ uint8_t *b = (uint8_t *) ptr;
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+#endif
+}
+
+static inline uint64_t ldq_be_p(void *ptr)
+{
+ uint32_t a,b;
+ a = ldl_be_p(ptr);
+ b = ldl_be_p(ptr+4);
+ return (((uint64_t)a<<32)|b);
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+#if defined(__i386__)
+ asm volatile ("xchgb %b0, %h0\n"
+ "movw %w0, %1\n"
+ : "=q" (v)
+ : "m" (*(uint16_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 8;
+ d[1] = v;
+#endif
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ asm volatile ("bswap %0\n"
+ "movl %0, %1\n"
+ : "=r" (v)
+ : "m" (*(uint32_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 24;
+ d[1] = v >> 16;
+ d[2] = v >> 8;
+ d[3] = v;
+#endif
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ stl_be_p(ptr, v >> 32);
+ stl_be_p(ptr + 4, v);
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_be_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_be_p(ptr, u.i);
+}
+
+static inline float64 ldfq_be_p(void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.upper = ldl_be_p(ptr);
+ u.l.lower = ldl_be_p(ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_be_p(ptr, u.l.upper);
+ stl_be_p(ptr + 4, u.l.lower);
+}
+
+#else
+
+static inline int lduw_be_p(void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_be_p(void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_be_p(void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_be_p(void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_be_p(void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+
+#endif
+
+/* target CPU memory access functions */
+#if defined(TARGET_WORDS_BIGENDIAN)
+#define lduw_p(p) lduw_be_p(p)
+#define ldsw_p(p) ldsw_be_p(p)
+#define ldl_p(p) ldl_be_p(p)
+#define ldq_p(p) ldq_be_p(p)
+#define ldfl_p(p) ldfl_be_p(p)
+#define ldfq_p(p) ldfq_be_p(p)
+#define stw_p(p, v) stw_be_p(p, v)
+#define stl_p(p, v) stl_be_p(p, v)
+#define stq_p(p, v) stq_be_p(p, v)
+#define stfl_p(p, v) stfl_be_p(p, v)
+#define stfq_p(p, v) stfq_be_p(p, v)
+#else
+#define lduw_p(p) lduw_le_p(p)
+#define ldsw_p(p) ldsw_le_p(p)
+#define ldl_p(p) ldl_le_p(p)
+#define ldq_p(p) ldq_le_p(p)
+#define ldfl_p(p) ldfl_le_p(p)
+#define ldfq_p(p) ldfq_le_p(p)
+#define stw_p(p, v) stw_le_p(p, v)
+#define stl_p(p, v) stl_le_p(p, v)
+#define stq_p(p, v) stq_le_p(p, v)
+#define stfl_p(p, v) stfl_le_p(p, v)
+#define stfq_p(p, v) stfq_le_p(p, v)
+#endif
+
+/* MMU memory access macros */
+
+#if defined(CONFIG_USER_ONLY)
+/* On some host systems the guest address space is reserved on the host.
+ * This allows the guest address space to be offset to a convenient location.
+ */
+//#define GUEST_BASE 0x20000000
+#define GUEST_BASE 0
+
+/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
+#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
+#define h2g(x) ((target_ulong)(x - GUEST_BASE))
+
+#define saddr(x) g2h(x)
+#define laddr(x) g2h(x)
+
+#else /* !CONFIG_USER_ONLY */
+/* NOTE: we use double casts if pointers and target_ulong have
+ different sizes */
+#define saddr(x) (uint8_t *)(long)(x)
+#define laddr(x) (uint8_t *)(long)(x)
+#endif
+
+#define ldub_raw(p) ldub_p(laddr((p)))
+#define ldsb_raw(p) ldsb_p(laddr((p)))
+#define lduw_raw(p) lduw_p(laddr((p)))
+#define ldsw_raw(p) ldsw_p(laddr((p)))
+#define ldl_raw(p) ldl_p(laddr((p)))
+#define ldq_raw(p) ldq_p(laddr((p)))
+#define ldfl_raw(p) ldfl_p(laddr((p)))
+#define ldfq_raw(p) ldfq_p(laddr((p)))
+#define stb_raw(p, v) stb_p(saddr((p)), v)
+#define stw_raw(p, v) stw_p(saddr((p)), v)
+#define stl_raw(p, v) stl_p(saddr((p)), v)
+#define stq_raw(p, v) stq_p(saddr((p)), v)
+#define stfl_raw(p, v) stfl_p(saddr((p)), v)
+#define stfq_raw(p, v) stfq_p(saddr((p)), v)
+
+
+#if defined(CONFIG_USER_ONLY)
+
+/* if user mode, no other memory access functions */
+#define ldub(p) ldub_raw(p)
+#define ldsb(p) ldsb_raw(p)
+#define lduw(p) lduw_raw(p)
+#define ldsw(p) ldsw_raw(p)
+#define ldl(p) ldl_raw(p)
+#define ldq(p) ldq_raw(p)
+#define ldfl(p) ldfl_raw(p)
+#define ldfq(p) ldfq_raw(p)
+#define stb(p, v) stb_raw(p, v)
+#define stw(p, v) stw_raw(p, v)
+#define stl(p, v) stl_raw(p, v)
+#define stq(p, v) stq_raw(p, v)
+#define stfl(p, v) stfl_raw(p, v)
+#define stfq(p, v) stfq_raw(p, v)
+
+#define ldub_code(p) ldub_raw(p)
+#define ldsb_code(p) ldsb_raw(p)
+#define lduw_code(p) lduw_raw(p)
+#define ldsw_code(p) ldsw_raw(p)
+#define ldl_code(p) ldl_raw(p)
+
+#define ldub_kernel(p) ldub_raw(p)
+#define ldsb_kernel(p) ldsb_raw(p)
+#define lduw_kernel(p) lduw_raw(p)
+#define ldsw_kernel(p) ldsw_raw(p)
+#define ldl_kernel(p) ldl_raw(p)
+#define ldfl_kernel(p) ldfl_raw(p)
+#define ldfq_kernel(p) ldfq_raw(p)
+#define stb_kernel(p, v) stb_raw(p, v)
+#define stw_kernel(p, v) stw_raw(p, v)
+#define stl_kernel(p, v) stl_raw(p, v)
+#define stq_kernel(p, v) stq_raw(p, v)
+#define stfl_kernel(p, v) stfl_raw(p, v)
+#define stfq_kernel(p, vt) stfq_raw(p, v)
+
+#endif /* defined(CONFIG_USER_ONLY) */
+
+/* page related stuff */
+
+#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
+#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
+#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
+
+/* ??? These should be the larger of unsigned long and target_ulong. */
+extern unsigned long qemu_real_host_page_size;
+extern unsigned long qemu_host_page_bits;
+extern unsigned long qemu_host_page_size;
+extern unsigned long qemu_host_page_mask;
+
+#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
+
+/* same as PROT_xxx */
+#define PAGE_READ 0x0001
+#define PAGE_WRITE 0x0002
+#define PAGE_EXEC 0x0004
+#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
+#define PAGE_VALID 0x0008
+/* original state of the write flag (used when tracking self-modifying
+ code */
+#define PAGE_WRITE_ORG 0x0010
+
+void page_dump(FILE *f);
+int page_get_flags(target_ulong address);
+void page_set_flags(target_ulong start, target_ulong end, int flags);
+void page_unprotect_range(target_ulong data, target_ulong data_size);
+
+#define SINGLE_CPU_DEFINES
+#ifdef SINGLE_CPU_DEFINES
+
+#if defined(TARGET_I386)
+
+#define CPUState CPUX86State
+#define cpu_init cpu_x86_init
+#define cpu_exec cpu_x86_exec
+#define cpu_gen_code cpu_x86_gen_code
+#define cpu_signal_handler cpu_x86_signal_handler
+
+#elif defined(TARGET_ARM)
+
+#define CPUState CPUARMState
+#define cpu_init cpu_arm_init
+#define cpu_exec cpu_arm_exec
+#define cpu_gen_code cpu_arm_gen_code
+#define cpu_signal_handler cpu_arm_signal_handler
+
+#elif defined(TARGET_SPARC)
+
+#define CPUState CPUSPARCState
+#define cpu_init cpu_sparc_init
+#define cpu_exec cpu_sparc_exec
+#define cpu_gen_code cpu_sparc_gen_code
+#define cpu_signal_handler cpu_sparc_signal_handler
+
+#elif defined(TARGET_PPC)
+
+#define CPUState CPUPPCState
+#define cpu_init cpu_ppc_init
+#define cpu_exec cpu_ppc_exec
+#define cpu_gen_code cpu_ppc_gen_code
+#define cpu_signal_handler cpu_ppc_signal_handler
+
+#elif defined(TARGET_MIPS)
+#define CPUState CPUMIPSState
+#define cpu_init cpu_mips_init
+#define cpu_exec cpu_mips_exec
+#define cpu_gen_code cpu_mips_gen_code
+#define cpu_signal_handler cpu_mips_signal_handler
+
+#elif defined(TARGET_SH4)
+#define CPUState CPUSH4State
+#define cpu_init cpu_sh4_init
+#define cpu_exec cpu_sh4_exec
+#define cpu_gen_code cpu_sh4_gen_code
+#define cpu_signal_handler cpu_sh4_signal_handler
+
+#else
+
+#error unsupported target CPU
+
+#endif
+
+#endif /* SINGLE_CPU_DEFINES */
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags);
+
+void cpu_abort(CPUState *env, const char *fmt, ...);
+extern CPUState *first_cpu;
+extern CPUState *cpu_single_env;
+extern int code_copy_enabled;
+
+#define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */
+#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */
+#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */
+#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */
+#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
+#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
+
+void cpu_interrupt(CPUState *s, int mask);
+void cpu_reset_interrupt(CPUState *env, int mask);
+
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
+void cpu_single_step(CPUState *env, int enabled);
+void cpu_reset(CPUState *s);
+
+/* Return the physical page corresponding to a virtual one. Use it
+ only for debugging because no protection checks are done. Return -1
+ if no page found. */
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
+
+#define CPU_LOG_TB_OUT_ASM (1 << 0)
+#define CPU_LOG_TB_IN_ASM (1 << 1)
+#define CPU_LOG_TB_OP (1 << 2)
+#define CPU_LOG_TB_OP_OPT (1 << 3)
+#define CPU_LOG_INT (1 << 4)
+#define CPU_LOG_EXEC (1 << 5)
+#define CPU_LOG_PCALL (1 << 6)
+#define CPU_LOG_IOPORT (1 << 7)
+#define CPU_LOG_TB_CPU (1 << 8)
+
+/* define log items */
+typedef struct CPULogItem {
+ int mask;
+ const char *name;
+ const char *help;
+} CPULogItem;
+
+extern CPULogItem cpu_log_items[];
+
+void cpu_set_log(int log_flags);
+void cpu_set_log_filename(const char *filename);
+int cpu_str_to_log_mask(const char *str);
+
+/* IO ports API */
+
+/* NOTE: as these functions may be even used when there is an isa
+ brige on non x86 targets, we always defined them */
+#ifndef NO_CPU_IO_DEFS
+void cpu_outb(CPUState *env, int addr, int val);
+void cpu_outw(CPUState *env, int addr, int val);
+void cpu_outl(CPUState *env, int addr, int val);
+int cpu_inb(CPUState *env, int addr);
+int cpu_inw(CPUState *env, int addr);
+int cpu_inl(CPUState *env, int addr);
+#endif
+
+/* memory API */
+
+extern int phys_ram_size;
+extern int phys_ram_fd;
+extern uint8_t *phys_ram_base;
+extern uint8_t *phys_ram_dirty;
+
+/* physical memory access */
+#define TLB_INVALID_MASK (1 << 3)
+#define IO_MEM_SHIFT 4
+#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
+
+#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
+#define IO_MEM_NOTDIRTY (4 << IO_MEM_SHIFT) /* used internally, never use directly */
+/* acts like a ROM when read and like a device when written. As an
+ exception, the write memory callback gets the ram offset instead of
+ the physical address */
+#define IO_MEM_ROMD (1)
+
+typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
+typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
+
+void cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset);
+int cpu_register_io_memory(int io_index,
+ CPUReadMemoryFunc **mem_read,
+ CPUWriteMemoryFunc **mem_write,
+ void *opaque);
+CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index);
+CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index);
+
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+ int len, int is_write);
+static inline void cpu_physical_memory_read(target_phys_addr_t addr,
+ uint8_t *buf, int len)
+{
+ cpu_physical_memory_rw(addr, buf, len, 0);
+}
+static inline void cpu_physical_memory_write(target_phys_addr_t addr,
+ const uint8_t *buf, int len)
+{
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
+}
+uint32_t ldub_phys(target_phys_addr_t addr);
+uint32_t lduw_phys(target_phys_addr_t addr);
+uint32_t ldl_phys(target_phys_addr_t addr);
+uint64_t ldq_phys(target_phys_addr_t addr);
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
+void stb_phys(target_phys_addr_t addr, uint32_t val);
+void stw_phys(target_phys_addr_t addr, uint32_t val);
+void stl_phys(target_phys_addr_t addr, uint32_t val);
+void stq_phys(target_phys_addr_t addr, uint64_t val);
+
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+ const uint8_t *buf, int len);
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+ uint8_t *buf, int len, int is_write);
+
+#define VGA_DIRTY_FLAG 0x01
+#define CODE_DIRTY_FLAG 0x02
+
+/* read dirty bit (return 0 or 1) */
+static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
+{
+ return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
+}
+
+static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
+ int dirty_flags)
+{
+ return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
+}
+
+static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
+{
+ phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+ int dirty_flags);
+void cpu_tlb_update_dirty(CPUState *env);
+
+void dump_exec_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+/*******************************************/
+/* host CPU ticks (if available) */
+
+#if defined(__powerpc__)
+
+static inline uint32_t get_tbl(void)
+{
+ uint32_t tbl;
+ asm volatile("mftb %0" : "=r" (tbl));
+ return tbl;
+}
+
+static inline uint32_t get_tbu(void)
+{
+ uint32_t tbl;
+ asm volatile("mftbu %0" : "=r" (tbl));
+ return tbl;
+}
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ uint32_t l, h, h1;
+ /* NOTE: we test if wrapping has occurred */
+ do {
+ h = get_tbu();
+ l = get_tbl();
+ h1 = get_tbu();
+ } while (h != h1);
+ return ((int64_t)h << 32) | l;
+}
+
+#elif defined(__i386__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile ("rdtsc" : "=A" (val));
+ return val;
+}
+
+#elif defined(__x86_64__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ uint32_t low,high;
+ int64_t val;
+ asm volatile("rdtsc" : "=a" (low), "=d" (high));
+ val = high;
+ val <<= 32;
+ val |= low;
+ return val;
+}
+
+#elif defined(__ia64)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory");
+ return val;
+}
+
+#elif defined(__s390__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc");
+ return val;
+}
+
+#elif defined(__sparc_v9__)
+
+static inline int64_t cpu_get_real_ticks (void)
+{
+#if defined(_LP64)
+ uint64_t rval;
+ asm volatile("rd %%tick,%0" : "=r"(rval));
+ return rval;
+#else
+ union {
+ uint64_t i64;
+ struct {
+ uint32_t high;
+ uint32_t low;
+ } i32;
+ } rval;
+ asm volatile("rd %%tick,%1; srlx %1,32,%0"
+ : "=r"(rval.i32.high), "=r"(rval.i32.low));
+ return rval.i64;
+#endif
+}
+#endif
+
+/* profiling */
+#ifdef CONFIG_PROFILER
+static inline int64_t profile_getclock(void)
+{
+ return cpu_get_real_ticks();
+}
+
+extern int64_t kqemu_time, kqemu_time_start;
+extern int64_t qemu_time, qemu_time_start;
+extern int64_t tlb_flush_time;
+extern int64_t kqemu_exec_count;
+extern int64_t dev_time;
+extern int64_t kqemu_ret_int_count;
+extern int64_t kqemu_ret_excp_count;
+extern int64_t kqemu_ret_intr_count;
+
+#endif
+
+#endif /* CPU_ALL_H */
diff --git a/cpu-defs.h b/cpu-defs.h
new file mode 100644
index 0000000..674c0bd
--- /dev/null
+++ b/cpu-defs.h
@@ -0,0 +1,125 @@
+/*
+ * common defines for all CPUs
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_DEFS_H
+#define CPU_DEFS_H
+
+#include "config.h"
+#include <setjmp.h>
+#include <inttypes.h>
+#include "osdep.h"
+
+#ifndef TARGET_LONG_BITS
+#error TARGET_LONG_BITS must be defined before including this header
+#endif
+
+#ifndef TARGET_PHYS_ADDR_BITS
+#if TARGET_LONG_BITS >= HOST_LONG_BITS
+#define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS
+#else
+#define TARGET_PHYS_ADDR_BITS HOST_LONG_BITS
+#endif
+#endif
+
+#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
+
+/* target_ulong is the type of a virtual address */
+#if TARGET_LONG_SIZE == 4
+typedef int32_t target_long;
+typedef uint32_t target_ulong;
+#define TARGET_FMT_lx "%08x"
+#elif TARGET_LONG_SIZE == 8
+typedef int64_t target_long;
+typedef uint64_t target_ulong;
+#define TARGET_FMT_lx "%016" PRIx64
+#else
+#error TARGET_LONG_SIZE undefined
+#endif
+
+/* target_phys_addr_t is the type of a physical address (its size can
+ be different from 'target_ulong'). We have sizeof(target_phys_addr)
+ = max(sizeof(unsigned long),
+ sizeof(size_of_target_physical_address)) because we must pass a
+ host pointer to memory operations in some cases */
+
+#if TARGET_PHYS_ADDR_BITS == 32
+typedef uint32_t target_phys_addr_t;
+#elif TARGET_PHYS_ADDR_BITS == 64
+typedef uint64_t target_phys_addr_t;
+#else
+#error TARGET_PHYS_ADDR_BITS undefined
+#endif
+
+/* address in the RAM (different from a physical address) */
+typedef unsigned long ram_addr_t;
+
+#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
+
+#define EXCP_INTERRUPT 0x10000 /* async interruption */
+#define EXCP_HLT 0x10001 /* hlt instruction reached */
+#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
+#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
+#define MAX_BREAKPOINTS 32
+
+#define TB_JMP_CACHE_BITS 12
+#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
+
+#define CPU_TLB_BITS 8
+#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
+
+typedef struct CPUTLBEntry {
+ /* bit 31 to TARGET_PAGE_BITS : virtual address
+ bit TARGET_PAGE_BITS-1..IO_MEM_SHIFT : if non zero, memory io
+ zone number
+ bit 3 : indicates that the entry is invalid
+ bit 2..0 : zero
+ */
+ target_ulong addr_read;
+ target_ulong addr_write;
+ target_ulong addr_code;
+ /* addend to virtual address to get physical address */
+ target_phys_addr_t addend;
+} CPUTLBEntry;
+
+#define CPU_COMMON \
+ struct TranslationBlock *current_tb; /* currently executing TB */ \
+ /* soft mmu support */ \
+ /* in order to avoid passing too many arguments to the memory \
+ write helpers, we store some rarely used information in the CPU \
+ context) */ \
+ unsigned long mem_write_pc; /* host pc at which the memory was \
+ written */ \
+ target_ulong mem_write_vaddr; /* target virtual addr at which the \
+ memory was written */ \
+ /* 0 = kernel, 1 = user */ \
+ CPUTLBEntry tlb_table[2][CPU_TLB_SIZE]; \
+ struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \
+ \
+ /* from this point: preserved by CPU reset */ \
+ /* ice debug support */ \
+ target_ulong breakpoints[MAX_BREAKPOINTS]; \
+ int nb_breakpoints; \
+ int singlestep_enabled; \
+ \
+ void *next_cpu; /* next CPU sharing TB cache */ \
+ int cpu_index; /* CPU index (informative) */ \
+ /* user data */ \
+ void *opaque;
+
+#endif
diff --git a/cpu-exec.c b/cpu-exec.c
new file mode 100644
index 0000000..60239d4
--- /dev/null
+++ b/cpu-exec.c
@@ -0,0 +1,1483 @@
+/*
+ * i386 emulator main execution loop
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#include "exec.h"
+#include "disas.h"
+
+#if !defined(CONFIG_SOFTMMU)
+#undef EAX
+#undef ECX
+#undef EDX
+#undef EBX
+#undef ESP
+#undef EBP
+#undef ESI
+#undef EDI
+#undef EIP
+#include <signal.h>
+#include <sys/ucontext.h>
+#endif
+
+int tb_invalidated_flag;
+
+//#define DEBUG_EXEC
+//#define DEBUG_SIGNAL
+
+#if defined(TARGET_ARM) || defined(TARGET_SPARC)
+/* XXX: unify with i386 target */
+void cpu_loop_exit(void)
+{
+ longjmp(env->jmp_env, 1);
+}
+#endif
+#if !(defined(TARGET_SPARC) || defined(TARGET_SH4))
+#define reg_T2
+#endif
+
+/* exit the current TB from a signal handler. The host registers are
+ restored in a state compatible with the CPU emulator
+ */
+void cpu_resume_from_signal(CPUState *env1, void *puc)
+{
+#if !defined(CONFIG_SOFTMMU)
+ struct ucontext *uc = puc;
+#endif
+
+ env = env1;
+
+ /* XXX: restore cpu registers saved in host registers */
+
+#if !defined(CONFIG_SOFTMMU)
+ if (puc) {
+ /* XXX: use siglongjmp ? */
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ }
+#endif
+ longjmp(env->jmp_env, 1);
+}
+
+
+static TranslationBlock *tb_find_slow(target_ulong pc,
+ target_ulong cs_base,
+ unsigned int flags)
+{
+ TranslationBlock *tb, **ptb1;
+ int code_gen_size;
+ unsigned int h;
+ target_ulong phys_pc, phys_page1, phys_page2, virt_page2;
+ uint8_t *tc_ptr;
+
+ spin_lock(&tb_lock);
+
+ tb_invalidated_flag = 0;
+
+ regs_to_env(); /* XXX: do it just before cpu_gen_code() */
+
+ /* find translated block using physical mappings */
+ phys_pc = get_phys_addr_code(env, pc);
+ phys_page1 = phys_pc & TARGET_PAGE_MASK;
+ phys_page2 = -1;
+ h = tb_phys_hash_func(phys_pc);
+ ptb1 = &tb_phys_hash[h];
+ for(;;) {
+ tb = *ptb1;
+ if (!tb)
+ goto not_found;
+ if (tb->pc == pc &&
+ tb->page_addr[0] == phys_page1 &&
+ tb->cs_base == cs_base &&
+ tb->flags == flags) {
+ /* check next page if needed */
+ if (tb->page_addr[1] != -1) {
+ virt_page2 = (pc & TARGET_PAGE_MASK) +
+ TARGET_PAGE_SIZE;
+ phys_page2 = get_phys_addr_code(env, virt_page2);
+ if (tb->page_addr[1] == phys_page2)
+ goto found;
+ } else {
+ goto found;
+ }
+ }
+ ptb1 = &tb->phys_hash_next;
+ }
+ not_found:
+ /* if no translated code available, then translate it now */
+ tb = tb_alloc(pc);
+ if (!tb) {
+ /* flush must be done */
+ tb_flush(env);
+ /* cannot fail at this point */
+ tb = tb_alloc(pc);
+ /* don't forget to invalidate previous TB info */
+ tb_invalidated_flag = 1;
+ }
+ tc_ptr = code_gen_ptr;
+ tb->tc_ptr = tc_ptr;
+ tb->cs_base = cs_base;
+ tb->flags = flags;
+ cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
+ code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+
+ /* check next page if needed */
+ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
+ phys_page2 = -1;
+ if ((pc & TARGET_PAGE_MASK) != virt_page2) {
+ phys_page2 = get_phys_addr_code(env, virt_page2);
+ }
+ tb_link_phys(tb, phys_pc, phys_page2);
+
+ found:
+ /* we add the TB in the virtual pc hash table */
+ env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
+ spin_unlock(&tb_lock);
+ return tb;
+}
+
+static inline TranslationBlock *tb_find_fast(void)
+{
+ TranslationBlock *tb;
+ target_ulong cs_base, pc;
+ unsigned int flags;
+
+ /* we record a subset of the CPU state. It will
+ always be the same before a given translated block
+ is executed. */
+#if defined(TARGET_I386)
+ flags = env->hflags;
+ flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+ cs_base = env->segs[R_CS].base;
+ pc = cs_base + env->eip;
+#elif defined(TARGET_ARM)
+ flags = env->thumb | (env->vfp.vec_len << 1)
+ | (env->vfp.vec_stride << 4);
+ if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR)
+ flags |= (1 << 6);
+ if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30))
+ flags |= (1 << 7);
+ cs_base = 0;
+ pc = env->regs[15];
+#elif defined(TARGET_SPARC)
+#ifdef TARGET_SPARC64
+ // Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled
+ flags = (((env->pstate & PS_PEF) >> 1) | ((env->fprs & FPRS_FEF) << 2))
+ | (env->pstate & PS_PRIV) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2);
+#else
+ // FPU enable . MMU enabled . MMU no-fault . Supervisor
+ flags = (env->psref << 3) | ((env->mmuregs[0] & (MMU_E | MMU_NF)) << 1)
+ | env->psrs;
+#endif
+ cs_base = env->npc;
+ pc = env->pc;
+#elif defined(TARGET_PPC)
+ flags = (msr_pr << MSR_PR) | (msr_fp << MSR_FP) |
+ (msr_se << MSR_SE) | (msr_le << MSR_LE);
+ cs_base = 0;
+ pc = env->nip;
+#elif defined(TARGET_MIPS)
+ flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK);
+ cs_base = 0;
+ pc = env->PC;
+#elif defined(TARGET_SH4)
+ flags = env->sr & (SR_MD | SR_RB);
+ cs_base = 0; /* XXXXX */
+ pc = env->pc;
+#else
+#error unsupported CPU
+#endif
+ tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
+ if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
+ tb->flags != flags, 0)) {
+ tb = tb_find_slow(pc, cs_base, flags);
+ /* Note: we do it here to avoid a gcc bug on Mac OS X when
+ doing it in tb_find_slow */
+ if (tb_invalidated_flag) {
+ /* as some TB could have been invalidated because
+ of memory exceptions while generating the code, we
+ must recompute the hash index here */
+ T0 = 0;
+ }
+ }
+ return tb;
+}
+
+
+/* main execution loop */
+
+int cpu_exec(CPUState *env1)
+{
+ int saved_T0, saved_T1;
+#if defined(reg_T2)
+ int saved_T2;
+#endif
+ CPUState *saved_env;
+#if defined(TARGET_I386)
+#ifdef reg_EAX
+ int saved_EAX;
+#endif
+#ifdef reg_ECX
+ int saved_ECX;
+#endif
+#ifdef reg_EDX
+ int saved_EDX;
+#endif
+#ifdef reg_EBX
+ int saved_EBX;
+#endif
+#ifdef reg_ESP
+ int saved_ESP;
+#endif
+#ifdef reg_EBP
+ int saved_EBP;
+#endif
+#ifdef reg_ESI
+ int saved_ESI;
+#endif
+#ifdef reg_EDI
+ int saved_EDI;
+#endif
+#elif defined(TARGET_SPARC)
+#if defined(reg_REGWPTR)
+ uint32_t *saved_regwptr;
+#endif
+#endif
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ int saved_i7, tmp_T0;
+#endif
+ int ret, interrupt_request;
+ void (*gen_func)(void);
+ TranslationBlock *tb;
+ uint8_t *tc_ptr;
+
+#if defined(TARGET_I386)
+ /* handle exit of HALTED state */
+ if (env1->hflags & HF_HALTED_MASK) {
+ /* disable halt condition */
+ if ((env1->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env1->eflags & IF_MASK)) {
+ env1->hflags &= ~HF_HALTED_MASK;
+ } else {
+ return EXCP_HALTED;
+ }
+ }
+#elif defined(TARGET_PPC)
+ if (env1->halted) {
+ if (env1->msr[MSR_EE] &&
+ (env1->interrupt_request &
+ (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER))) {
+ env1->halted = 0;
+ } else {
+ return EXCP_HALTED;
+ }
+ }
+#elif defined(TARGET_SPARC)
+ if (env1->halted) {
+ if ((env1->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env1->psret != 0)) {
+ env1->halted = 0;
+ } else {
+ return EXCP_HALTED;
+ }
+ }
+#elif defined(TARGET_ARM)
+ if (env1->halted) {
+ /* An interrupt wakes the CPU even if the I and F CPSR bits are
+ set. */
+ if (env1->interrupt_request
+ & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD)) {
+ env1->halted = 0;
+ } else {
+ return EXCP_HALTED;
+ }
+ }
+#elif defined(TARGET_MIPS)
+ if (env1->halted) {
+ if (env1->interrupt_request &
+ (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER)) {
+ env1->halted = 0;
+ } else {
+ return EXCP_HALTED;
+ }
+ }
+#endif
+
+ cpu_single_env = env1;
+
+ /* first we save global registers */
+ saved_env = env;
+ env = env1;
+ saved_T0 = T0;
+ saved_T1 = T1;
+#if defined(reg_T2)
+ saved_T2 = T2;
+#endif
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ /* we also save i7 because longjmp may not restore it */
+ asm volatile ("mov %%i7, %0" : "=r" (saved_i7));
+#endif
+
+#if defined(TARGET_I386)
+#ifdef reg_EAX
+ saved_EAX = EAX;
+#endif
+#ifdef reg_ECX
+ saved_ECX = ECX;
+#endif
+#ifdef reg_EDX
+ saved_EDX = EDX;
+#endif
+#ifdef reg_EBX
+ saved_EBX = EBX;
+#endif
+#ifdef reg_ESP
+ saved_ESP = ESP;
+#endif
+#ifdef reg_EBP
+ saved_EBP = EBP;
+#endif
+#ifdef reg_ESI
+ saved_ESI = ESI;
+#endif
+#ifdef reg_EDI
+ saved_EDI = EDI;
+#endif
+
+ env_to_regs();
+ /* put eflags in CPU temporary format */
+ CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((env->eflags >> 10) & 1));
+ CC_OP = CC_OP_EFLAGS;
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+#elif defined(TARGET_ARM)
+#elif defined(TARGET_SPARC)
+#if defined(reg_REGWPTR)
+ saved_regwptr = REGWPTR;
+#endif
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+ /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+ env->exception_index = -1;
+
+ /* prepare setjmp context for exception handling */
+ for(;;) {
+ if (setjmp(env->jmp_env) == 0) {
+ env->current_tb = NULL;
+ /* if an exception is pending, we execute it here */
+ if (env->exception_index >= 0) {
+ if (env->exception_index >= EXCP_INTERRUPT) {
+ /* exit request from the cpu execution loop */
+ ret = env->exception_index;
+ break;
+ } else if (env->user_mode_only) {
+ /* if user mode only, we simulate a fake exception
+ which will be hanlded outside the cpu execution
+ loop */
+#if defined(TARGET_I386)
+ do_interrupt_user(env->exception_index,
+ env->exception_is_int,
+ env->error_code,
+ env->exception_next_eip);
+#endif
+ ret = env->exception_index;
+ break;
+ } else {
+#if defined(TARGET_I386)
+ /* simulate a real cpu exception. On i386, it can
+ trigger new exceptions, but we do not handle
+ double or triple faults yet. */
+ do_interrupt(env->exception_index,
+ env->exception_is_int,
+ env->error_code,
+ env->exception_next_eip, 0);
+#elif defined(TARGET_PPC)
+ do_interrupt(env);
+#elif defined(TARGET_MIPS)
+ do_interrupt(env);
+#elif defined(TARGET_SPARC)
+ do_interrupt(env->exception_index);
+#elif defined(TARGET_ARM)
+ do_interrupt(env);
+#elif defined(TARGET_SH4)
+ do_interrupt(env);
+#endif
+ }
+ env->exception_index = -1;
+ }
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env) && env->interrupt_request == 0) {
+ int ret;
+ env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+ ret = kqemu_cpu_exec(env);
+ /* put eflags in CPU temporary format */
+ CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((env->eflags >> 10) & 1));
+ CC_OP = CC_OP_EFLAGS;
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ if (ret == 1) {
+ /* exception */
+ longjmp(env->jmp_env, 1);
+ } else if (ret == 2) {
+ /* softmmu execution needed */
+ } else {
+ if (env->interrupt_request != 0) {
+ /* hardware interrupt will be executed just after */
+ } else {
+ /* otherwise, we restart */
+ longjmp(env->jmp_env, 1);
+ }
+ }
+ }
+#endif
+
+ T0 = 0; /* force lookup of first TB */
+ for(;;) {
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ /* g1 can be modified by some libc? functions */
+ tmp_T0 = T0;
+#endif
+ interrupt_request = env->interrupt_request;
+ if (__builtin_expect(interrupt_request, 0)) {
+#if defined(TARGET_I386)
+ /* if hardware interrupt pending, we execute it */
+ if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK) &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+ int intno;
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ intno = cpu_get_pic_interrupt(env);
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "Servicing hardware INT=0x%02x\n", intno);
+ }
+ do_interrupt(intno, 0, 0, 0, 1);
+ /* ensure that no TB jump will be modified as
+ the program flow was changed */
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ }
+#elif defined(TARGET_PPC)
+#if 0
+ if ((interrupt_request & CPU_INTERRUPT_RESET)) {
+ cpu_ppc_reset(env);
+ }
+#endif
+ if (msr_ee != 0) {
+ if ((interrupt_request & CPU_INTERRUPT_HARD)) {
+ /* Raise it */
+ env->exception_index = EXCP_EXTERNAL;
+ env->error_code = 0;
+ do_interrupt(env);
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ } else if ((interrupt_request & CPU_INTERRUPT_TIMER)) {
+ /* Raise it */
+ env->exception_index = EXCP_DECR;
+ env->error_code = 0;
+ do_interrupt(env);
+ env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ }
+ }
+#elif defined(TARGET_MIPS)
+ if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->CP0_Status & (1 << CP0St_IE)) &&
+ (env->CP0_Status & env->CP0_Cause & 0x0000FF00) &&
+ !(env->hflags & MIPS_HFLAG_EXL) &&
+ !(env->hflags & MIPS_HFLAG_ERL) &&
+ !(env->hflags & MIPS_HFLAG_DM)) {
+ /* Raise it */
+ env->exception_index = EXCP_EXT_INTERRUPT;
+ env->error_code = 0;
+ do_interrupt(env);
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ }
+#elif defined(TARGET_SPARC)
+ if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->psret != 0)) {
+ int pil = env->interrupt_index & 15;
+ int type = env->interrupt_index & 0xf0;
+
+ if (((type == TT_EXTINT) &&
+ (pil == 15 || pil > env->psrpil)) ||
+ type != TT_EXTINT) {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ do_interrupt(env->interrupt_index);
+ env->interrupt_index = 0;
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ }
+ } else if (interrupt_request & CPU_INTERRUPT_TIMER) {
+ //do_interrupt(0, 0, 0, 0, 0);
+ env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
+ } else if (interrupt_request & CPU_INTERRUPT_HALT) {
+ env1->halted = 1;
+ return EXCP_HALTED;
+ }
+#elif defined(TARGET_ARM)
+ if (interrupt_request & CPU_INTERRUPT_FIQ
+ && !(env->uncached_cpsr & CPSR_F)) {
+ env->exception_index = EXCP_FIQ;
+ do_interrupt(env);
+ }
+ if (interrupt_request & CPU_INTERRUPT_HARD
+ && !(env->uncached_cpsr & CPSR_I)) {
+ env->exception_index = EXCP_IRQ;
+ do_interrupt(env);
+ }
+#elif defined(TARGET_SH4)
+ /* XXXXX */
+#endif
+ /* Don't use the cached interupt_request value,
+ do_interrupt may have updated the EXITTB flag. */
+ if (env->interrupt_request & CPU_INTERRUPT_EXITTB) {
+ env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
+ /* ensure that no TB jump will be modified as
+ the program flow was changed */
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ tmp_T0 = 0;
+#else
+ T0 = 0;
+#endif
+ }
+ if (interrupt_request & CPU_INTERRUPT_EXIT) {
+ env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
+ env->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit();
+ }
+ }
+#ifdef DEBUG_EXEC
+ if ((loglevel & CPU_LOG_TB_CPU)) {
+#if defined(TARGET_I386)
+ /* restore flags in standard format */
+#ifdef reg_EAX
+ env->regs[R_EAX] = EAX;
+#endif
+#ifdef reg_EBX
+ env->regs[R_EBX] = EBX;
+#endif
+#ifdef reg_ECX
+ env->regs[R_ECX] = ECX;
+#endif
+#ifdef reg_EDX
+ env->regs[R_EDX] = EDX;
+#endif
+#ifdef reg_ESI
+ env->regs[R_ESI] = ESI;
+#endif
+#ifdef reg_EDI
+ env->regs[R_EDI] = EDI;
+#endif
+#ifdef reg_EBP
+ env->regs[R_EBP] = EBP;
+#endif
+#ifdef reg_ESP
+ env->regs[R_ESP] = ESP;
+#endif
+ env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+#elif defined(TARGET_ARM)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_SPARC)
+ REGWPTR = env->regbase + (env->cwp * 16);
+ env->regwptr = REGWPTR;
+ cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_PPC)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_MIPS)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_SH4)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#else
+#error unsupported target CPU
+#endif
+ }
+#endif
+ tb = tb_find_fast();
+#ifdef DEBUG_EXEC
+ if ((loglevel & CPU_LOG_EXEC)) {
+ fprintf(logfile, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n",
+ (long)tb->tc_ptr, tb->pc,
+ lookup_symbol(tb->pc));
+ }
+#endif
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ T0 = tmp_T0;
+#endif
+ /* see if we can patch the calling TB. When the TB
+ spans two pages, we cannot safely do a direct
+ jump. */
+ {
+ if (T0 != 0 &&
+#if USE_KQEMU
+ (env->kqemu_enabled != 2) &&
+#endif
+ tb->page_addr[1] == -1
+#if defined(TARGET_I386) && defined(USE_CODE_COPY)
+ && (tb->cflags & CF_CODE_COPY) ==
+ (((TranslationBlock *)(T0 & ~3))->cflags & CF_CODE_COPY)
+#endif
+ ) {
+ spin_lock(&tb_lock);
+ tb_add_jump((TranslationBlock *)(long)(T0 & ~3), T0 & 3, tb);
+#if defined(USE_CODE_COPY)
+ /* propagates the FP use info */
+ ((TranslationBlock *)(T0 & ~3))->cflags |=
+ (tb->cflags & CF_FP_USED);
+#endif
+ spin_unlock(&tb_lock);
+ }
+ }
+ tc_ptr = tb->tc_ptr;
+ env->current_tb = tb;
+ /* execute the generated code */
+ gen_func = (void *)tc_ptr;
+#if defined(__sparc__)
+ __asm__ __volatile__("call %0\n\t"
+ "mov %%o7,%%i0"
+ : /* no outputs */
+ : "r" (gen_func)
+ : "i0", "i1", "i2", "i3", "i4", "i5",
+ "l0", "l1", "l2", "l3", "l4", "l5",
+ "l6", "l7");
+#elif defined(__arm__)
+ asm volatile ("mov pc, %0\n\t"
+ ".global exec_loop\n\t"
+ "exec_loop:\n\t"
+ : /* no outputs */
+ : "r" (gen_func)
+ : "r1", "r2", "r3", "r8", "r9", "r10", "r12", "r14");
+#elif defined(TARGET_I386) && defined(USE_CODE_COPY)
+{
+ if (!(tb->cflags & CF_CODE_COPY)) {
+ if ((tb->cflags & CF_FP_USED) && env->native_fp_regs) {
+ save_native_fp_state(env);
+ }
+ gen_func();
+ } else {
+ if ((tb->cflags & CF_FP_USED) && !env->native_fp_regs) {
+ restore_native_fp_state(env);
+ }
+ /* we work with native eflags */
+ CC_SRC = cc_table[CC_OP].compute_all();
+ CC_OP = CC_OP_EFLAGS;
+ asm(".globl exec_loop\n"
+ "\n"
+ "debug1:\n"
+ " pushl %%ebp\n"
+ " fs movl %10, %9\n"
+ " fs movl %11, %%eax\n"
+ " andl $0x400, %%eax\n"
+ " fs orl %8, %%eax\n"
+ " pushl %%eax\n"
+ " popf\n"
+ " fs movl %%esp, %12\n"
+ " fs movl %0, %%eax\n"
+ " fs movl %1, %%ecx\n"
+ " fs movl %2, %%edx\n"
+ " fs movl %3, %%ebx\n"
+ " fs movl %4, %%esp\n"
+ " fs movl %5, %%ebp\n"
+ " fs movl %6, %%esi\n"
+ " fs movl %7, %%edi\n"
+ " fs jmp *%9\n"
+ "exec_loop:\n"
+ " fs movl %%esp, %4\n"
+ " fs movl %12, %%esp\n"
+ " fs movl %%eax, %0\n"
+ " fs movl %%ecx, %1\n"
+ " fs movl %%edx, %2\n"
+ " fs movl %%ebx, %3\n"
+ " fs movl %%ebp, %5\n"
+ " fs movl %%esi, %6\n"
+ " fs movl %%edi, %7\n"
+ " pushf\n"
+ " popl %%eax\n"
+ " movl %%eax, %%ecx\n"
+ " andl $0x400, %%ecx\n"
+ " shrl $9, %%ecx\n"
+ " andl $0x8d5, %%eax\n"
+ " fs movl %%eax, %8\n"
+ " movl $1, %%eax\n"
+ " subl %%ecx, %%eax\n"
+ " fs movl %%eax, %11\n"
+ " fs movl %9, %%ebx\n" /* get T0 value */
+ " popl %%ebp\n"
+ :
+ : "m" (*(uint8_t *)offsetof(CPUState, regs[0])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[1])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[2])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[3])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[4])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[5])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[6])),
+ "m" (*(uint8_t *)offsetof(CPUState, regs[7])),
+ "m" (*(uint8_t *)offsetof(CPUState, cc_src)),
+ "m" (*(uint8_t *)offsetof(CPUState, tmp0)),
+ "a" (gen_func),
+ "m" (*(uint8_t *)offsetof(CPUState, df)),
+ "m" (*(uint8_t *)offsetof(CPUState, saved_esp))
+ : "%ecx", "%edx"
+ );
+ }
+}
+#elif defined(__ia64)
+ struct fptr {
+ void *ip;
+ void *gp;
+ } fp;
+
+ fp.ip = tc_ptr;
+ fp.gp = code_gen_buffer + 2 * (1 << 20);
+ (*(void (*)(void)) &fp)();
+#else
+ gen_func();
+#endif
+ env->current_tb = NULL;
+ /* reset soft MMU for next block (it can currently
+ only be set by a memory fault) */
+#if defined(TARGET_I386) && !defined(CONFIG_SOFTMMU)
+ if (env->hflags & HF_SOFTMMU_MASK) {
+ env->hflags &= ~HF_SOFTMMU_MASK;
+ /* do not allow linking to another block */
+ T0 = 0;
+ }
+#endif
+#if defined(USE_KQEMU)
+#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000)
+ if (kqemu_is_ok(env) &&
+ (cpu_get_time_fast() - env->last_io_time) >= MIN_CYCLE_BEFORE_SWITCH) {
+ cpu_loop_exit();
+ }
+#endif
+ }
+ } else {
+ env_to_regs();
+ }
+ } /* for(;;) */
+
+
+#if defined(TARGET_I386)
+#if defined(USE_CODE_COPY)
+ if (env->native_fp_regs) {
+ save_native_fp_state(env);
+ }
+#endif
+ /* restore flags in standard format */
+ env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+
+ /* restore global registers */
+#ifdef reg_EAX
+ EAX = saved_EAX;
+#endif
+#ifdef reg_ECX
+ ECX = saved_ECX;
+#endif
+#ifdef reg_EDX
+ EDX = saved_EDX;
+#endif
+#ifdef reg_EBX
+ EBX = saved_EBX;
+#endif
+#ifdef reg_ESP
+ ESP = saved_ESP;
+#endif
+#ifdef reg_EBP
+ EBP = saved_EBP;
+#endif
+#ifdef reg_ESI
+ ESI = saved_ESI;
+#endif
+#ifdef reg_EDI
+ EDI = saved_EDI;
+#endif
+#elif defined(TARGET_ARM)
+ /* XXX: Save/restore host fpu exception state?. */
+#elif defined(TARGET_SPARC)
+#if defined(reg_REGWPTR)
+ REGWPTR = saved_regwptr;
+#endif
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+ /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+ asm volatile ("mov %0, %%i7" : : "r" (saved_i7));
+#endif
+ T0 = saved_T0;
+ T1 = saved_T1;
+#if defined(reg_T2)
+ T2 = saved_T2;
+#endif
+ env = saved_env;
+ /* fail safe : never use cpu_single_env outside cpu_exec() */
+ cpu_single_env = NULL;
+ return ret;
+}
+
+/* must only be called from the generated code as an exception can be
+ generated */
+void tb_invalidate_page_range(target_ulong start, target_ulong end)
+{
+ /* XXX: cannot enable it yet because it yields to MMU exception
+ where NIP != read address on PowerPC */
+#if 0
+ target_ulong phys_addr;
+ phys_addr = get_phys_addr_code(env, start);
+ tb_invalidate_phys_page_range(phys_addr, phys_addr + end - start, 0);
+#endif
+}
+
+#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY)
+
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+ selector &= 0xffff;
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ (selector << 4), 0xffff, 0);
+ } else {
+ load_seg(seg_reg, selector);
+ }
+ env = saved_env;
+}
+
+void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_fsave((target_ulong)ptr, data32);
+
+ env = saved_env;
+}
+
+void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_frstor((target_ulong)ptr, data32);
+
+ env = saved_env;
+}
+
+#endif /* TARGET_I386 */
+
+#if !defined(CONFIG_SOFTMMU)
+
+#if defined(TARGET_I386)
+
+/* 'pc' is the host PC at which the exception was raised. 'address' is
+ the effective address of the memory exception. 'is_write' is 1 if a
+ write caused the exception and otherwise 0'. 'old_set' is the
+ signal set which should be restored */
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+
+ /* see if it is an MMU fault */
+ ret = cpu_x86_handle_mmu_fault(env, address, is_write,
+ ((env->hflags & HF_CPL_MASK) == 3), 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+ if (ret == 1) {
+#if 0
+ printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n",
+ env->eip, env->cr[2], env->error_code);
+#endif
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ raise_exception_err(env->exception_index, env->error_code);
+ } else {
+ /* activate soft MMU for this block */
+ env->hflags |= HF_SOFTMMU_MASK;
+ cpu_resume_from_signal(env, puc);
+ }
+ /* never comes here */
+ return 1;
+}
+
+#elif defined(TARGET_ARM)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+ /* see if it is an MMU fault */
+ ret = cpu_arm_handle_mmu_fault(env, address, is_write, 1, 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ cpu_loop_exit();
+}
+#elif defined(TARGET_SPARC)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+ /* see if it is an MMU fault */
+ ret = cpu_sparc_handle_mmu_fault(env, address, is_write, 1, 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ cpu_loop_exit();
+}
+#elif defined (TARGET_PPC)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+
+ /* see if it is an MMU fault */
+ ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+ if (ret == 1) {
+#if 0
+ printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+ env->nip, env->error_code, tb);
+#endif
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ do_raise_exception_err(env->exception_index, env->error_code);
+ } else {
+ /* activate soft MMU for this block */
+ cpu_resume_from_signal(env, puc);
+ }
+ /* never comes here */
+ return 1;
+}
+
+#elif defined (TARGET_MIPS)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+
+ /* see if it is an MMU fault */
+ ret = cpu_mips_handle_mmu_fault(env, address, is_write, 1, 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+ if (ret == 1) {
+#if 0
+ printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+ env->nip, env->error_code, tb);
+#endif
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ do_raise_exception_err(env->exception_index, env->error_code);
+ } else {
+ /* activate soft MMU for this block */
+ cpu_resume_from_signal(env, puc);
+ }
+ /* never comes here */
+ return 1;
+}
+
+#elif defined (TARGET_SH4)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+ printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+
+ /* see if it is an MMU fault */
+ ret = cpu_sh4_handle_mmu_fault(env, address, is_write, 1, 0);
+ if (ret < 0)
+ return 0; /* not an MMU fault */
+ if (ret == 0)
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, puc);
+ }
+#if 0
+ printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+ env->nip, env->error_code, tb);
+#endif
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ cpu_loop_exit();
+ /* never comes here */
+ return 1;
+}
+#else
+#error unsupported target CPU
+#endif
+
+#if defined(__i386__)
+
+#if defined(USE_CODE_COPY)
+static void cpu_send_trap(unsigned long pc, int trap,
+ struct ucontext *uc)
+{
+ TranslationBlock *tb;
+
+ if (cpu_single_env)
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, uc);
+ }
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ raise_exception_err(trap, env->error_code);
+}
+#endif
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int trapno;
+
+#ifndef REG_EIP
+/* for glibc 2.1 */
+#define REG_EIP EIP
+#define REG_ERR ERR
+#define REG_TRAPNO TRAPNO
+#endif
+ pc = uc->uc_mcontext.gregs[REG_EIP];
+ trapno = uc->uc_mcontext.gregs[REG_TRAPNO];
+#if defined(TARGET_I386) && defined(USE_CODE_COPY)
+ if (trapno == 0x00 || trapno == 0x05) {
+ /* send division by zero or bound exception */
+ cpu_send_trap(pc, trapno, uc);
+ return 1;
+ } else
+#endif
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ trapno == 0xe ?
+ (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__x86_64__)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+
+ pc = uc->uc_mcontext.gregs[REG_RIP];
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ?
+ (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__powerpc__)
+
+/***********************************************************************
+ * signal context platform-specific definitions
+ * From Wine
+ */
+#ifdef linux
+/* All Registers access - only for local access */
+# define REG_sig(reg_name, context) ((context)->uc_mcontext.regs->reg_name)
+/* Gpr Registers access */
+# define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
+# define IAR_sig(context) REG_sig(nip, context) /* Program counter */
+# define MSR_sig(context) REG_sig(msr, context) /* Machine State Register (Supervisor) */
+# define CTR_sig(context) REG_sig(ctr, context) /* Count register */
+# define XER_sig(context) REG_sig(xer, context) /* User's integer exception register */
+# define LR_sig(context) REG_sig(link, context) /* Link register */
+# define CR_sig(context) REG_sig(ccr, context) /* Condition register */
+/* Float Registers access */
+# define FLOAT_sig(reg_num, context) (((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num])
+# define FPSCR_sig(context) (*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4)))
+/* Exception Registers access */
+# define DAR_sig(context) REG_sig(dar, context)
+# define DSISR_sig(context) REG_sig(dsisr, context)
+# define TRAP_sig(context) REG_sig(trap, context)
+#endif /* linux */
+
+#ifdef __APPLE__
+# include <sys/ucontext.h>
+typedef struct ucontext SIGCONTEXT;
+/* All Registers access - only for local access */
+# define REG_sig(reg_name, context) ((context)->uc_mcontext->ss.reg_name)
+# define FLOATREG_sig(reg_name, context) ((context)->uc_mcontext->fs.reg_name)
+# define EXCEPREG_sig(reg_name, context) ((context)->uc_mcontext->es.reg_name)
+# define VECREG_sig(reg_name, context) ((context)->uc_mcontext->vs.reg_name)
+/* Gpr Registers access */
+# define GPR_sig(reg_num, context) REG_sig(r##reg_num, context)
+# define IAR_sig(context) REG_sig(srr0, context) /* Program counter */
+# define MSR_sig(context) REG_sig(srr1, context) /* Machine State Register (Supervisor) */
+# define CTR_sig(context) REG_sig(ctr, context)
+# define XER_sig(context) REG_sig(xer, context) /* Link register */
+# define LR_sig(context) REG_sig(lr, context) /* User's integer exception register */
+# define CR_sig(context) REG_sig(cr, context) /* Condition register */
+/* Float Registers access */
+# define FLOAT_sig(reg_num, context) FLOATREG_sig(fpregs[reg_num], context)
+# define FPSCR_sig(context) ((double)FLOATREG_sig(fpscr, context))
+/* Exception Registers access */
+# define DAR_sig(context) EXCEPREG_sig(dar, context) /* Fault registers for coredump */
+# define DSISR_sig(context) EXCEPREG_sig(dsisr, context)
+# define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */
+#endif /* __APPLE__ */
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+ pc = IAR_sig(uc);
+ is_write = 0;
+#if 0
+ /* ppc 4xx case */
+ if (DSISR_sig(uc) & 0x00800000)
+ is_write = 1;
+#else
+ if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000))
+ is_write = 1;
+#endif
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__alpha__)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ uint32_t *pc = uc->uc_mcontext.sc_pc;
+ uint32_t insn = *pc;
+ int is_write = 0;
+
+ /* XXX: need kernel patch to get write flag faster */
+ switch (insn >> 26) {
+ case 0x0d: // stw
+ case 0x0e: // stb
+ case 0x0f: // stq_u
+ case 0x24: // stf
+ case 0x25: // stg
+ case 0x26: // sts
+ case 0x27: // stt
+ case 0x2c: // stl
+ case 0x2d: // stq
+ case 0x2e: // stl_c
+ case 0x2f: // stq_c
+ is_write = 1;
+ }
+
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+#elif defined(__sparc__)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ uint32_t *regs = (uint32_t *)(info + 1);
+ void *sigmask = (regs + 20);
+ unsigned long pc;
+ int is_write;
+ uint32_t insn;
+
+ /* XXX: is there a standard glibc define ? */
+ pc = regs[1];
+ /* XXX: need kernel patch to get write flag faster */
+ is_write = 0;
+ insn = *(uint32_t *)pc;
+ if ((insn >> 30) == 3) {
+ switch((insn >> 19) & 0x3f) {
+ case 0x05: // stb
+ case 0x06: // sth
+ case 0x04: // st
+ case 0x07: // std
+ case 0x24: // stf
+ case 0x27: // stdf
+ case 0x25: // stfsr
+ is_write = 1;
+ break;
+ }
+ }
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, sigmask, NULL);
+}
+
+#elif defined(__arm__)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+ pc = uc->uc_mcontext.gregs[R15];
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask);
+}
+
+#elif defined(__mc68000)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+ pc = uc->uc_mcontext.gregs[16];
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__ia64)
+
+#ifndef __ISR_VALID
+ /* This ought to be in <bits/siginfo.h>... */
+# define __ISR_VALID 1
+#endif
+
+int cpu_signal_handler(int host_signum, struct siginfo *info, void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long ip;
+ int is_write = 0;
+
+ ip = uc->uc_mcontext.sc_ip;
+ switch (host_signum) {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGTRAP:
+ if (info->si_code && (info->si_segvflags & __ISR_VALID))
+ /* ISR.W (write-access) is bit 33: */
+ is_write = (info->si_isr >> 33) & 1;
+ break;
+
+ default:
+ break;
+ }
+ return handle_cpu_signal(ip, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__s390__)
+
+int cpu_signal_handler(int host_signum, struct siginfo *info,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+ pc = uc->uc_mcontext.psw.addr;
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask, puc);
+}
+
+#else
+
+#error host CPU specific signal handler needed
+
+#endif
+
+#endif /* !defined(CONFIG_SOFTMMU) */
diff --git a/dis-asm.h b/dis-asm.h
new file mode 100644
index 0000000..73b4380
--- /dev/null
+++ b/dis-asm.h
@@ -0,0 +1,455 @@
+/* Interface between the opcode library and its callers.
+ Written by Cygnus Support, 1993.
+
+ The opcode library (libopcodes.a) provides instruction decoders for
+ a large variety of instruction sets, callable with an identical
+ interface, for making instruction-processing programs more independent
+ of the instruction set being processed. */
+
+#ifndef DIS_ASM_H
+#define DIS_ASM_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define PARAMS(x) x
+typedef void *PTR;
+typedef uint64_t bfd_vma;
+typedef int64_t bfd_signed_vma;
+typedef uint8_t bfd_byte;
+#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x)
+
+#define BFD64
+
+enum bfd_flavour {
+ bfd_target_unknown_flavour,
+ bfd_target_aout_flavour,
+ bfd_target_coff_flavour,
+ bfd_target_ecoff_flavour,
+ bfd_target_elf_flavour,
+ bfd_target_ieee_flavour,
+ bfd_target_nlm_flavour,
+ bfd_target_oasys_flavour,
+ bfd_target_tekhex_flavour,
+ bfd_target_srec_flavour,
+ bfd_target_ihex_flavour,
+ bfd_target_som_flavour,
+ bfd_target_os9k_flavour,
+ bfd_target_versados_flavour,
+ bfd_target_msdos_flavour,
+ bfd_target_evax_flavour
+};
+
+enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
+
+enum bfd_architecture
+{
+ bfd_arch_unknown, /* File arch not known */
+ bfd_arch_obscure, /* Arch known, not one of these */
+ bfd_arch_m68k, /* Motorola 68xxx */
+#define bfd_mach_m68000 1
+#define bfd_mach_m68008 2
+#define bfd_mach_m68010 3
+#define bfd_mach_m68020 4
+#define bfd_mach_m68030 5
+#define bfd_mach_m68040 6
+#define bfd_mach_m68060 7
+#define bfd_mach_cpu32 8
+#define bfd_mach_mcf5200 9
+#define bfd_mach_mcf5206e 10
+#define bfd_mach_mcf5307 11
+#define bfd_mach_mcf5407 12
+#define bfd_mach_mcf528x 13
+#define bfd_mach_mcfv4e 14
+#define bfd_mach_mcf521x 15
+#define bfd_mach_mcf5249 16
+#define bfd_mach_mcf547x 17
+#define bfd_mach_mcf548x 18
+ bfd_arch_vax, /* DEC Vax */
+ bfd_arch_i960, /* Intel 960 */
+ /* The order of the following is important.
+ lower number indicates a machine type that
+ only accepts a subset of the instructions
+ available to machines with higher numbers.
+ The exception is the "ca", which is
+ incompatible with all other machines except
+ "core". */
+
+#define bfd_mach_i960_core 1
+#define bfd_mach_i960_ka_sa 2
+#define bfd_mach_i960_kb_sb 3
+#define bfd_mach_i960_mc 4
+#define bfd_mach_i960_xa 5
+#define bfd_mach_i960_ca 6
+#define bfd_mach_i960_jx 7
+#define bfd_mach_i960_hx 8
+
+ bfd_arch_a29k, /* AMD 29000 */
+ bfd_arch_sparc, /* SPARC */
+#define bfd_mach_sparc 1
+/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
+#define bfd_mach_sparc_sparclet 2
+#define bfd_mach_sparc_sparclite 3
+#define bfd_mach_sparc_v8plus 4
+#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */
+#define bfd_mach_sparc_sparclite_le 6
+#define bfd_mach_sparc_v9 7
+#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */
+#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */
+#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */
+/* Nonzero if MACH has the v9 instruction set. */
+#define bfd_mach_sparc_v9_p(mach) \
+ ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \
+ && (mach) != bfd_mach_sparc_sparclite_le)
+ bfd_arch_mips, /* MIPS Rxxxx */
+#define bfd_mach_mips3000 3000
+#define bfd_mach_mips3900 3900
+#define bfd_mach_mips4000 4000
+#define bfd_mach_mips4010 4010
+#define bfd_mach_mips4100 4100
+#define bfd_mach_mips4300 4300
+#define bfd_mach_mips4400 4400
+#define bfd_mach_mips4600 4600
+#define bfd_mach_mips4650 4650
+#define bfd_mach_mips5000 5000
+#define bfd_mach_mips6000 6000
+#define bfd_mach_mips8000 8000
+#define bfd_mach_mips10000 10000
+#define bfd_mach_mips16 16
+ bfd_arch_i386, /* Intel 386 */
+#define bfd_mach_i386_i386 0
+#define bfd_mach_i386_i8086 1
+#define bfd_mach_i386_i386_intel_syntax 2
+#define bfd_mach_x86_64 3
+#define bfd_mach_x86_64_intel_syntax 4
+ bfd_arch_we32k, /* AT&T WE32xxx */
+ bfd_arch_tahoe, /* CCI/Harris Tahoe */
+ bfd_arch_i860, /* Intel 860 */
+ bfd_arch_romp, /* IBM ROMP PC/RT */
+ bfd_arch_alliant, /* Alliant */
+ bfd_arch_convex, /* Convex */
+ bfd_arch_m88k, /* Motorola 88xxx */
+ bfd_arch_pyramid, /* Pyramid Technology */
+ bfd_arch_h8300, /* Hitachi H8/300 */
+#define bfd_mach_h8300 1
+#define bfd_mach_h8300h 2
+#define bfd_mach_h8300s 3
+ bfd_arch_powerpc, /* PowerPC */
+#define bfd_mach_ppc 0
+#define bfd_mach_ppc64 1
+#define bfd_mach_ppc_403 403
+#define bfd_mach_ppc_403gc 4030
+#define bfd_mach_ppc_505 505
+#define bfd_mach_ppc_601 601
+#define bfd_mach_ppc_602 602
+#define bfd_mach_ppc_603 603
+#define bfd_mach_ppc_ec603e 6031
+#define bfd_mach_ppc_604 604
+#define bfd_mach_ppc_620 620
+#define bfd_mach_ppc_630 630
+#define bfd_mach_ppc_750 750
+#define bfd_mach_ppc_860 860
+#define bfd_mach_ppc_a35 35
+#define bfd_mach_ppc_rs64ii 642
+#define bfd_mach_ppc_rs64iii 643
+#define bfd_mach_ppc_7400 7400
+ bfd_arch_rs6000, /* IBM RS/6000 */
+ bfd_arch_hppa, /* HP PA RISC */
+ bfd_arch_d10v, /* Mitsubishi D10V */
+ bfd_arch_z8k, /* Zilog Z8000 */
+#define bfd_mach_z8001 1
+#define bfd_mach_z8002 2
+ bfd_arch_h8500, /* Hitachi H8/500 */
+ bfd_arch_sh, /* Hitachi SH */
+#define bfd_mach_sh 1
+#define bfd_mach_sh2 0x20
+#define bfd_mach_sh_dsp 0x2d
+#define bfd_mach_sh2a 0x2a
+#define bfd_mach_sh2a_nofpu 0x2b
+#define bfd_mach_sh2e 0x2e
+#define bfd_mach_sh3 0x30
+#define bfd_mach_sh3_nommu 0x31
+#define bfd_mach_sh3_dsp 0x3d
+#define bfd_mach_sh3e 0x3e
+#define bfd_mach_sh4 0x40
+#define bfd_mach_sh4_nofpu 0x41
+#define bfd_mach_sh4_nommu_nofpu 0x42
+#define bfd_mach_sh4a 0x4a
+#define bfd_mach_sh4a_nofpu 0x4b
+#define bfd_mach_sh4al_dsp 0x4d
+#define bfd_mach_sh5 0x50
+ bfd_arch_alpha, /* Dec Alpha */
+ bfd_arch_arm, /* Advanced Risc Machines ARM */
+#define bfd_mach_arm_2 1
+#define bfd_mach_arm_2a 2
+#define bfd_mach_arm_3 3
+#define bfd_mach_arm_3M 4
+#define bfd_mach_arm_4 5
+#define bfd_mach_arm_4T 6
+ bfd_arch_ns32k, /* National Semiconductors ns32000 */
+ bfd_arch_w65, /* WDC 65816 */
+ bfd_arch_tic30, /* Texas Instruments TMS320C30 */
+ bfd_arch_v850, /* NEC V850 */
+#define bfd_mach_v850 0
+ bfd_arch_arc, /* Argonaut RISC Core */
+#define bfd_mach_arc_base 0
+ bfd_arch_m32r, /* Mitsubishi M32R/D */
+#define bfd_mach_m32r 0 /* backwards compatibility */
+ bfd_arch_mn10200, /* Matsushita MN10200 */
+ bfd_arch_mn10300, /* Matsushita MN10300 */
+ bfd_arch_last
+ };
+
+typedef struct symbol_cache_entry
+{
+ const char *name;
+ union
+ {
+ PTR p;
+ bfd_vma i;
+ } udata;
+} asymbol;
+
+typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
+
+enum dis_insn_type {
+ dis_noninsn, /* Not a valid instruction */
+ dis_nonbranch, /* Not a branch instruction */
+ dis_branch, /* Unconditional branch */
+ dis_condbranch, /* Conditional branch */
+ dis_jsr, /* Jump to subroutine */
+ dis_condjsr, /* Conditional jump to subroutine */
+ dis_dref, /* Data reference instruction */
+ dis_dref2 /* Two data references in instruction */
+};
+
+/* This struct is passed into the instruction decoding routine,
+ and is passed back out into each callback. The various fields are used
+ for conveying information from your main routine into your callbacks,
+ for passing information into the instruction decoders (such as the
+ addresses of the callback functions), or for passing information
+ back from the instruction decoders to their callers.
+
+ It must be initialized before it is first passed; this can be done
+ by hand, or using one of the initialization macros below. */
+
+typedef struct disassemble_info {
+ fprintf_ftype fprintf_func;
+ FILE *stream;
+ PTR application_data;
+
+ /* Target description. We could replace this with a pointer to the bfd,
+ but that would require one. There currently isn't any such requirement
+ so to avoid introducing one we record these explicitly. */
+ /* The bfd_flavour. This can be bfd_target_unknown_flavour. */
+ enum bfd_flavour flavour;
+ /* The bfd_arch value. */
+ enum bfd_architecture arch;
+ /* The bfd_mach value. */
+ unsigned long mach;
+ /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */
+ enum bfd_endian endian;
+
+ /* An array of pointers to symbols either at the location being disassembled
+ or at the start of the function being disassembled. The array is sorted
+ so that the first symbol is intended to be the one used. The others are
+ present for any misc. purposes. This is not set reliably, but if it is
+ not NULL, it is correct. */
+ asymbol **symbols;
+ /* Number of symbols in array. */
+ int num_symbols;
+
+ /* For use by the disassembler.
+ The top 16 bits are reserved for public use (and are documented here).
+ The bottom 16 bits are for the internal use of the disassembler. */
+ unsigned long flags;
+#define INSN_HAS_RELOC 0x80000000
+ PTR private_data;
+
+ /* Function used to get bytes to disassemble. MEMADDR is the
+ address of the stuff to be disassembled, MYADDR is the address to
+ put the bytes in, and LENGTH is the number of bytes to read.
+ INFO is a pointer to this struct.
+ Returns an errno value or 0 for success. */
+ int (*read_memory_func)
+ PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info));
+
+ /* Function which should be called if we get an error that we can't
+ recover from. STATUS is the errno value from read_memory_func and
+ MEMADDR is the address that we were trying to read. INFO is a
+ pointer to this struct. */
+ void (*memory_error_func)
+ PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
+
+ /* Function called to print ADDR. */
+ void (*print_address_func)
+ PARAMS ((bfd_vma addr, struct disassemble_info *info));
+
+ /* Function called to determine if there is a symbol at the given ADDR.
+ If there is, the function returns 1, otherwise it returns 0.
+ This is used by ports which support an overlay manager where
+ the overlay number is held in the top part of an address. In
+ some circumstances we want to include the overlay number in the
+ address, (normally because there is a symbol associated with
+ that address), but sometimes we want to mask out the overlay bits. */
+ int (* symbol_at_address_func)
+ PARAMS ((bfd_vma addr, struct disassemble_info * info));
+
+ /* These are for buffer_read_memory. */
+ bfd_byte *buffer;
+ bfd_vma buffer_vma;
+ int buffer_length;
+
+ /* This variable may be set by the instruction decoder. It suggests
+ the number of bytes objdump should display on a single line. If
+ the instruction decoder sets this, it should always set it to
+ the same value in order to get reasonable looking output. */
+ int bytes_per_line;
+
+ /* the next two variables control the way objdump displays the raw data */
+ /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */
+ /* output will look like this:
+ 00: 00000000 00000000
+ with the chunks displayed according to "display_endian". */
+ int bytes_per_chunk;
+ enum bfd_endian display_endian;
+
+ /* Results from instruction decoders. Not all decoders yet support
+ this information. This info is set each time an instruction is
+ decoded, and is only valid for the last such instruction.
+
+ To determine whether this decoder supports this information, set
+ insn_info_valid to 0, decode an instruction, then check it. */
+
+ char insn_info_valid; /* Branch info has been set. */
+ char branch_delay_insns; /* How many sequential insn's will run before
+ a branch takes effect. (0 = normal) */
+ char data_size; /* Size of data reference in insn, in bytes */
+ enum dis_insn_type insn_type; /* Type of instruction */
+ bfd_vma target; /* Target address of branch or dref, if known;
+ zero if unknown. */
+ bfd_vma target2; /* Second target address for dref2 */
+
+ /* Command line options specific to the target disassembler. */
+ char * disassembler_options;
+
+} disassemble_info;
+
+
+/* Standard disassemblers. Disassemble one instruction at the given
+ target address. Return number of bytes processed. */
+typedef int (*disassembler_ftype)
+ PARAMS((bfd_vma, disassemble_info *));
+
+extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
+extern disassembler_ftype arc_get_disassembler PARAMS ((int, int));
+extern int print_insn_arm PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m32r PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_mn10200 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_mn10300 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*));
+
+#if 0
+/* Fetch the disassembler for a given BFD, if that support is available. */
+extern disassembler_ftype disassembler PARAMS ((bfd *));
+#endif
+
+
+/* This block of definitions is for particular callers who read instructions
+ into a buffer before calling the instruction decoder. */
+
+/* Here is a function which callers may wish to use for read_memory_func.
+ It gets bytes from a buffer. */
+extern int buffer_read_memory
+ PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
+
+/* This function goes with buffer_read_memory.
+ It prints a message using info->fprintf_func and info->stream. */
+extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
+
+
+/* Just print the address in hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+extern void generic_print_address
+ PARAMS ((bfd_vma, struct disassemble_info *));
+
+/* Always true. */
+extern int generic_symbol_at_address
+ PARAMS ((bfd_vma, struct disassemble_info *));
+
+/* Macro to initialize a disassemble_info struct. This should be called
+ by all applications creating such a struct. */
+#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
+ (INFO).flavour = bfd_target_unknown_flavour, \
+ (INFO).arch = bfd_arch_unknown, \
+ (INFO).mach = 0, \
+ (INFO).endian = BFD_ENDIAN_UNKNOWN, \
+ INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC)
+
+/* Call this macro to initialize only the internal variables for the
+ disassembler. Architecture dependent things such as byte order, or machine
+ variant are not touched by this macro. This makes things much easier for
+ GDB which must initialize these things seperatly. */
+
+#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \
+ (INFO).fprintf_func = (FPRINTF_FUNC), \
+ (INFO).stream = (STREAM), \
+ (INFO).symbols = NULL, \
+ (INFO).num_symbols = 0, \
+ (INFO).buffer = NULL, \
+ (INFO).buffer_vma = 0, \
+ (INFO).buffer_length = 0, \
+ (INFO).read_memory_func = buffer_read_memory, \
+ (INFO).memory_error_func = perror_memory, \
+ (INFO).print_address_func = generic_print_address, \
+ (INFO).symbol_at_address_func = generic_symbol_at_address, \
+ (INFO).flags = 0, \
+ (INFO).bytes_per_line = 0, \
+ (INFO).bytes_per_chunk = 0, \
+ (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
+ (INFO).disassembler_options = NULL, \
+ (INFO).insn_info_valid = 0
+
+#define _(x) x
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+
+/* from libbfd */
+
+bfd_vma bfd_getl32 (const bfd_byte *addr);
+bfd_vma bfd_getb32 (const bfd_byte *addr);
+bfd_vma bfd_getl16 (const bfd_byte *addr);
+bfd_vma bfd_getb16 (const bfd_byte *addr);
+typedef enum bfd_boolean {false, true} boolean;
+typedef boolean bfd_boolean;
+
+#endif /* ! defined (DIS_ASM_H) */
diff --git a/disas.c b/disas.c
new file mode 100644
index 0000000..fd91b92
--- /dev/null
+++ b/disas.c
@@ -0,0 +1,413 @@
+/* General "disassemble this chunk" code. Used for debugging. */
+#include "config.h"
+#include "dis-asm.h"
+#include "elf.h"
+#include <errno.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+/* Filled in by elfload.c. Simplistic, but will do for now. */
+struct syminfo *syminfos = NULL;
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+ Transfer them to myaddr. */
+int
+buffer_read_memory (memaddr, myaddr, length, info)
+ bfd_vma memaddr;
+ bfd_byte *myaddr;
+ int length;
+ struct disassemble_info *info;
+{
+ if (memaddr < info->buffer_vma
+ || memaddr + length > info->buffer_vma + info->buffer_length)
+ /* Out of bounds. Use EIO because GDB uses it. */
+ return EIO;
+ memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
+ return 0;
+}
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+ Transfer them to myaddr. */
+static int
+target_read_memory (bfd_vma memaddr,
+ bfd_byte *myaddr,
+ int length,
+ struct disassemble_info *info)
+{
+ int i;
+ for(i = 0; i < length; i++) {
+ myaddr[i] = ldub_code(memaddr + i);
+ }
+ return 0;
+}
+
+/* Print an error message. We can assume that this is in response to
+ an error return from buffer_read_memory. */
+void
+perror_memory (status, memaddr, info)
+ int status;
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ if (status != EIO)
+ /* Can't happen. */
+ (*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
+ else
+ /* Actually, address between memaddr and memaddr + len was
+ out of bounds. */
+ (*info->fprintf_func) (info->stream,
+ "Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
+}
+
+/* This could be in a separate file, to save miniscule amounts of space
+ in statically linked executables. */
+
+/* Just print the address is hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+
+void
+generic_print_address (addr, info)
+ bfd_vma addr;
+ struct disassemble_info *info;
+{
+ (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
+}
+
+/* Just return the given address. */
+
+int
+generic_symbol_at_address (addr, info)
+ bfd_vma addr;
+ struct disassemble_info * info;
+{
+ return 1;
+}
+
+bfd_vma bfd_getl32 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0];
+ v |= (unsigned long) addr[1] << 8;
+ v |= (unsigned long) addr[2] << 16;
+ v |= (unsigned long) addr[3] << 24;
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb32 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0] << 24;
+ v |= (unsigned long) addr[1] << 16;
+ v |= (unsigned long) addr[2] << 8;
+ v |= (unsigned long) addr[3];
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getl16 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0];
+ v |= (unsigned long) addr[1] << 8;
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb16 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0] << 24;
+ v |= (unsigned long) addr[1] << 16;
+ return (bfd_vma) v;
+}
+
+#ifdef TARGET_ARM
+static int
+print_insn_thumb1(bfd_vma pc, disassemble_info *info)
+{
+ return print_insn_arm(pc | 1, info);
+}
+#endif
+
+/* Disassemble this for me please... (debugging). 'flags' has teh following
+ values:
+ i386 - nonzero means 16 bit code
+ arm - nonzero means thumb code
+ ppc - nonzero means little endian
+ other targets - unused
+ */
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
+{
+ target_ulong pc;
+ int count;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+ disasm_info.read_memory_func = target_read_memory;
+ disasm_info.buffer_vma = code;
+ disasm_info.buffer_length = size;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+ if (flags == 2)
+ disasm_info.mach = bfd_mach_x86_64;
+ else if (flags == 1)
+ disasm_info.mach = bfd_mach_i386_i8086;
+ else
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+ if (flags)
+ print_insn = print_insn_thumb1;
+ else
+ print_insn = print_insn_arm;
+#elif defined(TARGET_SPARC)
+ print_insn = print_insn_sparc;
+#ifdef TARGET_SPARC64
+ disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(TARGET_PPC)
+ if (flags)
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#ifdef TARGET_PPC64
+ disasm_info.mach = bfd_mach_ppc64;
+#else
+ disasm_info.mach = bfd_mach_ppc;
+#endif
+ print_insn = print_insn_ppc;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+ print_insn = print_insn_big_mips;
+#else
+ print_insn = print_insn_little_mips;
+#endif
+#elif defined(TARGET_M68K)
+ print_insn = print_insn_m68k;
+#elif defined(TARGET_SH4)
+ disasm_info.mach = bfd_mach_sh4;
+ print_insn = print_insn_sh;
+#else
+ fprintf(out, "0x" TARGET_FMT_lx
+ ": Asm output not supported on this arch\n", code);
+ return;
+#endif
+
+ for (pc = code; pc < code + size; pc += count) {
+ fprintf(out, "0x" TARGET_FMT_lx ": ", pc);
+ count = print_insn(pc, &disasm_info);
+#if 0
+ {
+ int i;
+ uint8_t b;
+ fprintf(out, " {");
+ for(i = 0; i < count; i++) {
+ target_read_memory(pc + i, &b, 1, &disasm_info);
+ fprintf(out, " %02x", b);
+ }
+ fprintf(out, " }");
+ }
+#endif
+ fprintf(out, "\n");
+ if (count < 0)
+ break;
+ }
+}
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size)
+{
+ unsigned long pc;
+ int count;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+ disasm_info.buffer = code;
+ disasm_info.buffer_vma = (unsigned long)code;
+ disasm_info.buffer_length = size;
+
+#ifdef WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(__i386__)
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(__x86_64__)
+ disasm_info.mach = bfd_mach_x86_64;
+ print_insn = print_insn_i386;
+#elif defined(__powerpc__)
+ print_insn = print_insn_ppc;
+#elif defined(__alpha__)
+ print_insn = print_insn_alpha;
+#elif defined(__sparc__)
+ print_insn = print_insn_sparc;
+#elif defined(__arm__)
+ print_insn = print_insn_arm;
+#elif defined(__MIPSEB__)
+ print_insn = print_insn_big_mips;
+#elif defined(__MIPSEL__)
+ print_insn = print_insn_little_mips;
+#elif defined(__m68k__)
+ print_insn = print_insn_m68k;
+#else
+ fprintf(out, "0x%lx: Asm output not supported on this arch\n",
+ (long) code);
+ return;
+#endif
+ for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) {
+ fprintf(out, "0x%08lx: ", pc);
+#ifdef __arm__
+ /* since data are included in the code, it is better to
+ display code data too */
+ if (is_host) {
+ fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc));
+ }
+#endif
+ count = print_insn(pc, &disasm_info);
+ fprintf(out, "\n");
+ if (count < 0)
+ break;
+ }
+}
+
+/* Look up symbol for debugging purpose. Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr)
+{
+ unsigned int i;
+ /* Hack, because we know this is x86. */
+ Elf32_Sym *sym;
+ struct syminfo *s;
+ target_ulong addr;
+
+ for (s = syminfos; s; s = s->next) {
+ sym = s->disas_symtab;
+ for (i = 0; i < s->disas_num_syms; i++) {
+ if (sym[i].st_shndx == SHN_UNDEF
+ || sym[i].st_shndx >= SHN_LORESERVE)
+ continue;
+
+ if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
+ continue;
+
+ addr = sym[i].st_value;
+#ifdef TARGET_ARM
+ /* The bottom address bit marks a Thumb symbol. */
+ addr &= ~(target_ulong)1;
+#endif
+ if (orig_addr >= addr
+ && orig_addr < addr + sym[i].st_size)
+ return s->disas_strtab + sym[i].st_name;
+ }
+ }
+ return "";
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+void term_vprintf(const char *fmt, va_list ap);
+void term_printf(const char *fmt, ...);
+
+static int monitor_disas_is_physical;
+static CPUState *monitor_disas_env;
+
+static int
+monitor_read_memory (memaddr, myaddr, length, info)
+ bfd_vma memaddr;
+ bfd_byte *myaddr;
+ int length;
+ struct disassemble_info *info;
+{
+ if (monitor_disas_is_physical) {
+ cpu_physical_memory_rw(memaddr, myaddr, length, 0);
+ } else {
+ cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0);
+ }
+ return 0;
+}
+
+static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ term_vprintf(fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+void monitor_disas(CPUState *env,
+ target_ulong pc, int nb_insn, int is_physical, int flags)
+{
+ int count, i;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf);
+
+ monitor_disas_env = env;
+ monitor_disas_is_physical = is_physical;
+ disasm_info.read_memory_func = monitor_read_memory;
+
+ disasm_info.buffer_vma = pc;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+ if (flags == 2)
+ disasm_info.mach = bfd_mach_x86_64;
+ else if (flags == 1)
+ disasm_info.mach = bfd_mach_i386_i8086;
+ else
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+ print_insn = print_insn_arm;
+#elif defined(TARGET_SPARC)
+ print_insn = print_insn_sparc;
+#elif defined(TARGET_PPC)
+#ifdef TARGET_PPC64
+ disasm_info.mach = bfd_mach_ppc64;
+#else
+ disasm_info.mach = bfd_mach_ppc;
+#endif
+ print_insn = print_insn_ppc;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+ print_insn = print_insn_big_mips;
+#else
+ print_insn = print_insn_little_mips;
+#endif
+#elif defined(TARGET_M68K)
+ print_insn = print_insn_m68k;
+#else
+ term_printf("0x" TARGET_FMT_lx
+ ": Asm output not supported on this arch\n", pc);
+ return;
+#endif
+
+ for(i = 0; i < nb_insn; i++) {
+ term_printf("0x" TARGET_FMT_lx ": ", pc);
+ count = print_insn(pc, &disasm_info);
+ term_printf("\n");
+ if (count < 0)
+ break;
+ pc += count;
+ }
+}
+#endif
diff --git a/disas.h b/disas.h
new file mode 100644
index 0000000..ee0a79c
--- /dev/null
+++ b/disas.h
@@ -0,0 +1,21 @@
+#ifndef _QEMU_DISAS_H
+#define _QEMU_DISAS_H
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size);
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
+void monitor_disas(CPUState *env,
+ target_ulong pc, int nb_insn, int is_physical, int flags);
+
+/* Look up symbol for debugging purpose. Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr);
+
+/* Filled in by elfload.c. Simplistic, but will do for now. */
+extern struct syminfo {
+ unsigned int disas_num_syms;
+ void *disas_symtab;
+ const char *disas_strtab;
+ struct syminfo *next;
+} *syminfos;
+
+#endif /* _QEMU_DISAS_H */
diff --git a/dyngen-exec.h b/dyngen-exec.h
new file mode 100644
index 0000000..0c39228
--- /dev/null
+++ b/dyngen-exec.h
@@ -0,0 +1,276 @@
+/*
+ * dyngen defines for micro operation code
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if !defined(__DYNGEN_EXEC_H__)
+#define __DYNGEN_EXEC_H__
+
+/* prevent Solaris from trying to typedef FILE in gcc's
+ include/floatingpoint.h which will conflict with the
+ definition down below */
+#ifdef __sun__
+#define _FILEDEFED
+#endif
+
+/* NOTE: standard headers should be used with special care at this
+ point because host CPU registers are used as global variables. Some
+ host headers do not allow that. */
+#include <stddef.h>
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+// Linux/Sparc64 defines uint64_t
+#if !(defined (__sparc_v9__) && defined(__linux__))
+/* XXX may be done for all 64 bits targets ? */
+#if defined (__x86_64__) || defined(__ia64)
+typedef unsigned long uint64_t;
+#else
+typedef unsigned long long uint64_t;
+#endif
+#endif
+
+/* if Solaris/__sun__, don't typedef int8_t, as it will be typedef'd
+ prior to this and will cause an error in compliation, conflicting
+ with /usr/include/sys/int_types.h, line 75 */
+#ifndef __sun__
+typedef signed char int8_t;
+#endif
+typedef signed short int16_t;
+typedef signed int int32_t;
+// Linux/Sparc64 defines int64_t
+#if !(defined (__sparc_v9__) && defined(__linux__))
+#if defined (__x86_64__) || defined(__ia64)
+typedef signed long int64_t;
+#else
+typedef signed long long int64_t;
+#endif
+#endif
+
+#define INT8_MIN (-128)
+#define INT16_MIN (-32767-1)
+#define INT32_MIN (-2147483647-1)
+#define INT64_MIN (-(int64_t)(9223372036854775807)-1)
+#define INT8_MAX (127)
+#define INT16_MAX (32767)
+#define INT32_MAX (2147483647)
+#define INT64_MAX ((int64_t)(9223372036854775807))
+#define UINT8_MAX (255)
+#define UINT16_MAX (65535)
+#define UINT32_MAX (4294967295U)
+#define UINT64_MAX ((uint64_t)(18446744073709551615))
+
+typedef struct FILE FILE;
+extern int fprintf(FILE *, const char *, ...);
+extern int printf(const char *, ...);
+#undef NULL
+#define NULL 0
+
+#ifdef __i386__
+#define AREG0 "ebp"
+#define AREG1 "ebx"
+#define AREG2 "esi"
+#define AREG3 "edi"
+#endif
+#ifdef __x86_64__
+#define AREG0 "rbp"
+#define AREG1 "rbx"
+#define AREG2 "r12"
+#define AREG3 "r13"
+//#define AREG4 "r14"
+//#define AREG5 "r15"
+#endif
+#ifdef __powerpc__
+#define AREG0 "r27"
+#define AREG1 "r24"
+#define AREG2 "r25"
+#define AREG3 "r26"
+/* XXX: suppress this hack */
+#if defined(CONFIG_USER_ONLY)
+#define AREG4 "r16"
+#define AREG5 "r17"
+#define AREG6 "r18"
+#define AREG7 "r19"
+#define AREG8 "r20"
+#define AREG9 "r21"
+#define AREG10 "r22"
+#define AREG11 "r23"
+#endif
+#define USE_INT_TO_FLOAT_HELPERS
+#define BUGGY_GCC_DIV64
+#endif
+#ifdef __arm__
+#define AREG0 "r7"
+#define AREG1 "r4"
+#define AREG2 "r5"
+#define AREG3 "r6"
+#endif
+#ifdef __mips__
+#define AREG0 "s3"
+#define AREG1 "s0"
+#define AREG2 "s1"
+#define AREG3 "s2"
+#endif
+#ifdef __sparc__
+#ifdef HOST_SOLARIS
+#define AREG0 "g2"
+#define AREG1 "g3"
+#define AREG2 "g4"
+#define AREG3 "g5"
+#define AREG4 "g6"
+#else
+#ifdef __sparc_v9__
+#define AREG0 "g1"
+#define AREG1 "g4"
+#define AREG2 "g5"
+#define AREG3 "g7"
+#else
+#define AREG0 "g6"
+#define AREG1 "g1"
+#define AREG2 "g2"
+#define AREG3 "g3"
+#define AREG4 "l0"
+#define AREG5 "l1"
+#define AREG6 "l2"
+#define AREG7 "l3"
+#define AREG8 "l4"
+#define AREG9 "l5"
+#define AREG10 "l6"
+#define AREG11 "l7"
+#endif
+#endif
+#define USE_FP_CONVERT
+#endif
+#ifdef __s390__
+#define AREG0 "r10"
+#define AREG1 "r7"
+#define AREG2 "r8"
+#define AREG3 "r9"
+#endif
+#ifdef __alpha__
+/* Note $15 is the frame pointer, so anything in op-i386.c that would
+ require a frame pointer, like alloca, would probably loose. */
+#define AREG0 "$15"
+#define AREG1 "$9"
+#define AREG2 "$10"
+#define AREG3 "$11"
+#define AREG4 "$12"
+#define AREG5 "$13"
+#define AREG6 "$14"
+#endif
+#ifdef __mc68000
+#define AREG0 "%a5"
+#define AREG1 "%a4"
+#define AREG2 "%d7"
+#define AREG3 "%d6"
+#define AREG4 "%d5"
+#endif
+#ifdef __ia64__
+#define AREG0 "r7"
+#define AREG1 "r4"
+#define AREG2 "r5"
+#define AREG3 "r6"
+#endif
+
+/* force GCC to generate only one epilog at the end of the function */
+#define FORCE_RET() asm volatile ("");
+
+#ifndef OPPROTO
+#define OPPROTO
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#ifdef __alpha__
+/* the symbols are considered non exported so a br immediate is generated */
+#define __hidden __attribute__((visibility("hidden")))
+#else
+#define __hidden
+#endif
+
+#if defined(__alpha__)
+/* Suggested by Richard Henderson. This will result in code like
+ ldah $0,__op_param1($29) !gprelhigh
+ lda $0,__op_param1($0) !gprellow
+ We can then conveniently change $29 to $31 and adapt the offsets to
+ emit the appropriate constant. */
+extern int __op_param1 __hidden;
+extern int __op_param2 __hidden;
+extern int __op_param3 __hidden;
+#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
+#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
+#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
+#else
+#if defined(__APPLE__)
+static int __op_param1, __op_param2, __op_param3;
+#else
+extern int __op_param1, __op_param2, __op_param3;
+#endif
+#define PARAM1 ((long)(&__op_param1))
+#define PARAM2 ((long)(&__op_param2))
+#define PARAM3 ((long)(&__op_param3))
+#endif /* !defined(__alpha__) */
+
+extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
+
+#if defined(_WIN32) || defined(__APPLE__)
+#define ASM_NAME(x) "_" #x
+#else
+#define ASM_NAME(x) #x
+#endif
+
+#ifdef __i386__
+#define EXIT_TB() asm volatile ("ret")
+#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __x86_64__
+#define EXIT_TB() asm volatile ("ret")
+#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __powerpc__
+#define EXIT_TB() asm volatile ("blr")
+#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __s390__
+#define EXIT_TB() asm volatile ("br %r14")
+#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __alpha__
+#define EXIT_TB() asm volatile ("ret")
+#endif
+#ifdef __ia64__
+#define EXIT_TB() asm volatile ("br.ret.sptk.many b0;;")
+#define GOTO_LABEL_PARAM(n) asm volatile ("br.sptk.many " \
+ ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __sparc__
+#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0; nop")
+#define GOTO_LABEL_PARAM(n) asm volatile ("ba " ASM_NAME(__op_gen_label) #n ";nop")
+#endif
+#ifdef __arm__
+#define EXIT_TB() asm volatile ("b exec_loop")
+#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
+#endif
+#ifdef __mc68000
+#define EXIT_TB() asm volatile ("rts")
+#endif
+
+#endif /* !defined(__DYNGEN_EXEC_H__) */
diff --git a/dyngen-op.h b/dyngen-op.h
new file mode 100644
index 0000000..f77a475
--- /dev/null
+++ b/dyngen-op.h
@@ -0,0 +1,9 @@
+static inline int gen_new_label(void)
+{
+ return nb_gen_labels++;
+}
+
+static inline void gen_set_label(int n)
+{
+ gen_labels[n] = gen_opc_ptr - gen_opc_buf;
+}
diff --git a/dyngen.c b/dyngen.c
new file mode 100644
index 0000000..5fb921e
--- /dev/null
+++ b/dyngen.c
@@ -0,0 +1,2618 @@
+/*
+ * Generic Dynamic compiler generator
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * The COFF object format support was extracted from Kazu's QEMU port
+ * to Win32.
+ *
+ * Mach-O Support by Matt Reda and Pierre d'Herbemont
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "config-host.h"
+
+/* NOTE: we test CONFIG_WIN32 instead of _WIN32 to enabled cross
+ compilation */
+#if defined(CONFIG_WIN32)
+#define CONFIG_FORMAT_COFF
+#elif defined(CONFIG_DARWIN)
+#define CONFIG_FORMAT_MACH
+#else
+#define CONFIG_FORMAT_ELF
+#endif
+
+#ifdef CONFIG_FORMAT_ELF
+
+/* elf format definitions. We use these macros to test the CPU to
+ allow cross compilation (this tool must be ran on the build
+ platform) */
+#if defined(HOST_I386)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_386
+#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) )
+#undef ELF_USES_RELOCA
+
+#elif defined(HOST_X86_64)
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_X86_64
+#define elf_check_arch(x) ((x) == EM_X86_64)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_PPC)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_PPC
+#define elf_check_arch(x) ((x) == EM_PPC)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_S390)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_S390
+#define elf_check_arch(x) ((x) == EM_S390)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_ALPHA)
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_ALPHA
+#define elf_check_arch(x) ((x) == EM_ALPHA)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_IA64)
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_IA_64
+#define elf_check_arch(x) ((x) == EM_IA_64)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_SPARC)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_SPARC
+#define elf_check_arch(x) ((x) == EM_SPARC || (x) == EM_SPARC32PLUS)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_SPARC64)
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_SPARCV9
+#define elf_check_arch(x) ((x) == EM_SPARCV9)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_ARM)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_ARM
+#define elf_check_arch(x) ((x) == EM_ARM)
+#define ELF_USES_RELOC
+
+#elif defined(HOST_M68K)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_68K
+#define elf_check_arch(x) ((x) == EM_68K)
+#define ELF_USES_RELOCA
+
+#else
+#error unsupported CPU - please update the code
+#endif
+
+#include "elf.h"
+
+#if ELF_CLASS == ELFCLASS32
+typedef int32_t host_long;
+typedef uint32_t host_ulong;
+#define swabls(x) swab32s(x)
+#else
+typedef int64_t host_long;
+typedef uint64_t host_ulong;
+#define swabls(x) swab64s(x)
+#endif
+
+#ifdef ELF_USES_RELOCA
+#define SHT_RELOC SHT_RELA
+#else
+#define SHT_RELOC SHT_REL
+#endif
+
+#define EXE_RELOC ELF_RELOC
+#define EXE_SYM ElfW(Sym)
+
+#endif /* CONFIG_FORMAT_ELF */
+
+#ifdef CONFIG_FORMAT_COFF
+
+#include "a.out.h"
+
+typedef int32_t host_long;
+typedef uint32_t host_ulong;
+
+#define FILENAMELEN 256
+
+typedef struct coff_sym {
+ struct external_syment *st_syment;
+ char st_name[FILENAMELEN];
+ uint32_t st_value;
+ int st_size;
+ uint8_t st_type;
+ uint8_t st_shndx;
+} coff_Sym;
+
+typedef struct coff_rel {
+ struct external_reloc *r_reloc;
+ int r_offset;
+ uint8_t r_type;
+} coff_Rel;
+
+#define EXE_RELOC struct coff_rel
+#define EXE_SYM struct coff_sym
+
+#endif /* CONFIG_FORMAT_COFF */
+
+#ifdef CONFIG_FORMAT_MACH
+
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+#include <mach-o/ppc/reloc.h>
+
+# define check_mach_header(x) (x.magic == MH_MAGIC)
+typedef int32_t host_long;
+typedef uint32_t host_ulong;
+
+struct nlist_extended
+{
+ union {
+ char *n_name;
+ long n_strx;
+ } n_un;
+ unsigned char n_type;
+ unsigned char n_sect;
+ short st_desc;
+ unsigned long st_value;
+ unsigned long st_size;
+};
+
+#define EXE_RELOC struct relocation_info
+#define EXE_SYM struct nlist_extended
+
+#endif /* CONFIG_FORMAT_MACH */
+
+#include "bswap.h"
+
+enum {
+ OUT_GEN_OP,
+ OUT_CODE,
+ OUT_INDEX_OP,
+};
+
+/* all dynamically generated functions begin with this code */
+#define OP_PREFIX "op_"
+
+int do_swap;
+
+void __attribute__((noreturn)) __attribute__((format (printf, 1, 2))) error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "dyngen: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+void *load_data(int fd, long offset, unsigned int size)
+{
+ char *data;
+
+ data = malloc(size);
+ if (!data)
+ return NULL;
+ lseek(fd, offset, SEEK_SET);
+ if (read(fd, data, size) != size) {
+ free(data);
+ return NULL;
+ }
+ return data;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+void swab16s(uint16_t *p)
+{
+ *p = bswap16(*p);
+}
+
+void swab32s(uint32_t *p)
+{
+ *p = bswap32(*p);
+}
+
+void swab64s(uint64_t *p)
+{
+ *p = bswap64(*p);
+}
+
+uint16_t get16(uint16_t *p)
+{
+ uint16_t val;
+ val = *p;
+ if (do_swap)
+ val = bswap16(val);
+ return val;
+}
+
+uint32_t get32(uint32_t *p)
+{
+ uint32_t val;
+ val = *p;
+ if (do_swap)
+ val = bswap32(val);
+ return val;
+}
+
+void put16(uint16_t *p, uint16_t val)
+{
+ if (do_swap)
+ val = bswap16(val);
+ *p = val;
+}
+
+void put32(uint32_t *p, uint32_t val)
+{
+ if (do_swap)
+ val = bswap32(val);
+ *p = val;
+}
+
+/* executable information */
+EXE_SYM *symtab;
+int nb_syms;
+int text_shndx;
+uint8_t *text;
+EXE_RELOC *relocs;
+int nb_relocs;
+
+#ifdef CONFIG_FORMAT_ELF
+
+/* ELF file info */
+struct elf_shdr *shdr;
+uint8_t **sdata;
+struct elfhdr ehdr;
+char *strtab;
+
+int elf_must_swap(struct elfhdr *h)
+{
+ union {
+ uint32_t i;
+ uint8_t b[4];
+ } swaptest;
+
+ swaptest.i = 1;
+ return (h->e_ident[EI_DATA] == ELFDATA2MSB) !=
+ (swaptest.b[0] == 0);
+}
+
+void elf_swap_ehdr(struct elfhdr *h)
+{
+ swab16s(&h->e_type); /* Object file type */
+ swab16s(&h-> e_machine); /* Architecture */
+ swab32s(&h-> e_version); /* Object file version */
+ swabls(&h-> e_entry); /* Entry point virtual address */
+ swabls(&h-> e_phoff); /* Program header table file offset */
+ swabls(&h-> e_shoff); /* Section header table file offset */
+ swab32s(&h-> e_flags); /* Processor-specific flags */
+ swab16s(&h-> e_ehsize); /* ELF header size in bytes */
+ swab16s(&h-> e_phentsize); /* Program header table entry size */
+ swab16s(&h-> e_phnum); /* Program header table entry count */
+ swab16s(&h-> e_shentsize); /* Section header table entry size */
+ swab16s(&h-> e_shnum); /* Section header table entry count */
+ swab16s(&h-> e_shstrndx); /* Section header string table index */
+}
+
+void elf_swap_shdr(struct elf_shdr *h)
+{
+ swab32s(&h-> sh_name); /* Section name (string tbl index) */
+ swab32s(&h-> sh_type); /* Section type */
+ swabls(&h-> sh_flags); /* Section flags */
+ swabls(&h-> sh_addr); /* Section virtual addr at execution */
+ swabls(&h-> sh_offset); /* Section file offset */
+ swabls(&h-> sh_size); /* Section size in bytes */
+ swab32s(&h-> sh_link); /* Link to another section */
+ swab32s(&h-> sh_info); /* Additional section information */
+ swabls(&h-> sh_addralign); /* Section alignment */
+ swabls(&h-> sh_entsize); /* Entry size if section holds table */
+}
+
+void elf_swap_phdr(struct elf_phdr *h)
+{
+ swab32s(&h->p_type); /* Segment type */
+ swabls(&h->p_offset); /* Segment file offset */
+ swabls(&h->p_vaddr); /* Segment virtual address */
+ swabls(&h->p_paddr); /* Segment physical address */
+ swabls(&h->p_filesz); /* Segment size in file */
+ swabls(&h->p_memsz); /* Segment size in memory */
+ swab32s(&h->p_flags); /* Segment flags */
+ swabls(&h->p_align); /* Segment alignment */
+}
+
+void elf_swap_rel(ELF_RELOC *rel)
+{
+ swabls(&rel->r_offset);
+ swabls(&rel->r_info);
+#ifdef ELF_USES_RELOCA
+ swabls(&rel->r_addend);
+#endif
+}
+
+struct elf_shdr *find_elf_section(struct elf_shdr *shdr, int shnum, const char *shstr,
+ const char *name)
+{
+ int i;
+ const char *shname;
+ struct elf_shdr *sec;
+
+ for(i = 0; i < shnum; i++) {
+ sec = &shdr[i];
+ if (!sec->sh_name)
+ continue;
+ shname = shstr + sec->sh_name;
+ if (!strcmp(shname, name))
+ return sec;
+ }
+ return NULL;
+}
+
+int find_reloc(int sh_index)
+{
+ struct elf_shdr *sec;
+ int i;
+
+ for(i = 0; i < ehdr.e_shnum; i++) {
+ sec = &shdr[i];
+ if (sec->sh_type == SHT_RELOC && sec->sh_info == sh_index)
+ return i;
+ }
+ return 0;
+}
+
+static host_ulong get_rel_offset(EXE_RELOC *rel)
+{
+ return rel->r_offset;
+}
+
+static char *get_rel_sym_name(EXE_RELOC *rel)
+{
+ return strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+}
+
+static char *get_sym_name(EXE_SYM *sym)
+{
+ return strtab + sym->st_name;
+}
+
+/* load an elf object file */
+int load_object(const char *filename)
+{
+ int fd;
+ struct elf_shdr *sec, *symtab_sec, *strtab_sec, *text_sec;
+ int i, j;
+ ElfW(Sym) *sym;
+ char *shstr;
+ ELF_RELOC *rel;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ error("can't open file '%s'", filename);
+
+ /* Read ELF header. */
+ if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr))
+ error("unable to read file header");
+
+ /* Check ELF identification. */
+ if (ehdr.e_ident[EI_MAG0] != ELFMAG0
+ || ehdr.e_ident[EI_MAG1] != ELFMAG1
+ || ehdr.e_ident[EI_MAG2] != ELFMAG2
+ || ehdr.e_ident[EI_MAG3] != ELFMAG3
+ || ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
+ error("bad ELF header");
+ }
+
+ do_swap = elf_must_swap(&ehdr);
+ if (do_swap)
+ elf_swap_ehdr(&ehdr);
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS)
+ error("Unsupported ELF class");
+ if (ehdr.e_type != ET_REL)
+ error("ELF object file expected");
+ if (ehdr.e_version != EV_CURRENT)
+ error("Invalid ELF version");
+ if (!elf_check_arch(ehdr.e_machine))
+ error("Unsupported CPU (e_machine=%d)", ehdr.e_machine);
+
+ /* read section headers */
+ shdr = load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(struct elf_shdr));
+ if (do_swap) {
+ for(i = 0; i < ehdr.e_shnum; i++) {
+ elf_swap_shdr(&shdr[i]);
+ }
+ }
+
+ /* read all section data */
+ sdata = malloc(sizeof(void *) * ehdr.e_shnum);
+ memset(sdata, 0, sizeof(void *) * ehdr.e_shnum);
+
+ for(i = 0;i < ehdr.e_shnum; i++) {
+ sec = &shdr[i];
+ if (sec->sh_type != SHT_NOBITS)
+ sdata[i] = load_data(fd, sec->sh_offset, sec->sh_size);
+ }
+
+ sec = &shdr[ehdr.e_shstrndx];
+ shstr = sdata[ehdr.e_shstrndx];
+
+ /* swap relocations */
+ for(i = 0; i < ehdr.e_shnum; i++) {
+ sec = &shdr[i];
+ if (sec->sh_type == SHT_RELOC) {
+ nb_relocs = sec->sh_size / sec->sh_entsize;
+ if (do_swap) {
+ for(j = 0, rel = (ELF_RELOC *)sdata[i]; j < nb_relocs; j++, rel++)
+ elf_swap_rel(rel);
+ }
+ }
+ }
+ /* text section */
+
+ text_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".text");
+ if (!text_sec)
+ error("could not find .text section");
+ text_shndx = text_sec - shdr;
+ text = sdata[text_shndx];
+
+ /* find text relocations, if any */
+ relocs = NULL;
+ nb_relocs = 0;
+ i = find_reloc(text_shndx);
+ if (i != 0) {
+ relocs = (ELF_RELOC *)sdata[i];
+ nb_relocs = shdr[i].sh_size / shdr[i].sh_entsize;
+ }
+
+ symtab_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".symtab");
+ if (!symtab_sec)
+ error("could not find .symtab section");
+ strtab_sec = &shdr[symtab_sec->sh_link];
+
+ symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr];
+ strtab = sdata[symtab_sec->sh_link];
+
+ nb_syms = symtab_sec->sh_size / sizeof(ElfW(Sym));
+ if (do_swap) {
+ for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ swab32s(&sym->st_name);
+ swabls(&sym->st_value);
+ swabls(&sym->st_size);
+ swab16s(&sym->st_shndx);
+ }
+ }
+ close(fd);
+ return 0;
+}
+
+#endif /* CONFIG_FORMAT_ELF */
+
+#ifdef CONFIG_FORMAT_COFF
+
+/* COFF file info */
+struct external_scnhdr *shdr;
+uint8_t **sdata;
+struct external_filehdr fhdr;
+struct external_syment *coff_symtab;
+char *strtab;
+int coff_text_shndx, coff_data_shndx;
+
+int data_shndx;
+
+#define STRTAB_SIZE 4
+
+#define DIR32 0x06
+#define DISP32 0x14
+
+#define T_FUNCTION 0x20
+#define C_EXTERNAL 2
+
+void sym_ent_name(struct external_syment *ext_sym, EXE_SYM *sym)
+{
+ char *q;
+ int c, i, len;
+
+ if (ext_sym->e.e.e_zeroes != 0) {
+ q = sym->st_name;
+ for(i = 0; i < 8; i++) {
+ c = ext_sym->e.e_name[i];
+ if (c == '\0')
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+ } else {
+ pstrcpy(sym->st_name, sizeof(sym->st_name), strtab + ext_sym->e.e.e_offset);
+ }
+
+ /* now convert the name to a C name (suppress the leading '_') */
+ if (sym->st_name[0] == '_') {
+ len = strlen(sym->st_name);
+ memmove(sym->st_name, sym->st_name + 1, len - 1);
+ sym->st_name[len - 1] = '\0';
+ }
+}
+
+char *name_for_dotdata(struct coff_rel *rel)
+{
+ int i;
+ struct coff_sym *sym;
+ uint32_t text_data;
+
+ text_data = *(uint32_t *)(text + rel->r_offset);
+
+ for (i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ if (sym->st_syment->e_scnum == data_shndx &&
+ text_data >= sym->st_value &&
+ text_data < sym->st_value + sym->st_size) {
+
+ return sym->st_name;
+
+ }
+ }
+ return NULL;
+}
+
+static char *get_sym_name(EXE_SYM *sym)
+{
+ return sym->st_name;
+}
+
+static char *get_rel_sym_name(EXE_RELOC *rel)
+{
+ char *name;
+ name = get_sym_name(symtab + *(uint32_t *)(rel->r_reloc->r_symndx));
+ if (!strcmp(name, ".data"))
+ name = name_for_dotdata(rel);
+ if (name[0] == '.')
+ return NULL;
+ return name;
+}
+
+static host_ulong get_rel_offset(EXE_RELOC *rel)
+{
+ return rel->r_offset;
+}
+
+struct external_scnhdr *find_coff_section(struct external_scnhdr *shdr, int shnum, const char *name)
+{
+ int i;
+ const char *shname;
+ struct external_scnhdr *sec;
+
+ for(i = 0; i < shnum; i++) {
+ sec = &shdr[i];
+ if (!sec->s_name)
+ continue;
+ shname = sec->s_name;
+ if (!strcmp(shname, name))
+ return sec;
+ }
+ return NULL;
+}
+
+/* load a coff object file */
+int load_object(const char *filename)
+{
+ int fd;
+ struct external_scnhdr *sec, *text_sec, *data_sec;
+ int i;
+ struct external_syment *ext_sym;
+ struct external_reloc *coff_relocs;
+ struct external_reloc *ext_rel;
+ uint32_t *n_strtab;
+ EXE_SYM *sym;
+ EXE_RELOC *rel;
+
+ fd = open(filename, O_RDONLY
+#ifdef _WIN32
+ | O_BINARY
+#endif
+ );
+ if (fd < 0)
+ error("can't open file '%s'", filename);
+
+ /* Read COFF header. */
+ if (read(fd, &fhdr, sizeof (fhdr)) != sizeof (fhdr))
+ error("unable to read file header");
+
+ /* Check COFF identification. */
+ if (fhdr.f_magic != I386MAGIC) {
+ error("bad COFF header");
+ }
+ do_swap = 0;
+
+ /* read section headers */
+ shdr = load_data(fd, sizeof(struct external_filehdr) + fhdr.f_opthdr, fhdr.f_nscns * sizeof(struct external_scnhdr));
+
+ /* read all section data */
+ sdata = malloc(sizeof(void *) * fhdr.f_nscns);
+ memset(sdata, 0, sizeof(void *) * fhdr.f_nscns);
+
+ const char *p;
+ for(i = 0;i < fhdr.f_nscns; i++) {
+ sec = &shdr[i];
+ if (!strstart(sec->s_name, ".bss", &p))
+ sdata[i] = load_data(fd, sec->s_scnptr, sec->s_size);
+ }
+
+
+ /* text section */
+ text_sec = find_coff_section(shdr, fhdr.f_nscns, ".text");
+ if (!text_sec)
+ error("could not find .text section");
+ coff_text_shndx = text_sec - shdr;
+ text = sdata[coff_text_shndx];
+
+ /* data section */
+ data_sec = find_coff_section(shdr, fhdr.f_nscns, ".data");
+ if (!data_sec)
+ error("could not find .data section");
+ coff_data_shndx = data_sec - shdr;
+
+ coff_symtab = load_data(fd, fhdr.f_symptr, fhdr.f_nsyms*SYMESZ);
+ for (i = 0, ext_sym = coff_symtab; i < nb_syms; i++, ext_sym++) {
+ for(i=0;i<8;i++)
+ printf(" %02x", ((uint8_t *)ext_sym->e.e_name)[i]);
+ printf("\n");
+ }
+
+
+ n_strtab = load_data(fd, (fhdr.f_symptr + fhdr.f_nsyms*SYMESZ), STRTAB_SIZE);
+ strtab = load_data(fd, (fhdr.f_symptr + fhdr.f_nsyms*SYMESZ), *n_strtab);
+
+ nb_syms = fhdr.f_nsyms;
+
+ for (i = 0, ext_sym = coff_symtab; i < nb_syms; i++, ext_sym++) {
+ if (strstart(ext_sym->e.e_name, ".text", NULL))
+ text_shndx = ext_sym->e_scnum;
+ if (strstart(ext_sym->e.e_name, ".data", NULL))
+ data_shndx = ext_sym->e_scnum;
+ }
+
+ /* set coff symbol */
+ symtab = malloc(sizeof(struct coff_sym) * nb_syms);
+
+ int aux_size, j;
+ for (i = 0, ext_sym = coff_symtab, sym = symtab; i < nb_syms; i++, ext_sym++, sym++) {
+ memset(sym, 0, sizeof(*sym));
+ sym->st_syment = ext_sym;
+ sym_ent_name(ext_sym, sym);
+ sym->st_value = ext_sym->e_value;
+
+ aux_size = *(int8_t *)ext_sym->e_numaux;
+ if (ext_sym->e_scnum == text_shndx && ext_sym->e_type == T_FUNCTION) {
+ for (j = aux_size + 1; j < nb_syms - i; j++) {
+ if ((ext_sym + j)->e_scnum == text_shndx &&
+ (ext_sym + j)->e_type == T_FUNCTION ){
+ sym->st_size = (ext_sym + j)->e_value - ext_sym->e_value;
+ break;
+ } else if (j == nb_syms - i - 1) {
+ sec = &shdr[coff_text_shndx];
+ sym->st_size = sec->s_size - ext_sym->e_value;
+ break;
+ }
+ }
+ } else if (ext_sym->e_scnum == data_shndx && *(uint8_t *)ext_sym->e_sclass == C_EXTERNAL) {
+ for (j = aux_size + 1; j < nb_syms - i; j++) {
+ if ((ext_sym + j)->e_scnum == data_shndx) {
+ sym->st_size = (ext_sym + j)->e_value - ext_sym->e_value;
+ break;
+ } else if (j == nb_syms - i - 1) {
+ sec = &shdr[coff_data_shndx];
+ sym->st_size = sec->s_size - ext_sym->e_value;
+ break;
+ }
+ }
+ } else {
+ sym->st_size = 0;
+ }
+
+ sym->st_type = ext_sym->e_type;
+ sym->st_shndx = ext_sym->e_scnum;
+ }
+
+
+ /* find text relocations, if any */
+ sec = &shdr[coff_text_shndx];
+ coff_relocs = load_data(fd, sec->s_relptr, sec->s_nreloc*RELSZ);
+ nb_relocs = sec->s_nreloc;
+
+ /* set coff relocation */
+ relocs = malloc(sizeof(struct coff_rel) * nb_relocs);
+ for (i = 0, ext_rel = coff_relocs, rel = relocs; i < nb_relocs;
+ i++, ext_rel++, rel++) {
+ memset(rel, 0, sizeof(*rel));
+ rel->r_reloc = ext_rel;
+ rel->r_offset = *(uint32_t *)ext_rel->r_vaddr;
+ rel->r_type = *(uint16_t *)ext_rel->r_type;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_FORMAT_COFF */
+
+#ifdef CONFIG_FORMAT_MACH
+
+/* File Header */
+struct mach_header mach_hdr;
+
+/* commands */
+struct segment_command *segment = 0;
+struct dysymtab_command *dysymtabcmd = 0;
+struct symtab_command *symtabcmd = 0;
+
+/* section */
+struct section *section_hdr;
+struct section *text_sec_hdr;
+uint8_t **sdata;
+
+/* relocs */
+struct relocation_info *relocs;
+
+/* symbols */
+EXE_SYM *symtab;
+struct nlist *symtab_std;
+char *strtab;
+
+/* indirect symbols */
+uint32_t *tocdylib;
+
+/* Utility functions */
+
+static inline char *find_str_by_index(int index)
+{
+ return strtab+index;
+}
+
+/* Used by dyngen common code */
+static char *get_sym_name(EXE_SYM *sym)
+{
+ char *name = find_str_by_index(sym->n_un.n_strx);
+
+ if ( sym->n_type & N_STAB ) /* Debug symbols are ignored */
+ return "debug";
+
+ if(!name)
+ return name;
+ if(name[0]=='_')
+ return name + 1;
+ else
+ return name;
+}
+
+/* find a section index given its segname, sectname */
+static int find_mach_sec_index(struct section *section_hdr, int shnum, const char *segname,
+ const char *sectname)
+{
+ int i;
+ struct section *sec = section_hdr;
+
+ for(i = 0; i < shnum; i++, sec++) {
+ if (!sec->segname || !sec->sectname)
+ continue;
+ if (!strcmp(sec->sectname, sectname) && !strcmp(sec->segname, segname))
+ return i;
+ }
+ return -1;
+}
+
+/* find a section header given its segname, sectname */
+struct section *find_mach_sec_hdr(struct section *section_hdr, int shnum, const char *segname,
+ const char *sectname)
+{
+ int index = find_mach_sec_index(section_hdr, shnum, segname, sectname);
+ if(index == -1)
+ return NULL;
+ return section_hdr+index;
+}
+
+
+static inline void fetch_next_pair_value(struct relocation_info * rel, unsigned int *value)
+{
+ struct scattered_relocation_info * scarel;
+
+ if(R_SCATTERED & rel->r_address) {
+ scarel = (struct scattered_relocation_info*)rel;
+ if(scarel->r_type != PPC_RELOC_PAIR)
+ error("fetch_next_pair_value: looking for a pair which was not found (1)");
+ *value = scarel->r_value;
+ } else {
+ if(rel->r_type != PPC_RELOC_PAIR)
+ error("fetch_next_pair_value: looking for a pair which was not found (2)");
+ *value = rel->r_address;
+ }
+}
+
+/* find a sym name given its value, in a section number */
+static const char * find_sym_with_value_and_sec_number( int value, int sectnum, int * offset )
+{
+ int i, ret = -1;
+
+ for( i = 0 ; i < nb_syms; i++ )
+ {
+ if( !(symtab[i].n_type & N_STAB) && (symtab[i].n_type & N_SECT) &&
+ (symtab[i].n_sect == sectnum) && (symtab[i].st_value <= value) )
+ {
+ if( (ret<0) || (symtab[i].st_value >= symtab[ret].st_value) )
+ ret = i;
+ }
+ }
+ if( ret < 0 ) {
+ *offset = 0;
+ return 0;
+ } else {
+ *offset = value - symtab[ret].st_value;
+ return get_sym_name(&symtab[ret]);
+ }
+}
+
+/*
+ * Find symbol name given a (virtual) address, and a section which is of type
+ * S_NON_LAZY_SYMBOL_POINTERS or S_LAZY_SYMBOL_POINTERS or S_SYMBOL_STUBS
+ */
+static const char * find_reloc_name_in_sec_ptr(int address, struct section * sec_hdr)
+{
+ unsigned int tocindex, symindex, size;
+ const char *name = 0;
+
+ /* Sanity check */
+ if(!( address >= sec_hdr->addr && address < (sec_hdr->addr + sec_hdr->size) ) )
+ return (char*)0;
+
+ if( sec_hdr->flags & S_SYMBOL_STUBS ){
+ size = sec_hdr->reserved2;
+ if(size == 0)
+ error("size = 0");
+
+ }
+ else if( sec_hdr->flags & S_LAZY_SYMBOL_POINTERS ||
+ sec_hdr->flags & S_NON_LAZY_SYMBOL_POINTERS)
+ size = sizeof(unsigned long);
+ else
+ return 0;
+
+ /* Compute our index in toc */
+ tocindex = (address - sec_hdr->addr)/size;
+ symindex = tocdylib[sec_hdr->reserved1 + tocindex];
+
+ name = get_sym_name(&symtab[symindex]);
+
+ return name;
+}
+
+static const char * find_reloc_name_given_its_address(int address)
+{
+ unsigned int i;
+ for(i = 0; i < segment->nsects ; i++)
+ {
+ const char * name = find_reloc_name_in_sec_ptr(address, &section_hdr[i]);
+ if((long)name != -1)
+ return name;
+ }
+ return 0;
+}
+
+static const char * get_reloc_name(EXE_RELOC * rel, int * sslide)
+{
+ char * name = 0;
+ struct scattered_relocation_info * sca_rel = (struct scattered_relocation_info*)rel;
+ int sectnum = rel->r_symbolnum;
+ int sectoffset;
+ int other_half=0;
+
+ /* init the slide value */
+ *sslide = 0;
+
+ if(R_SCATTERED & rel->r_address)
+ return (char *)find_reloc_name_given_its_address(sca_rel->r_value);
+
+ if(rel->r_extern)
+ {
+ /* ignore debug sym */
+ if ( symtab[rel->r_symbolnum].n_type & N_STAB )
+ return 0;
+ return get_sym_name(&symtab[rel->r_symbolnum]);
+ }
+
+ /* Intruction contains an offset to the symbols pointed to, in the rel->r_symbolnum section */
+ sectoffset = *(uint32_t *)(text + rel->r_address) & 0xffff;
+
+ if(sectnum==0xffffff)
+ return 0;
+
+ /* Sanity Check */
+ if(sectnum > segment->nsects)
+ error("sectnum > segment->nsects");
+
+ switch(rel->r_type)
+ {
+ case PPC_RELOC_LO16: fetch_next_pair_value(rel+1, &other_half); sectoffset |= (other_half << 16);
+ break;
+ case PPC_RELOC_HI16: fetch_next_pair_value(rel+1, &other_half); sectoffset = (sectoffset << 16) | (uint16_t)(other_half & 0xffff);
+ break;
+ case PPC_RELOC_HA16: fetch_next_pair_value(rel+1, &other_half); sectoffset = (sectoffset << 16) + (int16_t)(other_half & 0xffff);
+ break;
+ case PPC_RELOC_BR24:
+ sectoffset = ( *(uint32_t *)(text + rel->r_address) & 0x03fffffc );
+ if (sectoffset & 0x02000000) sectoffset |= 0xfc000000;
+ break;
+ default:
+ error("switch(rel->type) not found");
+ }
+
+ if(rel->r_pcrel)
+ sectoffset += rel->r_address;
+
+ if (rel->r_type == PPC_RELOC_BR24)
+ name = (char *)find_reloc_name_in_sec_ptr((int)sectoffset, &section_hdr[sectnum-1]);
+
+ /* search it in the full symbol list, if not found */
+ if(!name)
+ name = (char *)find_sym_with_value_and_sec_number(sectoffset, sectnum, sslide);
+
+ return name;
+}
+
+/* Used by dyngen common code */
+static const char * get_rel_sym_name(EXE_RELOC * rel)
+{
+ int sslide;
+ return get_reloc_name( rel, &sslide);
+}
+
+/* Used by dyngen common code */
+static host_ulong get_rel_offset(EXE_RELOC *rel)
+{
+ struct scattered_relocation_info * sca_rel = (struct scattered_relocation_info*)rel;
+ if(R_SCATTERED & rel->r_address)
+ return sca_rel->r_address;
+ else
+ return rel->r_address;
+}
+
+/* load a mach-o object file */
+int load_object(const char *filename)
+{
+ int fd;
+ unsigned int offset_to_segment = 0;
+ unsigned int offset_to_dysymtab = 0;
+ unsigned int offset_to_symtab = 0;
+ struct load_command lc;
+ unsigned int i, j;
+ EXE_SYM *sym;
+ struct nlist *syment;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ error("can't open file '%s'", filename);
+
+ /* Read Mach header. */
+ if (read(fd, &mach_hdr, sizeof (mach_hdr)) != sizeof (mach_hdr))
+ error("unable to read file header");
+
+ /* Check Mach identification. */
+ if (!check_mach_header(mach_hdr)) {
+ error("bad Mach header");
+ }
+
+ if (mach_hdr.cputype != CPU_TYPE_POWERPC)
+ error("Unsupported CPU");
+
+ if (mach_hdr.filetype != MH_OBJECT)
+ error("Unsupported Mach Object");
+
+ /* read segment headers */
+ for(i=0, j=sizeof(mach_hdr); i<mach_hdr.ncmds ; i++)
+ {
+ if(read(fd, &lc, sizeof(struct load_command)) != sizeof(struct load_command))
+ error("unable to read load_command");
+ if(lc.cmd == LC_SEGMENT)
+ {
+ offset_to_segment = j;
+ lseek(fd, offset_to_segment, SEEK_SET);
+ segment = malloc(sizeof(struct segment_command));
+ if(read(fd, segment, sizeof(struct segment_command)) != sizeof(struct segment_command))
+ error("unable to read LC_SEGMENT");
+ }
+ if(lc.cmd == LC_DYSYMTAB)
+ {
+ offset_to_dysymtab = j;
+ lseek(fd, offset_to_dysymtab, SEEK_SET);
+ dysymtabcmd = malloc(sizeof(struct dysymtab_command));
+ if(read(fd, dysymtabcmd, sizeof(struct dysymtab_command)) != sizeof(struct dysymtab_command))
+ error("unable to read LC_DYSYMTAB");
+ }
+ if(lc.cmd == LC_SYMTAB)
+ {
+ offset_to_symtab = j;
+ lseek(fd, offset_to_symtab, SEEK_SET);
+ symtabcmd = malloc(sizeof(struct symtab_command));
+ if(read(fd, symtabcmd, sizeof(struct symtab_command)) != sizeof(struct symtab_command))
+ error("unable to read LC_SYMTAB");
+ }
+ j+=lc.cmdsize;
+
+ lseek(fd, j, SEEK_SET);
+ }
+
+ if(!segment)
+ error("unable to find LC_SEGMENT");
+
+ /* read section headers */
+ section_hdr = load_data(fd, offset_to_segment + sizeof(struct segment_command), segment->nsects * sizeof(struct section));
+
+ /* read all section data */
+ sdata = (uint8_t **)malloc(sizeof(void *) * segment->nsects);
+ memset(sdata, 0, sizeof(void *) * segment->nsects);
+
+ /* Load the data in section data */
+ for(i = 0; i < segment->nsects; i++) {
+ sdata[i] = load_data(fd, section_hdr[i].offset, section_hdr[i].size);
+ }
+
+ /* text section */
+ text_sec_hdr = find_mach_sec_hdr(section_hdr, segment->nsects, SEG_TEXT, SECT_TEXT);
+ i = find_mach_sec_index(section_hdr, segment->nsects, SEG_TEXT, SECT_TEXT);
+ if (i == -1 || !text_sec_hdr)
+ error("could not find __TEXT,__text section");
+ text = sdata[i];
+
+ /* Make sure dysym was loaded */
+ if(!(int)dysymtabcmd)
+ error("could not find __DYSYMTAB segment");
+
+ /* read the table of content of the indirect sym */
+ tocdylib = load_data( fd, dysymtabcmd->indirectsymoff, dysymtabcmd->nindirectsyms * sizeof(uint32_t) );
+
+ /* Make sure symtab was loaded */
+ if(!(int)symtabcmd)
+ error("could not find __SYMTAB segment");
+ nb_syms = symtabcmd->nsyms;
+
+ symtab_std = load_data(fd, symtabcmd->symoff, symtabcmd->nsyms * sizeof(struct nlist));
+ strtab = load_data(fd, symtabcmd->stroff, symtabcmd->strsize);
+
+ symtab = malloc(sizeof(EXE_SYM) * nb_syms);
+
+ /* Now transform the symtab, to an extended version, with the sym size, and the C name */
+ for(i = 0, sym = symtab, syment = symtab_std; i < nb_syms; i++, sym++, syment++) {
+ struct nlist *sym_follow, *sym_next = 0;
+ unsigned int j;
+ memset(sym, 0, sizeof(*sym));
+
+ if ( syment->n_type & N_STAB ) /* Debug symbols are skipped */
+ continue;
+
+ memcpy(sym, syment, sizeof(*syment));
+
+ /* Find the following symbol in order to get the current symbol size */
+ for(j = 0, sym_follow = symtab_std; j < nb_syms; j++, sym_follow++) {
+ if ( sym_follow->n_sect != 1 || sym_follow->n_type & N_STAB || !(sym_follow->n_value > sym->st_value))
+ continue;
+ if(!sym_next) {
+ sym_next = sym_follow;
+ continue;
+ }
+ if(!(sym_next->n_value > sym_follow->n_value))
+ continue;
+ sym_next = sym_follow;
+ }
+ if(sym_next)
+ sym->st_size = sym_next->n_value - sym->st_value;
+ else
+ sym->st_size = text_sec_hdr->size - sym->st_value;
+ }
+
+ /* Find Reloc */
+ relocs = load_data(fd, text_sec_hdr->reloff, text_sec_hdr->nreloc * sizeof(struct relocation_info));
+ nb_relocs = text_sec_hdr->nreloc;
+
+ close(fd);
+ return 0;
+}
+
+#endif /* CONFIG_FORMAT_MACH */
+
+void get_reloc_expr(char *name, int name_size, const char *sym_name)
+{
+ const char *p;
+
+ if (strstart(sym_name, "__op_param", &p)) {
+ snprintf(name, name_size, "param%s", p);
+ } else if (strstart(sym_name, "__op_gen_label", &p)) {
+ snprintf(name, name_size, "gen_labels[param%s]", p);
+ } else {
+#ifdef HOST_SPARC
+ if (sym_name[0] == '.')
+ snprintf(name, name_size,
+ "(long)(&__dot_%s)",
+ sym_name + 1);
+ else
+#endif
+ snprintf(name, name_size, "(long)(&%s)", sym_name);
+ }
+}
+
+#ifdef HOST_IA64
+
+#define PLT_ENTRY_SIZE 16 /* 1 bundle containing "brl" */
+
+struct plt_entry {
+ struct plt_entry *next;
+ const char *name;
+ unsigned long addend;
+} *plt_list;
+
+static int
+get_plt_index (const char *name, unsigned long addend)
+{
+ struct plt_entry *plt, *prev= NULL;
+ int index = 0;
+
+ /* see if we already have an entry for this target: */
+ for (plt = plt_list; plt; ++index, prev = plt, plt = plt->next)
+ if (strcmp(plt->name, name) == 0 && plt->addend == addend)
+ return index;
+
+ /* nope; create a new PLT entry: */
+
+ plt = malloc(sizeof(*plt));
+ if (!plt) {
+ perror("malloc");
+ exit(1);
+ }
+ memset(plt, 0, sizeof(*plt));
+ plt->name = strdup(name);
+ plt->addend = addend;
+
+ /* append to plt-list: */
+ if (prev)
+ prev->next = plt;
+ else
+ plt_list = plt;
+ return index;
+}
+
+#endif
+
+#ifdef HOST_ARM
+
+int arm_emit_ldr_info(const char *name, unsigned long start_offset,
+ FILE *outfile, uint8_t *p_start, uint8_t *p_end,
+ ELF_RELOC *relocs, int nb_relocs)
+{
+ uint8_t *p;
+ uint32_t insn;
+ int offset, min_offset, pc_offset, data_size;
+ uint8_t data_allocated[1024];
+ unsigned int data_index;
+
+ memset(data_allocated, 0, sizeof(data_allocated));
+
+ p = p_start;
+ min_offset = p_end - p_start;
+ while (p < p_start + min_offset) {
+ insn = get32((uint32_t *)p);
+ if ((insn & 0x0d5f0000) == 0x051f0000) {
+ /* ldr reg, [pc, #im] */
+ offset = insn & 0xfff;
+ if (!(insn & 0x00800000))
+ offset = -offset;
+ if ((offset & 3) !=0)
+ error("%s:%04x: ldr pc offset must be 32 bit aligned",
+ name, start_offset + p - p_start);
+ pc_offset = p - p_start + offset + 8;
+ if (pc_offset <= (p - p_start) ||
+ pc_offset >= (p_end - p_start))
+ error("%s:%04x: ldr pc offset must point inside the function code",
+ name, start_offset + p - p_start);
+ if (pc_offset < min_offset)
+ min_offset = pc_offset;
+ if (outfile) {
+ /* ldr position */
+ fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n",
+ p - p_start);
+ /* ldr data index */
+ data_index = ((p_end - p_start) - pc_offset - 4) >> 2;
+ fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n",
+ data_index);
+ fprintf(outfile, " arm_ldr_ptr++;\n");
+ if (data_index >= sizeof(data_allocated))
+ error("%s: too many data", name);
+ if (!data_allocated[data_index]) {
+ ELF_RELOC *rel;
+ int i, addend, type;
+ const char *sym_name, *p;
+ char relname[1024];
+
+ data_allocated[data_index] = 1;
+
+ /* data value */
+ addend = get32((uint32_t *)(p_start + pc_offset));
+ relname[0] = '\0';
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset == (pc_offset + start_offset)) {
+ sym_name = get_rel_sym_name(rel);
+ /* the compiler leave some unnecessary references to the code */
+ get_reloc_expr(relname, sizeof(relname), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ if (type != R_ARM_ABS32)
+ error("%s: unsupported data relocation", name);
+ break;
+ }
+ }
+ fprintf(outfile, " arm_data_ptr[%d] = 0x%x",
+ data_index, addend);
+ if (relname[0] != '\0')
+ fprintf(outfile, " + %s", relname);
+ fprintf(outfile, ";\n");
+ }
+ }
+ }
+ p += 4;
+ }
+ data_size = (p_end - p_start) - min_offset;
+ if (data_size > 0 && outfile) {
+ fprintf(outfile, " arm_data_ptr += %d;\n", data_size >> 2);
+ }
+
+ /* the last instruction must be a mov pc, lr */
+ if (p == p_start)
+ goto arm_ret_error;
+ p -= 4;
+ insn = get32((uint32_t *)p);
+ if ((insn & 0xffff0000) != 0xe91b0000) {
+ arm_ret_error:
+ if (!outfile)
+ printf("%s: invalid epilog\n", name);
+ }
+ return p - p_start;
+}
+#endif
+
+
+#define MAX_ARGS 3
+
+/* generate op code */
+void gen_code(const char *name, host_ulong offset, host_ulong size,
+ FILE *outfile, int gen_switch)
+{
+ int copy_size = 0;
+ uint8_t *p_start, *p_end;
+ host_ulong start_offset;
+ int nb_args, i, n;
+ uint8_t args_present[MAX_ARGS];
+ const char *sym_name, *p;
+ EXE_RELOC *rel;
+
+ /* Compute exact size excluding prologue and epilogue instructions.
+ * Increment start_offset to skip epilogue instructions, then compute
+ * copy_size the indicate the size of the remaining instructions (in
+ * bytes).
+ */
+ p_start = text + offset;
+ p_end = p_start + size;
+ start_offset = offset;
+#if defined(HOST_I386) || defined(HOST_X86_64)
+#ifdef CONFIG_FORMAT_COFF
+ {
+ uint8_t *p;
+ p = p_end - 1;
+ if (p == p_start)
+ error("empty code for %s", name);
+ while (*p != 0xc3) {
+ p--;
+ if (p <= p_start)
+ error("ret or jmp expected at the end of %s", name);
+ }
+ copy_size = p - p_start;
+ }
+#else
+ {
+ int len;
+ len = p_end - p_start;
+ if (len == 0)
+ error("empty code for %s", name);
+ if (p_end[-1] == 0xc3) {
+ len--;
+ } else {
+ error("ret or jmp expected at the end of %s", name);
+ }
+ copy_size = len;
+ }
+#endif
+#elif defined(HOST_PPC)
+ {
+ uint8_t *p;
+ p = (void *)(p_end - 4);
+ if (p == p_start)
+ error("empty code for %s", name);
+ if (get32((uint32_t *)p) != 0x4e800020)
+ error("blr expected at the end of %s", name);
+ copy_size = p - p_start;
+ }
+#elif defined(HOST_S390)
+ {
+ uint8_t *p;
+ p = (void *)(p_end - 2);
+ if (p == p_start)
+ error("empty code for %s", name);
+ if (get16((uint16_t *)p) != 0x07fe && get16((uint16_t *)p) != 0x07f4)
+ error("br %%r14 expected at the end of %s", name);
+ copy_size = p - p_start;
+ }
+#elif defined(HOST_ALPHA)
+ {
+ uint8_t *p;
+ p = p_end - 4;
+#if 0
+ /* XXX: check why it occurs */
+ if (p == p_start)
+ error("empty code for %s", name);
+#endif
+ if (get32((uint32_t *)p) != 0x6bfa8001)
+ error("ret expected at the end of %s", name);
+ copy_size = p - p_start;
+ }
+#elif defined(HOST_IA64)
+ {
+ uint8_t *p;
+ p = (void *)(p_end - 4);
+ if (p == p_start)
+ error("empty code for %s", name);
+ /* br.ret.sptk.many b0;; */
+ /* 08 00 84 00 */
+ if (get32((uint32_t *)p) != 0x00840008)
+ error("br.ret.sptk.many b0;; expected at the end of %s", name);
+ copy_size = p_end - p_start;
+ }
+#elif defined(HOST_SPARC)
+ {
+#define INSN_SAVE 0x9de3a000
+#define INSN_RET 0x81c7e008
+#define INSN_RETL 0x81c3e008
+#define INSN_RESTORE 0x81e80000
+#define INSN_RETURN 0x81cfe008
+#define INSN_NOP 0x01000000
+#define INSN_ADD_SP 0x9c03a000 // add %sp, nn, %sp
+#define INSN_SUB_SP 0x9c23a000 // sub %sp, nn, %sp
+
+ uint32_t start_insn, end_insn1, end_insn2;
+ uint8_t *p;
+ p = (void *)(p_end - 8);
+ if (p <= p_start)
+ error("empty code for %s", name);
+ start_insn = get32((uint32_t *)(p_start + 0x0));
+ end_insn1 = get32((uint32_t *)(p + 0x0));
+ end_insn2 = get32((uint32_t *)(p + 0x4));
+ if (((start_insn & ~0x1fff) == INSN_SAVE) ||
+ (start_insn & ~0x1fff) == INSN_ADD_SP) {
+ p_start += 0x4;
+ start_offset += 0x4;
+ if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
+ /* SPARC v7: ret; restore; */ ;
+ else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
+ /* SPARC v9: return; nop; */ ;
+ else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
+ /* SPARC v7: retl; sub %sp, nn, %sp; */ ;
+ else
+
+ error("ret; restore; not found at end of %s", name);
+ } else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
+ ;
+ } else {
+ error("No save at the beginning of %s", name);
+ }
+#if 0
+ /* Skip a preceeding nop, if present. */
+ if (p > p_start) {
+ skip_insn = get32((uint32_t *)(p - 0x4));
+ if (skip_insn == INSN_NOP)
+ p -= 4;
+ }
+#endif
+ copy_size = p - p_start;
+ }
+#elif defined(HOST_SPARC64)
+ {
+#define INSN_SAVE 0x9de3a000
+#define INSN_RET 0x81c7e008
+#define INSN_RETL 0x81c3e008
+#define INSN_RESTORE 0x81e80000
+#define INSN_RETURN 0x81cfe008
+#define INSN_NOP 0x01000000
+#define INSN_ADD_SP 0x9c03a000 // add %sp, nn, %sp
+#define INSN_SUB_SP 0x9c23a000 // sub %sp, nn, %sp
+
+ uint32_t start_insn, end_insn1, end_insn2, skip_insn;
+ uint8_t *p;
+ p = (void *)(p_end - 8);
+#if 0
+ /* XXX: check why it occurs */
+ if (p <= p_start)
+ error("empty code for %s", name);
+#endif
+ start_insn = get32((uint32_t *)(p_start + 0x0));
+ end_insn1 = get32((uint32_t *)(p + 0x0));
+ end_insn2 = get32((uint32_t *)(p + 0x4));
+ if (((start_insn & ~0x1fff) == INSN_SAVE) ||
+ (start_insn & ~0x1fff) == INSN_ADD_SP) {
+ p_start += 0x4;
+ start_offset += 0x4;
+ if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
+ /* SPARC v7: ret; restore; */ ;
+ else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
+ /* SPARC v9: return; nop; */ ;
+ else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
+ /* SPARC v7: retl; sub %sp, nn, %sp; */ ;
+ else
+
+ error("ret; restore; not found at end of %s", name);
+ } else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
+ ;
+ } else {
+ error("No save at the beginning of %s", name);
+ }
+
+ /* Skip a preceeding nop, if present. */
+ if (p > p_start) {
+ skip_insn = get32((uint32_t *)(p - 0x4));
+ if (skip_insn == 0x01000000)
+ p -= 4;
+ }
+
+ copy_size = p - p_start;
+ }
+#elif defined(HOST_ARM)
+ {
+ if ((p_end - p_start) <= 16)
+ error("%s: function too small", name);
+ if (get32((uint32_t *)p_start) != 0xe1a0c00d ||
+ (get32((uint32_t *)(p_start + 4)) & 0xffff0000) != 0xe92d0000 ||
+ get32((uint32_t *)(p_start + 8)) != 0xe24cb004)
+ error("%s: invalid prolog", name);
+ p_start += 12;
+ start_offset += 12;
+ copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end,
+ relocs, nb_relocs);
+ }
+#elif defined(HOST_M68K)
+ {
+ uint8_t *p;
+ p = (void *)(p_end - 2);
+ if (p == p_start)
+ error("empty code for %s", name);
+ // remove NOP's, probably added for alignment
+ while ((get16((uint16_t *)p) == 0x4e71) &&
+ (p>p_start))
+ p -= 2;
+ if (get16((uint16_t *)p) != 0x4e75)
+ error("rts expected at the end of %s", name);
+ copy_size = p - p_start;
+ }
+#else
+#error unsupported CPU
+#endif
+
+ /* compute the number of arguments by looking at the relocations */
+ for(i = 0;i < MAX_ARGS; i++)
+ args_present[i] = 0;
+
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ host_ulong offset = get_rel_offset(rel);
+ if (offset >= start_offset &&
+ offset < start_offset + (p_end - p_start)) {
+ sym_name = get_rel_sym_name(rel);
+ if(!sym_name)
+ continue;
+ if (strstart(sym_name, "__op_param", &p) ||
+ strstart(sym_name, "__op_gen_label", &p)) {
+ n = strtoul(p, NULL, 10);
+ if (n > MAX_ARGS)
+ error("too many arguments in %s", name);
+ args_present[n - 1] = 1;
+ }
+ }
+ }
+
+ nb_args = 0;
+ while (nb_args < MAX_ARGS && args_present[nb_args])
+ nb_args++;
+ for(i = nb_args; i < MAX_ARGS; i++) {
+ if (args_present[i])
+ error("inconsistent argument numbering in %s", name);
+ }
+
+ if (gen_switch == 2) {
+ fprintf(outfile, "DEF(%s, %d, %d)\n", name + 3, nb_args, copy_size);
+ } else if (gen_switch == 1) {
+
+ /* output C code */
+ fprintf(outfile, "case INDEX_%s: {\n", name);
+ if (nb_args > 0) {
+ fprintf(outfile, " long ");
+ for(i = 0; i < nb_args; i++) {
+ if (i != 0)
+ fprintf(outfile, ", ");
+ fprintf(outfile, "param%d", i + 1);
+ }
+ fprintf(outfile, ";\n");
+ }
+#if defined(HOST_IA64)
+ fprintf(outfile, " extern char %s;\n", name);
+#else
+ fprintf(outfile, " extern void %s();\n", name);
+#endif
+
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ host_ulong offset = get_rel_offset(rel);
+ if (offset >= start_offset &&
+ offset < start_offset + (p_end - p_start)) {
+ sym_name = get_rel_sym_name(rel);
+ if(!sym_name)
+ continue;
+ if (*sym_name &&
+ !strstart(sym_name, "__op_param", NULL) &&
+ !strstart(sym_name, "__op_jmp", NULL) &&
+ !strstart(sym_name, "__op_gen_label", NULL)) {
+#if defined(HOST_SPARC)
+ if (sym_name[0] == '.') {
+ fprintf(outfile,
+ "extern char __dot_%s __asm__(\"%s\");\n",
+ sym_name+1, sym_name);
+ continue;
+ }
+#endif
+#if defined(__APPLE__)
+/* set __attribute((unused)) on darwin because we wan't to avoid warning when we don't use the symbol */
+ fprintf(outfile, "extern char %s __attribute__((unused));\n", sym_name);
+#elif defined(HOST_IA64)
+ if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+ /*
+ * PCREL21 br.call targets generally
+ * are out of range and need to go
+ * through an "import stub".
+ */
+ fprintf(outfile, " extern char %s;\n",
+ sym_name);
+#else
+ fprintf(outfile, "extern char %s;\n", sym_name);
+#endif
+ }
+ }
+ }
+
+ fprintf(outfile, " memcpy(gen_code_ptr, (void *)((char *)&%s+%d), %d);\n",
+ name, (int)(start_offset - offset), copy_size);
+
+ /* emit code offset information */
+ {
+ EXE_SYM *sym;
+ const char *sym_name, *p;
+ unsigned long val;
+ int n;
+
+ for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ sym_name = get_sym_name(sym);
+ if (strstart(sym_name, "__op_label", &p)) {
+ uint8_t *ptr;
+ unsigned long offset;
+
+ /* test if the variable refers to a label inside
+ the code we are generating */
+#ifdef CONFIG_FORMAT_COFF
+ if (sym->st_shndx == text_shndx) {
+ ptr = sdata[coff_text_shndx];
+ } else if (sym->st_shndx == data_shndx) {
+ ptr = sdata[coff_data_shndx];
+ } else {
+ ptr = NULL;
+ }
+#elif defined(CONFIG_FORMAT_MACH)
+ if(!sym->n_sect)
+ continue;
+ ptr = sdata[sym->n_sect-1];
+#else
+ ptr = sdata[sym->st_shndx];
+#endif
+ if (!ptr)
+ error("__op_labelN in invalid section");
+ offset = sym->st_value;
+#ifdef CONFIG_FORMAT_MACH
+ offset -= section_hdr[sym->n_sect-1].addr;
+#endif
+ val = *(unsigned long *)(ptr + offset);
+#ifdef ELF_USES_RELOCA
+ {
+ int reloc_shndx, nb_relocs1, j;
+
+ /* try to find a matching relocation */
+ reloc_shndx = find_reloc(sym->st_shndx);
+ if (reloc_shndx) {
+ nb_relocs1 = shdr[reloc_shndx].sh_size /
+ shdr[reloc_shndx].sh_entsize;
+ rel = (ELF_RELOC *)sdata[reloc_shndx];
+ for(j = 0; j < nb_relocs1; j++) {
+ if (rel->r_offset == offset) {
+ val = rel->r_addend;
+ break;
+ }
+ rel++;
+ }
+ }
+ }
+#endif
+ if (val >= start_offset && val <= start_offset + copy_size) {
+ n = strtol(p, NULL, 10);
+ fprintf(outfile, " label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, (long)(val - start_offset));
+ }
+ }
+ }
+ }
+
+ /* load parameres in variables */
+ for(i = 0; i < nb_args; i++) {
+ fprintf(outfile, " param%d = *opparam_ptr++;\n", i + 1);
+ }
+
+ /* patch relocations */
+#if defined(HOST_I386)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = get_rel_sym_name(rel);
+ if (!sym_name)
+ continue;
+ reloc_offset = rel->r_offset - start_offset;
+ if (strstart(sym_name, "__op_jmp", &p)) {
+ int n;
+ n = strtol(p, NULL, 10);
+ /* __op_jmp relocations are done at
+ runtime to do translated block
+ chaining: the offset of the instruction
+ needs to be stored */
+ fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
+ n, reloc_offset);
+ continue;
+ }
+
+ get_reloc_expr(name, sizeof(name), sym_name);
+ addend = get32((uint32_t *)(text + rel->r_offset));
+#ifdef CONFIG_FORMAT_ELF
+ type = ELF32_R_TYPE(rel->r_info);
+ switch(type) {
+ case R_386_32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_386_PC32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n",
+ reloc_offset, name, reloc_offset, addend);
+ break;
+ default:
+ error("unsupported i386 relocation (%d)", type);
+ }
+#elif defined(CONFIG_FORMAT_COFF)
+ {
+ char *temp_name;
+ int j;
+ EXE_SYM *sym;
+ temp_name = get_sym_name(symtab + *(uint32_t *)(rel->r_reloc->r_symndx));
+ if (!strcmp(temp_name, ".data")) {
+ for (j = 0, sym = symtab; j < nb_syms; j++, sym++) {
+ if (strstart(sym->st_name, sym_name, NULL)) {
+ addend -= sym->st_value;
+ }
+ }
+ }
+ }
+ type = rel->r_type;
+ switch(type) {
+ case DIR32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case DISP32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d -4;\n",
+ reloc_offset, name, reloc_offset, addend);
+ break;
+ default:
+ error("unsupported i386 relocation (%d)", type);
+ }
+#else
+#error unsupport object format
+#endif
+ }
+ }
+ }
+#elif defined(HOST_X86_64)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_X86_64_32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (uint32_t)%s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_X86_64_32S:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (int32_t)%s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_X86_64_PC32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n",
+ reloc_offset, name, reloc_offset, addend);
+ break;
+ default:
+ error("unsupported X86_64 relocation (%d)", type);
+ }
+ }
+ }
+ }
+#elif defined(HOST_PPC)
+ {
+#ifdef CONFIG_FORMAT_ELF
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+ reloc_offset = rel->r_offset - start_offset;
+ if (strstart(sym_name, "__op_jmp", &p)) {
+ int n;
+ n = strtol(p, NULL, 10);
+ /* __op_jmp relocations are done at
+ runtime to do translated block
+ chaining: the offset of the instruction
+ needs to be stored */
+ fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
+ n, reloc_offset);
+ continue;
+ }
+
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ switch(type) {
+ case R_PPC_ADDR32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_PPC_ADDR16_LO:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d);\n",
+ reloc_offset, name, addend);
+ break;
+ case R_PPC_ADDR16_HI:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d) >> 16;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_PPC_ADDR16_HA:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d + 0x8000) >> 16;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_PPC_REL24:
+ /* warning: must be at 32 MB distancy */
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((%s - (long)(gen_code_ptr + %d) + %d) & 0x03fffffc);\n",
+ reloc_offset, reloc_offset, name, reloc_offset, addend);
+ break;
+ default:
+ error("unsupported powerpc relocation (%d)", type);
+ }
+ }
+ }
+#elif defined(CONFIG_FORMAT_MACH)
+ struct scattered_relocation_info *scarel;
+ struct relocation_info * rel;
+ char final_sym_name[256];
+ const char *sym_name;
+ const char *p;
+ int slide, sslide;
+ int i;
+
+ for(i = 0, rel = relocs; i < nb_relocs; i++, rel++) {
+ unsigned int offset, length, value = 0;
+ unsigned int type, pcrel, isym = 0;
+ unsigned int usesym = 0;
+
+ if(R_SCATTERED & rel->r_address) {
+ scarel = (struct scattered_relocation_info*)rel;
+ offset = (unsigned int)scarel->r_address;
+ length = scarel->r_length;
+ pcrel = scarel->r_pcrel;
+ type = scarel->r_type;
+ value = scarel->r_value;
+ } else {
+ value = isym = rel->r_symbolnum;
+ usesym = (rel->r_extern);
+ offset = rel->r_address;
+ length = rel->r_length;
+ pcrel = rel->r_pcrel;
+ type = rel->r_type;
+ }
+
+ slide = offset - start_offset;
+
+ if (!(offset >= start_offset && offset < start_offset + size))
+ continue; /* not in our range */
+
+ sym_name = get_reloc_name(rel, &sslide);
+
+ if(usesym && symtab[isym].n_type & N_STAB)
+ continue; /* don't handle STAB (debug sym) */
+
+ if (sym_name && strstart(sym_name, "__op_jmp", &p)) {
+ int n;
+ n = strtol(p, NULL, 10);
+ fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
+ n, slide);
+ continue; /* Nothing more to do */
+ }
+
+ if(!sym_name)
+ {
+ fprintf(outfile, "/* #warning relocation not handled in %s (value 0x%x, %s, offset 0x%x, length 0x%x, %s, type 0x%x) */\n",
+ name, value, usesym ? "use sym" : "don't use sym", offset, length, pcrel ? "pcrel":"", type);
+ continue; /* dunno how to handle without final_sym_name */
+ }
+
+ get_reloc_expr(final_sym_name, sizeof(final_sym_name),
+ sym_name);
+ switch(type) {
+ case PPC_RELOC_BR24:
+ if (!strstart(sym_name,"__op_gen_label",&p)) {
+ fprintf(outfile, "{\n");
+ fprintf(outfile, " uint32_t imm = *(uint32_t *)(gen_code_ptr + %d) & 0x3fffffc;\n", slide);
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((imm + ((long)%s - (long)gen_code_ptr) + %d) & 0x03fffffc);\n",
+ slide, slide, name, sslide );
+ fprintf(outfile, "}\n");
+ } else {
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | (((long)%s - (long)gen_code_ptr - %d) & 0x03fffffc);\n",
+ slide, slide, final_sym_name, slide);
+ }
+ break;
+ case PPC_RELOC_HI16:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d) >> 16;\n",
+ slide, final_sym_name, sslide);
+ break;
+ case PPC_RELOC_LO16:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d);\n",
+ slide, final_sym_name, sslide);
+ break;
+ case PPC_RELOC_HA16:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d + 0x8000) >> 16;\n",
+ slide, final_sym_name, sslide);
+ break;
+ default:
+ error("unsupported powerpc relocation (%d)", type);
+ }
+ }
+#else
+#error unsupport object format
+#endif
+ }
+#elif defined(HOST_S390)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_390_32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_390_16:
+ fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_390_8:
+ fprintf(outfile, " *(uint8_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ default:
+ error("unsupported s390 relocation (%d)", type);
+ }
+ }
+ }
+ }
+#elif defined(HOST_ALPHA)
+ {
+ for (i = 0, rel = relocs; i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset && rel->r_offset < start_offset + copy_size) {
+ int type;
+ long reloc_offset;
+
+ type = ELF64_R_TYPE(rel->r_info);
+ sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
+ reloc_offset = rel->r_offset - start_offset;
+ switch (type) {
+ case R_ALPHA_GPDISP:
+ /* The gp is just 32 bit, and never changes, so it's easiest to emit it
+ as an immediate instead of constructing it from the pv or ra. */
+ fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, gp);\n",
+ reloc_offset);
+ fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, gp);\n",
+ reloc_offset + (int)rel->r_addend);
+ break;
+ case R_ALPHA_LITUSE:
+ /* jsr to literal hint. Could be used to optimize to bsr. Ignore for
+ now, since some called functions (libc) need pv to be set up. */
+ break;
+ case R_ALPHA_HINT:
+ /* Branch target prediction hint. Ignore for now. Should be already
+ correct for in-function jumps. */
+ break;
+ case R_ALPHA_LITERAL:
+ /* Load a literal from the GOT relative to the gp. Since there's only a
+ single gp, nothing is to be done. */
+ break;
+ case R_ALPHA_GPRELHIGH:
+ /* Handle fake relocations against __op_param symbol. Need to emit the
+ high part of the immediate value instead. Other symbols need no
+ special treatment. */
+ if (strstart(sym_name, "__op_param", &p))
+ fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, param%s);\n",
+ reloc_offset, p);
+ break;
+ case R_ALPHA_GPRELLOW:
+ if (strstart(sym_name, "__op_param", &p))
+ fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, param%s);\n",
+ reloc_offset, p);
+ break;
+ case R_ALPHA_BRSGP:
+ /* PC-relative jump. Tweak offset to skip the two instructions that try to
+ set up the gp from the pv. */
+ fprintf(outfile, " fix_bsr(gen_code_ptr + %ld, (uint8_t *) &%s - (gen_code_ptr + %ld + 4) + 8);\n",
+ reloc_offset, sym_name, reloc_offset);
+ break;
+ default:
+ error("unsupported Alpha relocation (%d)", type);
+ }
+ }
+ }
+ }
+#elif defined(HOST_IA64)
+ {
+ unsigned long sym_idx;
+ long code_offset;
+ char name[256];
+ int type;
+ long addend;
+
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ sym_idx = ELF64_R_SYM(rel->r_info);
+ if (rel->r_offset < start_offset
+ || rel->r_offset >= start_offset + copy_size)
+ continue;
+ sym_name = (strtab + symtab[sym_idx].st_name);
+ code_offset = rel->r_offset - start_offset;
+ if (strstart(sym_name, "__op_jmp", &p)) {
+ int n;
+ n = strtol(p, NULL, 10);
+ /* __op_jmp relocations are done at
+ runtime to do translated block
+ chaining: the offset of the instruction
+ needs to be stored */
+ fprintf(outfile, " jmp_offsets[%d] ="
+ "%ld + (gen_code_ptr - gen_code_buf);\n",
+ n, code_offset);
+ continue;
+ }
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF64_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ switch(type) {
+ case R_IA64_IMM64:
+ fprintf(outfile,
+ " ia64_imm64(gen_code_ptr + %ld, "
+ "%s + %ld);\n",
+ code_offset, name, addend);
+ break;
+ case R_IA64_LTOFF22X:
+ case R_IA64_LTOFF22:
+ fprintf(outfile, " IA64_LTOFF(gen_code_ptr + %ld,"
+ " %s + %ld, %d);\n",
+ code_offset, name, addend,
+ (type == R_IA64_LTOFF22X));
+ break;
+ case R_IA64_LDXMOV:
+ fprintf(outfile,
+ " ia64_ldxmov(gen_code_ptr + %ld,"
+ " %s + %ld);\n", code_offset, name, addend);
+ break;
+
+ case R_IA64_PCREL21B:
+ if (strstart(sym_name, "__op_gen_label", NULL)) {
+ fprintf(outfile,
+ " ia64_imm21b(gen_code_ptr + %ld,"
+ " (long) (%s + %ld -\n\t\t"
+ "((long) gen_code_ptr + %ld)) >> 4);\n",
+ code_offset, name, addend,
+ code_offset & ~0xfUL);
+ } else {
+ fprintf(outfile,
+ " IA64_PLT(gen_code_ptr + %ld, "
+ "%d);\t/* %s + %ld */\n",
+ code_offset,
+ get_plt_index(sym_name, addend),
+ sym_name, addend);
+ }
+ break;
+ default:
+ error("unsupported ia64 relocation (0x%x)",
+ type);
+ }
+ }
+ fprintf(outfile, " ia64_nop_b(gen_code_ptr + %d);\n",
+ copy_size - 16 + 2);
+ }
+#elif defined(HOST_SPARC)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_SPARC_32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_SPARC_HI22:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffff) "
+ " | (((%s + %d) >> 10) & 0x3fffff);\n",
+ reloc_offset, reloc_offset, name, addend);
+ break;
+ case R_SPARC_LO10:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3ff) "
+ " | ((%s + %d) & 0x3ff);\n",
+ reloc_offset, reloc_offset, name, addend);
+ break;
+ case R_SPARC_WDISP30:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffffff) "
+ " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+ " & 0x3fffffff);\n",
+ reloc_offset, reloc_offset, name, addend,
+ reloc_offset);
+ break;
+ case R_SPARC_WDISP22:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffff) "
+ " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+ " & 0x3fffff);\n",
+ rel->r_offset - start_offset,
+ rel->r_offset - start_offset,
+ name, addend,
+ rel->r_offset - start_offset);
+ break;
+ default:
+ error("unsupported sparc relocation (%d)", type);
+ }
+ }
+ }
+ }
+#elif defined(HOST_SPARC64)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = rel->r_addend;
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_SPARC_32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_SPARC_HI22:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffff) "
+ " | (((%s + %d) >> 10) & 0x3fffff);\n",
+ reloc_offset, reloc_offset, name, addend);
+ break;
+ case R_SPARC_LO10:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3ff) "
+ " | ((%s + %d) & 0x3ff);\n",
+ reloc_offset, reloc_offset, name, addend);
+ break;
+ case R_SPARC_OLO10:
+ addend += ELF64_R_TYPE_DATA (rel->r_info);
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3ff) "
+ " | ((%s + %d) & 0x3ff);\n",
+ reloc_offset, reloc_offset, name, addend);
+ break;
+ case R_SPARC_WDISP30:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffffff) "
+ " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+ " & 0x3fffffff);\n",
+ reloc_offset, reloc_offset, name, addend,
+ reloc_offset);
+ break;
+ case R_SPARC_WDISP22:
+ fprintf(outfile,
+ " *(uint32_t *)(gen_code_ptr + %d) = "
+ "((*(uint32_t *)(gen_code_ptr + %d)) "
+ " & ~0x3fffff) "
+ " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+ " & 0x3fffff);\n",
+ reloc_offset, reloc_offset, name, addend,
+ reloc_offset);
+ break;
+ default:
+ error("unsupported sparc64 relocation (%d) for symbol %s", type, name);
+ }
+ }
+ }
+ }
+#elif defined(HOST_ARM)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+
+ arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end,
+ relocs, nb_relocs);
+
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+ /* the compiler leave some unnecessary references to the code */
+ if (sym_name[0] == '\0')
+ continue;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = get32((uint32_t *)(text + rel->r_offset));
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_ARM_ABS32:
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+ reloc_offset, name, addend);
+ break;
+ case R_ARM_PC24:
+ fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n",
+ reloc_offset, addend, name);
+ break;
+ default:
+ error("unsupported arm relocation (%d)", type);
+ }
+ }
+ }
+ }
+#elif defined(HOST_M68K)
+ {
+ char name[256];
+ int type;
+ int addend;
+ int reloc_offset;
+ Elf32_Sym *sym;
+ for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ if (rel->r_offset >= start_offset &&
+ rel->r_offset < start_offset + copy_size) {
+ sym = &(symtab[ELFW(R_SYM)(rel->r_info)]);
+ sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+ get_reloc_expr(name, sizeof(name), sym_name);
+ type = ELF32_R_TYPE(rel->r_info);
+ addend = get32((uint32_t *)(text + rel->r_offset)) + rel->r_addend;
+ reloc_offset = rel->r_offset - start_offset;
+ switch(type) {
+ case R_68K_32:
+ fprintf(outfile, " /* R_68K_32 RELOC, offset %x */\n", rel->r_offset) ;
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %#x;\n",
+ reloc_offset, name, addend );
+ break;
+ case R_68K_PC32:
+ fprintf(outfile, " /* R_68K_PC32 RELOC, offset %x */\n", rel->r_offset);
+ fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %#x) + %#x;\n",
+ reloc_offset, name, reloc_offset, /*sym->st_value+*/ addend);
+ break;
+ default:
+ error("unsupported m68k relocation (%d)", type);
+ }
+ }
+ }
+ }
+#else
+#error unsupported CPU
+#endif
+ fprintf(outfile, " gen_code_ptr += %d;\n", copy_size);
+ fprintf(outfile, "}\n");
+ fprintf(outfile, "break;\n\n");
+ } else {
+ fprintf(outfile, "static inline void gen_%s(", name);
+ if (nb_args == 0) {
+ fprintf(outfile, "void");
+ } else {
+ for(i = 0; i < nb_args; i++) {
+ if (i != 0)
+ fprintf(outfile, ", ");
+ fprintf(outfile, "long param%d", i + 1);
+ }
+ }
+ fprintf(outfile, ")\n");
+ fprintf(outfile, "{\n");
+ for(i = 0; i < nb_args; i++) {
+ fprintf(outfile, " *gen_opparam_ptr++ = param%d;\n", i + 1);
+ }
+ fprintf(outfile, " *gen_opc_ptr++ = INDEX_%s;\n", name);
+ fprintf(outfile, "}\n\n");
+ }
+}
+
+int gen_file(FILE *outfile, int out_type)
+{
+ int i;
+ EXE_SYM *sym;
+
+ if (out_type == OUT_INDEX_OP) {
+ fprintf(outfile, "DEF(end, 0, 0)\n");
+ fprintf(outfile, "DEF(nop, 0, 0)\n");
+ fprintf(outfile, "DEF(nop1, 1, 0)\n");
+ fprintf(outfile, "DEF(nop2, 2, 0)\n");
+ fprintf(outfile, "DEF(nop3, 3, 0)\n");
+ for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ const char *name;
+ name = get_sym_name(sym);
+ if (strstart(name, OP_PREFIX, NULL)) {
+ gen_code(name, sym->st_value, sym->st_size, outfile, 2);
+ }
+ }
+ } else if (out_type == OUT_GEN_OP) {
+ /* generate gen_xxx functions */
+ fprintf(outfile, "#include \"dyngen-op.h\"\n");
+ for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ const char *name;
+ name = get_sym_name(sym);
+ if (strstart(name, OP_PREFIX, NULL)) {
+#if defined(CONFIG_FORMAT_ELF) || defined(CONFIG_FORMAT_COFF)
+ if (sym->st_shndx != text_shndx)
+ error("invalid section for opcode (0x%x)", sym->st_shndx);
+#endif
+ gen_code(name, sym->st_value, sym->st_size, outfile, 0);
+ }
+ }
+
+ } else {
+ /* generate big code generation switch */
+fprintf(outfile,
+"int dyngen_code(uint8_t *gen_code_buf,\n"
+" uint16_t *label_offsets, uint16_t *jmp_offsets,\n"
+" const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels)\n"
+"{\n"
+" uint8_t *gen_code_ptr;\n"
+" const uint16_t *opc_ptr;\n"
+" const uint32_t *opparam_ptr;\n");
+
+#ifdef HOST_ARM
+fprintf(outfile,
+" uint8_t *last_gen_code_ptr = gen_code_buf;\n"
+" LDREntry *arm_ldr_ptr = arm_ldr_table;\n"
+" uint32_t *arm_data_ptr = arm_data_table;\n");
+#endif
+#ifdef HOST_IA64
+ {
+ long addend, not_first = 0;
+ unsigned long sym_idx;
+ int index, max_index;
+ const char *sym_name;
+ EXE_RELOC *rel;
+
+ max_index = -1;
+ for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ sym_idx = ELF64_R_SYM(rel->r_info);
+ sym_name = (strtab + symtab[sym_idx].st_name);
+ if (strstart(sym_name, "__op_gen_label", NULL))
+ continue;
+ if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+ continue;
+
+ addend = rel->r_addend;
+ index = get_plt_index(sym_name, addend);
+ if (index <= max_index)
+ continue;
+ max_index = index;
+ fprintf(outfile, " extern void %s(void);\n", sym_name);
+ }
+
+ fprintf(outfile,
+ " struct ia64_fixup *plt_fixes = NULL, "
+ "*ltoff_fixes = NULL;\n"
+ " static long plt_target[] = {\n\t");
+
+ max_index = -1;
+ for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+ sym_idx = ELF64_R_SYM(rel->r_info);
+ sym_name = (strtab + symtab[sym_idx].st_name);
+ if (strstart(sym_name, "__op_gen_label", NULL))
+ continue;
+ if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+ continue;
+
+ addend = rel->r_addend;
+ index = get_plt_index(sym_name, addend);
+ if (index <= max_index)
+ continue;
+ max_index = index;
+
+ if (not_first)
+ fprintf(outfile, ",\n\t");
+ not_first = 1;
+ if (addend)
+ fprintf(outfile, "(long) &%s + %ld", sym_name, addend);
+ else
+ fprintf(outfile, "(long) &%s", sym_name);
+ }
+ fprintf(outfile, "\n };\n"
+ " unsigned int plt_offset[%u] = { 0 };\n", max_index + 1);
+ }
+#endif
+
+fprintf(outfile,
+"\n"
+" gen_code_ptr = gen_code_buf;\n"
+" opc_ptr = opc_buf;\n"
+" opparam_ptr = opparam_buf;\n");
+
+ /* Generate prologue, if needed. */
+
+fprintf(outfile,
+" for(;;) {\n"
+" switch(*opc_ptr++) {\n"
+);
+
+ for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+ const char *name;
+ name = get_sym_name(sym);
+ if (strstart(name, OP_PREFIX, NULL)) {
+#if 0
+ printf("%4d: %s pos=0x%08x len=%d\n",
+ i, name, sym->st_value, sym->st_size);
+#endif
+#if defined(CONFIG_FORMAT_ELF) || defined(CONFIG_FORMAT_COFF)
+ if (sym->st_shndx != text_shndx)
+ error("invalid section for opcode (0x%x)", sym->st_shndx);
+#endif
+ gen_code(name, sym->st_value, sym->st_size, outfile, 1);
+ }
+ }
+
+fprintf(outfile,
+" case INDEX_op_nop:\n"
+" break;\n"
+" case INDEX_op_nop1:\n"
+" opparam_ptr++;\n"
+" break;\n"
+" case INDEX_op_nop2:\n"
+" opparam_ptr += 2;\n"
+" break;\n"
+" case INDEX_op_nop3:\n"
+" opparam_ptr += 3;\n"
+" break;\n"
+" default:\n"
+" goto the_end;\n"
+" }\n");
+
+#ifdef HOST_ARM
+/* generate constant table if needed */
+fprintf(outfile,
+" if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n"
+" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n"
+" last_gen_code_ptr = gen_code_ptr;\n"
+" arm_ldr_ptr = arm_ldr_table;\n"
+" arm_data_ptr = arm_data_table;\n"
+" }\n");
+#endif
+
+
+fprintf(outfile,
+" }\n"
+" the_end:\n"
+);
+#ifdef HOST_IA64
+ fprintf(outfile,
+ " {\n"
+ " extern char code_gen_buffer[];\n"
+ " ia64_apply_fixes(&gen_code_ptr, ltoff_fixes, "
+ "(uint64_t) code_gen_buffer + 2*(1<<20), plt_fixes,\n\t\t\t"
+ "sizeof(plt_target)/sizeof(plt_target[0]),\n\t\t\t"
+ "plt_target, plt_offset);\n }\n");
+#endif
+
+/* generate some code patching */
+#ifdef HOST_ARM
+fprintf(outfile, "gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 0);\n");
+#endif
+ /* flush instruction cache */
+ fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n");
+
+ fprintf(outfile, "return gen_code_ptr - gen_code_buf;\n");
+ fprintf(outfile, "}\n\n");
+
+ }
+
+ return 0;
+}
+
+void usage(void)
+{
+ printf("dyngen (c) 2003 Fabrice Bellard\n"
+ "usage: dyngen [-o outfile] [-c] objfile\n"
+ "Generate a dynamic code generator from an object file\n"
+ "-c output enum of operations\n"
+ "-g output gen_op_xx() functions\n"
+ );
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int c, out_type;
+ const char *filename, *outfilename;
+ FILE *outfile;
+
+ outfilename = "out.c";
+ out_type = OUT_CODE;
+ for(;;) {
+ c = getopt(argc, argv, "ho:cg");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ usage();
+ break;
+ case 'o':
+ outfilename = optarg;
+ break;
+ case 'c':
+ out_type = OUT_INDEX_OP;
+ break;
+ case 'g':
+ out_type = OUT_GEN_OP;
+ break;
+ }
+ }
+ if (optind >= argc)
+ usage();
+ filename = argv[optind];
+ outfile = fopen(outfilename, "w");
+ if (!outfile)
+ error("could not open '%s'", outfilename);
+
+ load_object(filename);
+ gen_file(outfile, out_type);
+ fclose(outfile);
+ return 0;
+}
diff --git a/dyngen.h b/dyngen.h
new file mode 100644
index 0000000..fe0a936
--- /dev/null
+++ b/dyngen.h
@@ -0,0 +1,435 @@
+/*
+ * dyngen helpers
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+int __op_param1, __op_param2, __op_param3;
+#ifdef __sparc__
+ void __op_gen_label1(){}
+ void __op_gen_label2(){}
+ void __op_gen_label3(){}
+#else
+ int __op_gen_label1, __op_gen_label2, __op_gen_label3;
+#endif
+int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
+
+#ifdef __i386__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
+#endif
+
+#ifdef __x86_64__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
+#endif
+
+#ifdef __s390__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
+#endif
+
+#ifdef __ia64__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ while (start < stop) {
+ asm volatile ("fc %0" :: "r"(start));
+ start += 32;
+ }
+ asm volatile (";;sync.i;;srlz.i;;");
+}
+#endif
+
+#ifdef __powerpc__
+
+#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
+
+static void inline flush_icache_range(unsigned long start, unsigned long stop)
+{
+ unsigned long p;
+
+ start &= ~(MIN_CACHE_LINE_SIZE - 1);
+ stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
+
+ for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+ asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
+ }
+ asm volatile ("sync" : : : "memory");
+ for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+ asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
+ }
+ asm volatile ("sync" : : : "memory");
+ asm volatile ("isync" : : : "memory");
+}
+#endif
+
+#ifdef __alpha__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ asm ("imb");
+}
+#endif
+
+#ifdef __sparc__
+
+static void inline flush_icache_range(unsigned long start, unsigned long stop)
+{
+ unsigned long p;
+
+ p = start & ~(8UL - 1UL);
+ stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
+
+ for (; p < stop; p += 8)
+ __asm__ __volatile__("flush\t%0" : : "r" (p));
+}
+
+#endif
+
+#ifdef __arm__
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ register unsigned long _beg __asm ("a1") = start;
+ register unsigned long _end __asm ("a2") = stop;
+ register unsigned long _flg __asm ("a3") = 0;
+ __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+}
+#endif
+
+#ifdef __mc68000
+#include <asm/cachectl.h>
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ cacheflush(start,FLUSH_SCOPE_LINE,FLUSH_CACHE_BOTH,stop-start+16);
+}
+#endif
+
+#ifdef __alpha__
+
+register int gp asm("$29");
+
+static inline void immediate_ldah(void *p, int val) {
+ uint32_t *dest = p;
+ long high = ((val >> 16) + ((val >> 15) & 1)) & 0xffff;
+
+ *dest &= ~0xffff;
+ *dest |= high;
+ *dest |= 31 << 16;
+}
+static inline void immediate_lda(void *dest, int val) {
+ *(uint16_t *) dest = val;
+}
+void fix_bsr(void *p, int offset) {
+ uint32_t *dest = p;
+ *dest &= ~((1 << 21) - 1);
+ *dest |= (offset >> 2) & ((1 << 21) - 1);
+}
+
+#endif /* __alpha__ */
+
+#ifdef __arm__
+
+#define MAX_OP_SIZE (128 * 4) /* in bytes */
+/* max size of the code that can be generated without calling arm_flush_ldr */
+#define MAX_FRAG_SIZE (1024 * 4)
+//#define MAX_FRAG_SIZE (135 * 4) /* for testing */
+
+typedef struct LDREntry {
+ uint8_t *ptr;
+ uint32_t *data_ptr;
+} LDREntry;
+
+static LDREntry arm_ldr_table[1024];
+static uint32_t arm_data_table[1024];
+
+extern char exec_loop;
+
+static inline void arm_reloc_pc24(uint32_t *ptr, uint32_t insn, int val)
+{
+ *ptr = (insn & ~0xffffff) | ((insn + ((val - (int)ptr) >> 2)) & 0xffffff);
+}
+
+static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr,
+ LDREntry *ldr_start, LDREntry *ldr_end,
+ uint32_t *data_start, uint32_t *data_end,
+ int gen_jmp)
+{
+ LDREntry *le;
+ uint32_t *ptr;
+ int offset, data_size, target;
+ uint8_t *data_ptr;
+ uint32_t insn;
+
+ data_size = (uint8_t *)data_end - (uint8_t *)data_start;
+
+ if (gen_jmp) {
+ /* generate branch to skip the data */
+ if (data_size == 0)
+ return gen_code_ptr;
+ target = (long)gen_code_ptr + data_size + 4;
+ arm_reloc_pc24((uint32_t *)gen_code_ptr, 0xeafffffe, target);
+ gen_code_ptr += 4;
+ }
+
+ /* copy the data */
+ data_ptr = gen_code_ptr;
+ memcpy(gen_code_ptr, data_start, data_size);
+ gen_code_ptr += data_size;
+
+ /* patch the ldr to point to the data */
+ for(le = ldr_start; le < ldr_end; le++) {
+ ptr = (uint32_t *)le->ptr;
+ offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) +
+ (unsigned long)data_ptr -
+ (unsigned long)ptr - 8;
+ insn = *ptr & ~(0xfff | 0x00800000);
+ if (offset < 0) {
+ offset = - offset;
+ } else {
+ insn |= 0x00800000;
+ }
+ if (offset > 0xfff) {
+ fprintf(stderr, "Error ldr offset\n");
+ abort();
+ }
+ insn |= offset;
+ *ptr = insn;
+ }
+ return gen_code_ptr;
+}
+
+#endif /* __arm__ */
+
+#ifdef __ia64
+
+
+/* Patch instruction with "val" where "mask" has 1 bits. */
+static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
+{
+ uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
+# define insn_mask ((1UL << 41) - 1)
+ unsigned long shift;
+
+ b0 = b[0]; b1 = b[1];
+ shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */
+ if (shift >= 64) {
+ m1 = mask << (shift - 64);
+ v1 = val << (shift - 64);
+ } else {
+ m0 = mask << shift; m1 = mask >> (64 - shift);
+ v0 = val << shift; v1 = val >> (64 - shift);
+ b[0] = (b0 & ~m0) | (v0 & m0);
+ }
+ b[1] = (b1 & ~m1) | (v1 & m1);
+}
+
+static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val)
+{
+ ia64_patch(insn_addr,
+ 0x011ffffe000UL,
+ ( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */
+ | ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */));
+ ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18);
+}
+
+static inline void ia64_imm64 (void *insn, uint64_t val)
+{
+ /* Ignore the slot number of the relocation; GCC and Intel
+ toolchains differed for some time on whether IMM64 relocs are
+ against slot 1 (Intel) or slot 2 (GCC). */
+ uint64_t insn_addr = (uint64_t) insn & ~3UL;
+
+ ia64_patch(insn_addr + 2,
+ 0x01fffefe000UL,
+ ( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */
+ | ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */
+ | ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */
+ | ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */
+ | ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)
+ );
+ ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22);
+}
+
+static inline void ia64_imm60b (void *insn, uint64_t val)
+{
+ /* Ignore the slot number of the relocation; GCC and Intel
+ toolchains differed for some time on whether IMM64 relocs are
+ against slot 1 (Intel) or slot 2 (GCC). */
+ uint64_t insn_addr = (uint64_t) insn & ~3UL;
+
+ if (val + ((uint64_t) 1 << 59) >= (1UL << 60))
+ fprintf(stderr, "%s: value %ld out of IMM60 range\n",
+ __FUNCTION__, (int64_t) val);
+ ia64_patch_imm60(insn_addr + 2, val);
+}
+
+static inline void ia64_imm22 (void *insn, uint64_t val)
+{
+ if (val + (1 << 21) >= (1 << 22))
+ fprintf(stderr, "%s: value %li out of IMM22 range\n",
+ __FUNCTION__, (int64_t)val);
+ ia64_patch((uint64_t) insn, 0x01fffcfe000UL,
+ ( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
+ | ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
+ | ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
+ | ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
+}
+
+/* Like ia64_imm22(), but also clear bits 20-21. For addl, this has
+ the effect of turning "addl rX=imm22,rY" into "addl
+ rX=imm22,r0". */
+static inline void ia64_imm22_r0 (void *insn, uint64_t val)
+{
+ if (val + (1 << 21) >= (1 << 22))
+ fprintf(stderr, "%s: value %li out of IMM22 range\n",
+ __FUNCTION__, (int64_t)val);
+ ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20),
+ ( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
+ | ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
+ | ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
+ | ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
+}
+
+static inline void ia64_imm21b (void *insn, uint64_t val)
+{
+ if (val + (1 << 20) >= (1 << 21))
+ fprintf(stderr, "%s: value %li out of IMM21b range\n",
+ __FUNCTION__, (int64_t)val);
+ ia64_patch((uint64_t) insn, 0x11ffffe000UL,
+ ( ((val & 0x100000UL) << 16) /* bit 20 -> 36 */
+ | ((val & 0x0fffffUL) << 13) /* bit 0 -> 13 */));
+}
+
+static inline void ia64_nop_b (void *insn)
+{
+ ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37);
+}
+
+static inline void ia64_ldxmov(void *insn, uint64_t val)
+{
+ if (val + (1 << 21) < (1 << 22))
+ ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37);
+}
+
+static inline int ia64_patch_ltoff(void *insn, uint64_t val,
+ int relaxable)
+{
+ if (relaxable && (val + (1 << 21) < (1 << 22))) {
+ ia64_imm22_r0(insn, val);
+ return 0;
+ }
+ return 1;
+}
+
+struct ia64_fixup {
+ struct ia64_fixup *next;
+ void *addr; /* address that needs to be patched */
+ long value;
+};
+
+#define IA64_PLT(insn, plt_index) \
+do { \
+ struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
+ fixup->next = plt_fixes; \
+ plt_fixes = fixup; \
+ fixup->addr = (insn); \
+ fixup->value = (plt_index); \
+ plt_offset[(plt_index)] = 1; \
+} while (0)
+
+#define IA64_LTOFF(insn, val, relaxable) \
+do { \
+ if (ia64_patch_ltoff(insn, val, relaxable)) { \
+ struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
+ fixup->next = ltoff_fixes; \
+ ltoff_fixes = fixup; \
+ fixup->addr = (insn); \
+ fixup->value = (val); \
+ } \
+} while (0)
+
+static inline void ia64_apply_fixes (uint8_t **gen_code_pp,
+ struct ia64_fixup *ltoff_fixes,
+ uint64_t gp,
+ struct ia64_fixup *plt_fixes,
+ int num_plts,
+ unsigned long *plt_target,
+ unsigned int *plt_offset)
+{
+ static const uint8_t plt_bundle[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; movl r1=GP */
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60,
+
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; brl IP */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0
+ };
+ uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start, *vp;
+ struct ia64_fixup *fixup;
+ unsigned int offset = 0;
+ struct fdesc {
+ long ip;
+ long gp;
+ } *fdesc;
+ int i;
+
+ if (plt_fixes) {
+ plt_start = gen_code_ptr;
+
+ for (i = 0; i < num_plts; ++i) {
+ if (plt_offset[i]) {
+ plt_offset[i] = offset;
+ offset += sizeof(plt_bundle);
+
+ fdesc = (struct fdesc *) plt_target[i];
+ memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle));
+ ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp);
+ ia64_imm60b(gen_code_ptr + 0x12,
+ (fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4);
+ gen_code_ptr += sizeof(plt_bundle);
+ }
+ }
+
+ for (fixup = plt_fixes; fixup; fixup = fixup->next)
+ ia64_imm21b(fixup->addr,
+ ((long) plt_start + plt_offset[fixup->value]
+ - ((long) fixup->addr & ~0xf)) >> 4);
+ }
+
+ got_start = gen_code_ptr;
+
+ /* First, create the GOT: */
+ for (fixup = ltoff_fixes; fixup; fixup = fixup->next) {
+ /* first check if we already have this value in the GOT: */
+ for (vp = got_start; vp < gen_code_ptr; ++vp)
+ if (*(uint64_t *) vp == fixup->value)
+ break;
+ if (vp == gen_code_ptr) {
+ /* Nope, we need to put the value in the GOT: */
+ *(uint64_t *) vp = fixup->value;
+ gen_code_ptr += 8;
+ }
+ ia64_imm22(fixup->addr, (long) vp - gp);
+ }
+ /* Keep code ptr aligned. */
+ if ((long) gen_code_ptr & 15)
+ gen_code_ptr += 8;
+ *gen_code_pp = gen_code_ptr;
+}
+
+#endif
diff --git a/elf.h b/elf.h
new file mode 100644
index 0000000..8ceb949
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,1162 @@
+#ifndef _QEMU_ELF_H
+#define _QEMU_ELF_H
+
+#include <inttypes.h>
+
+/* 32-bit ELF base types. */
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+/* 64-bit ELF base types. */
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef int16_t Elf64_SHalf;
+typedef uint64_t Elf64_Off;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+
+/* These constants are for the segment types stored in the image headers */
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_LOPROC 0x70000000
+#define PT_HIPROC 0x7fffffff
+#define PT_MIPS_REGINFO 0x70000000
+#define PT_MIPS_OPTIONS 0x70000001
+
+/* Flags in the e_flags field of the header */
+/* MIPS architecture level. */
+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */
+#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */
+
+/* The ABI of a file. */
+#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */
+#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */
+
+#define EF_MIPS_NOREORDER 0x00000001
+#define EF_MIPS_PIC 0x00000002
+#define EF_MIPS_CPIC 0x00000004
+#define EF_MIPS_ABI2 0x00000020
+#define EF_MIPS_OPTIONS_FIRST 0x00000080
+#define EF_MIPS_32BITMODE 0x00000100
+#define EF_MIPS_ABI 0x0000f000
+#define EF_MIPS_ARCH 0xf0000000
+
+/* These constants define the different elf file types */
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* These constants define the various ELF target machines */
+#define EM_NONE 0
+#define EM_M32 1
+#define EM_SPARC 2
+#define EM_386 3
+#define EM_68K 4
+#define EM_88K 5
+#define EM_486 6 /* Perhaps disused */
+#define EM_860 7
+
+#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
+
+#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
+
+#define EM_PARISC 15 /* HPPA */
+
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC64 */
+
+#define EM_ARM 40 /* ARM */
+
+#define EM_SH 42 /* SuperH */
+
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+
+#define EM_IA_64 50 /* HP/Intel IA-64 */
+
+#define EM_X86_64 62 /* AMD x86-64 */
+
+#define EM_S390 22 /* IBM S/390 */
+
+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+
+#define EM_V850 87 /* NEC v850 */
+
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+
+/*
+ * This is an interim value that we will use until the committee comes
+ * up with a final number.
+ */
+#define EM_ALPHA 0x9026
+
+/* Bogus old v850 magic number, used by old tools. */
+#define EM_CYGNUS_V850 0x9080
+
+/*
+ * This is the old interim value for S/390 architecture
+ */
+#define EM_S390_OLD 0xA390
+
+/* This is the info that is needed to parse the dynamic section of the file */
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_LOPROC 0x70000000
+#define DT_HIPROC 0x7fffffff
+#define DT_MIPS_RLD_VERSION 0x70000001
+#define DT_MIPS_TIME_STAMP 0x70000002
+#define DT_MIPS_ICHECKSUM 0x70000003
+#define DT_MIPS_IVERSION 0x70000004
+#define DT_MIPS_FLAGS 0x70000005
+ #define RHF_NONE 0
+ #define RHF_HARDWAY 1
+ #define RHF_NOTPOT 2
+#define DT_MIPS_BASE_ADDRESS 0x70000006
+#define DT_MIPS_CONFLICT 0x70000008
+#define DT_MIPS_LIBLIST 0x70000009
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a
+#define DT_MIPS_CONFLICTNO 0x7000000b
+#define DT_MIPS_LIBLISTNO 0x70000010
+#define DT_MIPS_SYMTABNO 0x70000011
+#define DT_MIPS_UNREFEXTNO 0x70000012
+#define DT_MIPS_GOTSYM 0x70000013
+#define DT_MIPS_HIPAGENO 0x70000014
+#define DT_MIPS_RLD_MAP 0x70000016
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL 0
+#define STB_GLOBAL 1
+#define STB_WEAK 2
+
+#define STT_NOTYPE 0
+#define STT_OBJECT 1
+#define STT_FUNC 2
+#define STT_SECTION 3
+#define STT_FILE 4
+
+#define ELF_ST_BIND(x) ((x) >> 4)
+#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf)
+#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x)
+#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x)
+
+/* Symbolic values for the entries in the auxiliary table
+ put on the initial stack */
+#define AT_NULL 0 /* end of vector */
+#define AT_IGNORE 1 /* entry should be ignored */
+#define AT_EXECFD 2 /* file descriptor of program */
+#define AT_PHDR 3 /* program headers for program */
+#define AT_PHENT 4 /* size of program header entry */
+#define AT_PHNUM 5 /* number of program headers */
+#define AT_PAGESZ 6 /* system page size */
+#define AT_BASE 7 /* base address of interpreter */
+#define AT_FLAGS 8 /* flags */
+#define AT_ENTRY 9 /* entry point of program */
+#define AT_NOTELF 10 /* program is not ELF */
+#define AT_UID 11 /* real uid */
+#define AT_EUID 12 /* effective uid */
+#define AT_GID 13 /* real gid */
+#define AT_EGID 14 /* effective gid */
+#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
+#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
+#define AT_CLKTCK 17 /* frequency at which times() increments */
+
+typedef struct dynamic{
+ Elf32_Sword d_tag;
+ union{
+ Elf32_Sword d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+ Elf64_Sxword d_tag; /* entry tag value */
+ union {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ } d_un;
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
+#define ELF64_R_TYPE_DATA(i) (((ELF64_R_TYPE(i) >> 8) ^ 0x00800000) - 0x00800000)
+
+#define R_386_NONE 0
+#define R_386_32 1
+#define R_386_PC32 2
+#define R_386_GOT32 3
+#define R_386_PLT32 4
+#define R_386_COPY 5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+#define R_386_GOTOFF 9
+#define R_386_GOTPC 10
+#define R_386_NUM 11
+
+#define R_MIPS_NONE 0
+#define R_MIPS_16 1
+#define R_MIPS_32 2
+#define R_MIPS_REL32 3
+#define R_MIPS_26 4
+#define R_MIPS_HI16 5
+#define R_MIPS_LO16 6
+#define R_MIPS_GPREL16 7
+#define R_MIPS_LITERAL 8
+#define R_MIPS_GOT16 9
+#define R_MIPS_PC16 10
+#define R_MIPS_CALL16 11
+#define R_MIPS_GPREL32 12
+/* The remaining relocs are defined on Irix, although they are not
+ in the MIPS ELF ABI. */
+#define R_MIPS_UNUSED1 13
+#define R_MIPS_UNUSED2 14
+#define R_MIPS_UNUSED3 15
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_GOTHI16 22
+#define R_MIPS_GOTLO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_CALLHI16 30
+#define R_MIPS_CALLLO16 31
+/*
+ * This range is reserved for vendor specific relocations.
+ */
+#define R_MIPS_LOVENDOR 100
+#define R_MIPS_HIVENDOR 127
+
+
+/*
+ * Sparc ELF relocation types
+ */
+#define R_SPARC_NONE 0
+#define R_SPARC_8 1
+#define R_SPARC_16 2
+#define R_SPARC_32 3
+#define R_SPARC_DISP8 4
+#define R_SPARC_DISP16 5
+#define R_SPARC_DISP32 6
+#define R_SPARC_WDISP30 7
+#define R_SPARC_WDISP22 8
+#define R_SPARC_HI22 9
+#define R_SPARC_22 10
+#define R_SPARC_13 11
+#define R_SPARC_LO10 12
+#define R_SPARC_GOT10 13
+#define R_SPARC_GOT13 14
+#define R_SPARC_GOT22 15
+#define R_SPARC_PC10 16
+#define R_SPARC_PC22 17
+#define R_SPARC_WPLT30 18
+#define R_SPARC_COPY 19
+#define R_SPARC_GLOB_DAT 20
+#define R_SPARC_JMP_SLOT 21
+#define R_SPARC_RELATIVE 22
+#define R_SPARC_UA32 23
+#define R_SPARC_PLT32 24
+#define R_SPARC_HIPLT22 25
+#define R_SPARC_LOPLT10 26
+#define R_SPARC_PCPLT32 27
+#define R_SPARC_PCPLT22 28
+#define R_SPARC_PCPLT10 29
+#define R_SPARC_10 30
+#define R_SPARC_11 31
+#define R_SPARC_64 32
+#define R_SPARC_OLO10 33
+#define R_SPARC_WDISP16 40
+#define R_SPARC_WDISP19 41
+#define R_SPARC_7 43
+#define R_SPARC_5 44
+#define R_SPARC_6 45
+
+/* Bits present in AT_HWCAP, primarily for Sparc32. */
+
+#define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */
+#define HWCAP_SPARC_STBAR 2
+#define HWCAP_SPARC_SWAP 4
+#define HWCAP_SPARC_MULDIV 8
+#define HWCAP_SPARC_V9 16
+#define HWCAP_SPARC_ULTRA3 32
+
+/*
+ * 68k ELF relocation types
+ */
+#define R_68K_NONE 0
+#define R_68K_32 1
+#define R_68K_16 2
+#define R_68K_8 3
+#define R_68K_PC32 4
+#define R_68K_PC16 5
+#define R_68K_PC8 6
+#define R_68K_GOT32 7
+#define R_68K_GOT16 8
+#define R_68K_GOT8 9
+#define R_68K_GOT32O 10
+#define R_68K_GOT16O 11
+#define R_68K_GOT8O 12
+#define R_68K_PLT32 13
+#define R_68K_PLT16 14
+#define R_68K_PLT8 15
+#define R_68K_PLT32O 16
+#define R_68K_PLT16O 17
+#define R_68K_PLT8O 18
+#define R_68K_COPY 19
+#define R_68K_GLOB_DAT 20
+#define R_68K_JMP_SLOT 21
+#define R_68K_RELATIVE 22
+
+/*
+ * Alpha ELF relocation types
+ */
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+#define R_ALPHA_BRSGP 28
+#define R_ALPHA_TLSGD 29
+#define R_ALPHA_TLS_LDM 30
+#define R_ALPHA_DTPMOD64 31
+#define R_ALPHA_GOTDTPREL 32
+#define R_ALPHA_DTPREL64 33
+#define R_ALPHA_DTPRELHI 34
+#define R_ALPHA_DTPRELLO 35
+#define R_ALPHA_DTPREL16 36
+#define R_ALPHA_GOTTPREL 37
+#define R_ALPHA_TPREL64 38
+#define R_ALPHA_TPRELHI 39
+#define R_ALPHA_TPRELLO 40
+#define R_ALPHA_TPREL16 41
+
+#define SHF_ALPHA_GPREL 0x10000000
+
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE 0
+#define R_PPC_ADDR32 1 /* 32bit absolute address */
+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
+#define R_PPC_ADDR16 3 /* 16bit absolute address */
+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN 8
+#define R_PPC_ADDR14_BRNTAKEN 9
+#define R_PPC_REL24 10 /* PC relative 26 bit */
+#define R_PPC_REL14 11 /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN 12
+#define R_PPC_REL14_BRNTAKEN 13
+#define R_PPC_GOT16 14
+#define R_PPC_GOT16_LO 15
+#define R_PPC_GOT16_HI 16
+#define R_PPC_GOT16_HA 17
+#define R_PPC_PLTREL24 18
+#define R_PPC_COPY 19
+#define R_PPC_GLOB_DAT 20
+#define R_PPC_JMP_SLOT 21
+#define R_PPC_RELATIVE 22
+#define R_PPC_LOCAL24PC 23
+#define R_PPC_UADDR32 24
+#define R_PPC_UADDR16 25
+#define R_PPC_REL32 26
+#define R_PPC_PLT32 27
+#define R_PPC_PLTREL32 28
+#define R_PPC_PLT16_LO 29
+#define R_PPC_PLT16_HI 30
+#define R_PPC_PLT16_HA 31
+#define R_PPC_SDAREL16 32
+#define R_PPC_SECTOFF 33
+#define R_PPC_SECTOFF_LO 34
+#define R_PPC_SECTOFF_HI 35
+#define R_PPC_SECTOFF_HA 36
+/* Keep this the last entry. */
+#define R_PPC_NUM 37
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_ARM_RELEXEC 0x01
+#define EF_ARM_HASENTRY 0x02
+#define EF_ARM_INTERWORK 0x04
+#define EF_ARM_APCS_26 0x08
+#define EF_ARM_APCS_FLOAT 0x10
+#define EF_ARM_PIC 0x20
+#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */
+#define EF_NEW_ABI 0x80
+#define EF_OLD_ABI 0x100
+
+/* Additional symbol types for Thumb */
+#define STT_ARM_TFUNC 0xd
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
+ in the input to a link step */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB 0x10000000 /* Segment contains the location
+ addressed by the static base */
+
+/* ARM relocs. */
+#define R_ARM_NONE 0 /* No reloc */
+#define R_ARM_PC24 1 /* PC relative 26 bit branch */
+#define R_ARM_ABS32 2 /* Direct 32 bit */
+#define R_ARM_REL32 3 /* PC relative 32 bit */
+#define R_ARM_PC13 4
+#define R_ARM_ABS16 5 /* Direct 16 bit */
+#define R_ARM_ABS12 6 /* Direct 12 bit */
+#define R_ARM_THM_ABS5 7
+#define R_ARM_ABS8 8 /* Direct 8 bit */
+#define R_ARM_SBREL32 9
+#define R_ARM_THM_PC22 10
+#define R_ARM_THM_PC8 11
+#define R_ARM_AMP_VCALL9 12
+#define R_ARM_SWI24 13
+#define R_ARM_THM_SWI8 14
+#define R_ARM_XPC25 15
+#define R_ARM_THM_XPC22 16
+#define R_ARM_COPY 20 /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
+#define R_ARM_RELATIVE 23 /* Adjust by program base */
+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32 26 /* 32 bit GOT entry */
+#define R_ARM_PLT32 27 /* 32 bit PLT address */
+#define R_ARM_GNU_VTENTRY 100
+#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
+#define R_ARM_THM_PC9 103 /* thumb conditional branch */
+#define R_ARM_RXPC25 249
+#define R_ARM_RSBREL32 250
+#define R_ARM_THM_RPC22 251
+#define R_ARM_RREL32 252
+#define R_ARM_RABS22 253
+#define R_ARM_RPC24 254
+#define R_ARM_RBASE 255
+/* Keep this the last entry. */
+#define R_ARM_NUM 256
+
+/* s390 relocations defined by the ABIs */
+#define R_390_NONE 0 /* No reloc. */
+#define R_390_8 1 /* Direct 8 bit. */
+#define R_390_12 2 /* Direct 12 bit. */
+#define R_390_16 3 /* Direct 16 bit. */
+#define R_390_32 4 /* Direct 32 bit. */
+#define R_390_PC32 5 /* PC relative 32 bit. */
+#define R_390_GOT12 6 /* 12 bit GOT offset. */
+#define R_390_GOT32 7 /* 32 bit GOT offset. */
+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
+#define R_390_COPY 9 /* Copy symbol at runtime. */
+#define R_390_GLOB_DAT 10 /* Create GOT entry. */
+#define R_390_JMP_SLOT 11 /* Create PLT entry. */
+#define R_390_RELATIVE 12 /* Adjust by program base. */
+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
+#define R_390_GOTPC 14 /* 32 bit PC rel. offset to GOT. */
+#define R_390_GOT16 15 /* 16 bit GOT offset. */
+#define R_390_PC16 16 /* PC relative 16 bit. */
+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
+#define R_390_64 22 /* Direct 64 bit. */
+#define R_390_PC64 23 /* PC relative 64 bit. */
+#define R_390_GOT64 24 /* 64 bit GOT offset. */
+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL 38 /* Tag for function call in general
+ dynamic TLS code. */
+#define R_390_TLS_LDCALL 39 /* Tag for function call in local
+ dynamic TLS code. */
+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
+ thread local data in LD code. */
+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
+ thread local data in LD code. */
+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
+ block. */
+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
+ block. */
+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
+#define R_390_TLS_TPOFF 56 /* Negate offset in static TLS
+ block. */
+/* Keep this the last entry. */
+#define R_390_NUM 57
+
+/* x86-64 relocation types */
+#define R_X86_64_NONE 0 /* No reloc */
+#define R_X86_64_64 1 /* Direct 64 bit */
+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
+#define R_X86_64_PLT32 4 /* 32 bit PLT address */
+#define R_X86_64_COPY 5 /* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
+#define R_X86_64_RELATIVE 8 /* Adjust by program base */
+#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative
+ offset to GOT */
+#define R_X86_64_32 10 /* Direct 32 bit zero extended */
+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
+#define R_X86_64_16 12 /* Direct 16 bit zero extended */
+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
+#define R_X86_64_8 14 /* Direct 8 bit sign extended */
+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
+
+#define R_X86_64_NUM 16
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
+
+/* HPPA specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
+ prediction. */
+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
+
+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
+
+/* Additional section indeces. */
+
+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
+ symbols in ANSI C. */
+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
+
+#define STT_HP_OPAQUE (STT_LOOS + 0x1)
+#define STT_HP_STUB (STT_LOOS + 0x2)
+
+/* HPPA relocs. */
+
+#define R_PARISC_NONE 0 /* No reloc. */
+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64 64 /* 64 bits function address. */
+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LORESERVE 128
+#define R_PARISC_COPY 128 /* Copy relocation. */
+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_HIRESERVE 255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PT_HP_TLS (PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
+#define PT_HP_PARALLEL (PT_LOOS + 0x10)
+#define PT_HP_FASTBIND (PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
+#define PT_HP_STACK (PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT 0x70000000
+#define PT_PARISC_UNWIND 0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PF_PARISC_SBP 0x08000000
+
+#define PF_HP_PAGE_SIZE 0x00100000
+#define PF_HP_FAR_SHARED 0x00200000
+#define PF_HP_NEAR_SHARED 0x00400000
+#define PF_HP_CODE 0x01000000
+#define PF_HP_MODIFY 0x02000000
+#define PF_HP_LAZYSWAP 0x04000000
+#define PF_HP_SBP 0x08000000
+
+/* IA-64 specific declarations. */
+
+/* Processor specific flags for the Ehdr e_flags field. */
+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
+
+/* Processor specific flags for the Phdr p_flags field. */
+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field. */
+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field. */
+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
+#define DT_IA_64_NUM 1
+
+/* IA-64 relocations. */
+#define R_IA64_NONE 0x00 /* none */
+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY 0x84 /* copy relocation */
+#define R_IA64_SUB 0x85 /* Addend and symbol difference */
+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+
+typedef struct elf32_rel {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Xword r_info; /* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Xword r_info; /* index and type of relocation */
+ Elf64_Sxword r_addend; /* Constant addend used to compute value */
+} Elf64_Rela;
+
+typedef struct elf32_sym{
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+ Elf64_Word st_name; /* Symbol name, index in string tbl */
+ unsigned char st_info; /* Type and binding attributes */
+ unsigned char st_other; /* No defined meaning, 0 */
+ Elf64_Half st_shndx; /* Associated section index */
+ Elf64_Addr st_value; /* Value of the symbol */
+ Elf64_Xword st_size; /* Associated symbol size */
+} Elf64_Sym;
+
+
+#define EI_NIDENT 16
+
+typedef struct elf32_hdr{
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct elf64_hdr {
+ unsigned char e_ident[16]; /* ELF "magic number" */
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+/* These constants define the permissions on sections in the program
+ header, p_flags. */
+#define PF_R 0x4
+#define PF_W 0x2
+#define PF_X 0x1
+
+typedef struct elf32_phdr{
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct elf64_phdr {
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment, file & memory */
+} Elf64_Phdr;
+
+/* sh_type */
+#define SHT_NULL 0
+#define SHT_PROGBITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+#define SHT_NOTE 7
+#define SHT_NOBITS 8
+#define SHT_REL 9
+#define SHT_SHLIB 10
+#define SHT_DYNSYM 11
+#define SHT_NUM 12
+#define SHT_LOPROC 0x70000000
+#define SHT_HIPROC 0x7fffffff
+#define SHT_LOUSER 0x80000000
+#define SHT_HIUSER 0xffffffff
+#define SHT_MIPS_LIST 0x70000000
+#define SHT_MIPS_CONFLICT 0x70000002
+#define SHT_MIPS_GPTAB 0x70000003
+#define SHT_MIPS_UCODE 0x70000004
+
+/* sh_flags */
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXECINSTR 0x4
+#define SHF_MASKPROC 0xf0000000
+#define SHF_MIPS_GPREL 0x10000000
+
+/* special section indexes */
+#define SHN_UNDEF 0
+#define SHN_LORESERVE 0xff00
+#define SHN_LOPROC 0xff00
+#define SHN_HIPROC 0xff1f
+#define SHN_ABS 0xfff1
+#define SHN_COMMON 0xfff2
+#define SHN_HIRESERVE 0xffff
+#define SHN_MIPS_ACCOMON 0xff00
+
+typedef struct elf32_shdr {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf64_shdr {
+ Elf64_Word sh_name; /* Section name, index in string tbl */
+ Elf64_Word sh_type; /* Type of section */
+ Elf64_Xword sh_flags; /* Miscellaneous section attributes */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Size of section in bytes */
+ Elf64_Word sh_link; /* Index of another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_PAD 7
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define ELFCLASSNONE 0 /* EI_CLASS */
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EV_NONE 0 /* e_version, EI_VERSION */
+#define EV_CURRENT 1
+#define EV_NUM 2
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */
+
+
+/* Note header in a PT_NOTE section */
+typedef struct elf32_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+/* Note header in a PT_NOTE section */
+typedef struct elf64_note {
+ Elf64_Word n_namesz; /* Name size */
+ Elf64_Word n_descsz; /* Content size */
+ Elf64_Word n_type; /* Content type */
+} Elf64_Nhdr;
+
+#if ELF_CLASS == ELFCLASS32
+
+#define elfhdr elf32_hdr
+#define elf_phdr elf32_phdr
+#define elf_note elf32_note
+#define elf_shdr elf32_shdr
+#define elf_sym elf32_sym
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC Elf32_Rela
+#else
+# define ELF_RELOC Elf32_Rel
+#endif
+
+#else
+
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC Elf64_Rela
+#else
+# define ELF_RELOC Elf64_Rel
+#endif
+
+#endif /* ELF_CLASS */
+
+#ifndef ElfW
+# if ELF_CLASS == ELFCLASS32
+# define ElfW(x) Elf32_ ## x
+# define ELFW(x) ELF32_ ## x
+# else
+# define ElfW(x) Elf64_ ## x
+# define ELFW(x) ELF64_ ## x
+# endif
+#endif
+
+
+#endif /* _QEMU_ELF_H */
diff --git a/elf_ops.h b/elf_ops.h
new file mode 100644
index 0000000..122bf10
--- /dev/null
+++ b/elf_ops.h
@@ -0,0 +1,205 @@
+static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr)
+{
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswapSZs(&ehdr->e_entry); /* Entry point virtual address */
+ bswapSZs(&ehdr->e_phoff); /* Program header table file offset */
+ bswapSZs(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr)
+{
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswapSZs(&phdr->p_offset); /* Segment file offset */
+ bswapSZs(&phdr->p_vaddr); /* Segment virtual address */
+ bswapSZs(&phdr->p_paddr); /* Segment physical address */
+ bswapSZs(&phdr->p_filesz); /* Segment size in file */
+ bswapSZs(&phdr->p_memsz); /* Segment size in memory */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswapSZs(&phdr->p_align); /* Segment alignment */
+}
+
+static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr)
+{
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswapSZs(&shdr->sh_flags);
+ bswapSZs(&shdr->sh_addr);
+ bswapSZs(&shdr->sh_offset);
+ bswapSZs(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswapSZs(&shdr->sh_addralign);
+ bswapSZs(&shdr->sh_entsize);
+}
+
+static void glue(bswap_sym, SZ)(struct elf_sym *sym)
+{
+ bswap32s(&sym->st_name);
+ bswapSZs(&sym->st_value);
+ bswapSZs(&sym->st_size);
+ bswap16s(&sym->st_shndx);
+}
+
+static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
+ int n, int type)
+{
+ int i;
+ for(i=0;i<n;i++) {
+ if (shdr_table[i].sh_type == type)
+ return shdr_table + i;
+ }
+ return NULL;
+}
+
+static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
+{
+ struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
+ struct elf_sym *syms = NULL;
+#if (SZ == 64)
+ struct elf32_sym *syms32 = NULL;
+#endif
+ struct syminfo *s;
+ int nsyms, i;
+ char *str = NULL;
+
+ shdr_table = load_at(fd, ehdr->e_shoff,
+ sizeof(struct elf_shdr) * ehdr->e_shnum);
+ if (!shdr_table)
+ return -1;
+
+ if (must_swab) {
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ glue(bswap_shdr, SZ)(shdr_table + i);
+ }
+ }
+
+ symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
+ if (!symtab)
+ goto fail;
+ syms = load_at(fd, symtab->sh_offset, symtab->sh_size);
+ if (!syms)
+ goto fail;
+
+ nsyms = symtab->sh_size / sizeof(struct elf_sym);
+#if (SZ == 64)
+ syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym));
+#endif
+ for (i = 0; i < nsyms; i++) {
+ if (must_swab)
+ glue(bswap_sym, SZ)(&syms[i]);
+#if (SZ == 64)
+ syms32[i].st_name = syms[i].st_name;
+ syms32[i].st_info = syms[i].st_info;
+ syms32[i].st_other = syms[i].st_other;
+ syms32[i].st_shndx = syms[i].st_shndx;
+ syms32[i].st_value = syms[i].st_value & 0xffffffff;
+ syms32[i].st_size = syms[i].st_size & 0xffffffff;
+#endif
+ }
+ /* String table */
+ if (symtab->sh_link >= ehdr->e_shnum)
+ goto fail;
+ strtab = &shdr_table[symtab->sh_link];
+
+ str = load_at(fd, strtab->sh_offset, strtab->sh_size);
+ if (!str)
+ goto fail;
+
+ /* Commit */
+ s = qemu_mallocz(sizeof(*s));
+#if (SZ == 64)
+ s->disas_symtab = syms32;
+ qemu_free(syms);
+#else
+ s->disas_symtab = syms;
+#endif
+ s->disas_num_syms = nsyms;
+ s->disas_strtab = str;
+ s->next = syminfos;
+ syminfos = s;
+ qemu_free(shdr_table);
+ return 0;
+ fail:
+#if (SZ == 64)
+ qemu_free(syms32);
+#endif
+ qemu_free(syms);
+ qemu_free(str);
+ qemu_free(shdr_table);
+ return -1;
+}
+
+int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
+ int must_swab, uint64_t *pentry)
+{
+ struct elfhdr ehdr;
+ struct elf_phdr *phdr = NULL, *ph;
+ int size, i, total_size;
+ elf_word mem_size, addr;
+ uint8_t *data = NULL;
+
+ if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
+ goto fail;
+ if (must_swab) {
+ glue(bswap_ehdr, SZ)(&ehdr);
+ }
+
+ if (pentry)
+ *pentry = (uint64_t)ehdr.e_entry;
+
+ glue(load_symbols, SZ)(&ehdr, fd, must_swab);
+
+ size = ehdr.e_phnum * sizeof(phdr[0]);
+ lseek(fd, ehdr.e_phoff, SEEK_SET);
+ phdr = qemu_mallocz(size);
+ if (!phdr)
+ goto fail;
+ if (read(fd, phdr, size) != size)
+ goto fail;
+ if (must_swab) {
+ for(i = 0; i < ehdr.e_phnum; i++) {
+ ph = &phdr[i];
+ glue(bswap_phdr, SZ)(ph);
+ }
+ }
+
+ total_size = 0;
+ for(i = 0; i < ehdr.e_phnum; i++) {
+ ph = &phdr[i];
+ if (ph->p_type == PT_LOAD) {
+ mem_size = ph->p_memsz;
+ /* XXX: avoid allocating */
+ data = qemu_mallocz(mem_size);
+ if (ph->p_filesz > 0) {
+ if (lseek(fd, ph->p_offset, SEEK_SET) < 0)
+ goto fail;
+ if (read(fd, data, ph->p_filesz) != ph->p_filesz)
+ goto fail;
+ }
+ addr = ph->p_vaddr + virt_to_phys_addend;
+
+ cpu_physical_memory_write_rom(addr, data, mem_size);
+
+ total_size += mem_size;
+
+ qemu_free(data);
+ data = NULL;
+ }
+ }
+ qemu_free(phdr);
+ return total_size;
+ fail:
+ qemu_free(data);
+ qemu_free(phdr);
+ return -1;
+}
+
diff --git a/exec-all.h b/exec-all.h
new file mode 100644
index 0000000..b598948
--- /dev/null
+++ b/exec-all.h
@@ -0,0 +1,605 @@
+/*
+ * internal execution defines for qemu
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* allow to see translation results - the slowdown should be negligible, so we leave it */
+#define DEBUG_DISAS
+
+#ifndef glue
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+#endif
+
+#if __GNUC__ < 3
+#define __builtin_expect(x, n) (x)
+#endif
+
+#ifdef __i386__
+#define REGPARM(n) __attribute((regparm(n)))
+#else
+#define REGPARM(n)
+#endif
+
+/* is_jmp field values */
+#define DISAS_NEXT 0 /* next instruction can be analyzed */
+#define DISAS_JUMP 1 /* only pc was modified dynamically */
+#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
+#define DISAS_TB_JUMP 3 /* only pc was modified statically */
+
+struct TranslationBlock;
+
+/* XXX: make safe guess about sizes */
+#define MAX_OP_PER_INSTR 32
+#define OPC_BUF_SIZE 512
+#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
+
+#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * 3)
+
+extern uint16_t gen_opc_buf[OPC_BUF_SIZE];
+extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
+extern long gen_labels[OPC_BUF_SIZE];
+extern int nb_gen_labels;
+extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
+extern target_ulong gen_opc_npc[OPC_BUF_SIZE];
+extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+extern target_ulong gen_opc_jump_pc[2];
+extern uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+
+typedef void (GenOpFunc)(void);
+typedef void (GenOpFunc1)(long);
+typedef void (GenOpFunc2)(long, long);
+typedef void (GenOpFunc3)(long, long, long);
+
+#if defined(TARGET_I386)
+
+void optimize_flags_init(void);
+
+#endif
+
+extern FILE *logfile;
+extern int loglevel;
+
+int gen_intermediate_code(CPUState *env, struct TranslationBlock *tb);
+int gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb);
+void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf);
+int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
+ int max_code_size, int *gen_code_size_ptr);
+int cpu_restore_state(struct TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc);
+int cpu_gen_code_copy(CPUState *env, struct TranslationBlock *tb,
+ int max_code_size, int *gen_code_size_ptr);
+int cpu_restore_state_copy(struct TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc);
+void cpu_resume_from_signal(CPUState *env1, void *puc);
+void cpu_exec_init(CPUState *env);
+int page_unprotect(target_ulong address, unsigned long pc, void *puc);
+void tb_invalidate_phys_page_range(target_ulong start, target_ulong end,
+ int is_cpu_write_access);
+void tb_invalidate_page_range(target_ulong start, target_ulong end);
+void tlb_flush_page(CPUState *env, target_ulong addr);
+void tlb_flush(CPUState *env, int flush_global);
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu);
+static inline int tlb_set_page(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu)
+{
+ if (prot & PAGE_READ)
+ prot |= PAGE_EXEC;
+ return tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+}
+
+#define CODE_GEN_MAX_SIZE 65536
+#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
+
+#define CODE_GEN_PHYS_HASH_BITS 15
+#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS)
+
+/* maximum total translate dcode allocated */
+
+/* NOTE: the translated code area cannot be too big because on some
+ archs the range of "fast" function calls is limited. Here is a
+ summary of the ranges:
+
+ i386 : signed 32 bits
+ arm : signed 26 bits
+ ppc : signed 24 bits
+ sparc : signed 32 bits
+ alpha : signed 23 bits
+*/
+
+#if defined(__alpha__)
+#define CODE_GEN_BUFFER_SIZE (2 * 1024 * 1024)
+#elif defined(__ia64)
+#define CODE_GEN_BUFFER_SIZE (4 * 1024 * 1024) /* range of addl */
+#elif defined(__powerpc__)
+#define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024)
+#else
+#define CODE_GEN_BUFFER_SIZE (16 * 1024 * 1024)
+#endif
+
+//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
+
+/* estimated block size for TB allocation */
+/* XXX: use a per code average code fragment size and modulate it
+ according to the host CPU */
+#if defined(CONFIG_SOFTMMU)
+#define CODE_GEN_AVG_BLOCK_SIZE 128
+#else
+#define CODE_GEN_AVG_BLOCK_SIZE 64
+#endif
+
+#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / CODE_GEN_AVG_BLOCK_SIZE)
+
+#if defined(__powerpc__)
+#define USE_DIRECT_JUMP
+#endif
+#if defined(__i386__) && !defined(_WIN32)
+#define USE_DIRECT_JUMP
+#endif
+
+typedef struct TranslationBlock {
+ target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
+ target_ulong cs_base; /* CS base for this block */
+ unsigned int flags; /* flags defining in which context the code was generated */
+ uint16_t size; /* size of target code for this block (1 <=
+ size <= TARGET_PAGE_SIZE) */
+ uint16_t cflags; /* compile flags */
+#define CF_CODE_COPY 0x0001 /* block was generated in code copy mode */
+#define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */
+#define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */
+#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */
+
+ uint8_t *tc_ptr; /* pointer to the translated code */
+ /* next matching tb for physical address. */
+ struct TranslationBlock *phys_hash_next;
+ /* first and second physical page containing code. The lower bit
+ of the pointer tells the index in page_next[] */
+ struct TranslationBlock *page_next[2];
+ target_ulong page_addr[2];
+
+ /* the following data are used to directly call another TB from
+ the code of this one. */
+ uint16_t tb_next_offset[2]; /* offset of original jump target */
+#ifdef USE_DIRECT_JUMP
+ uint16_t tb_jmp_offset[4]; /* offset of jump instruction */
+#else
+ uint32_t tb_next[2]; /* address of jump generated code */
+#endif
+ /* list of TBs jumping to this one. This is a circular list using
+ the two least significant bits of the pointers to tell what is
+ the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
+ jmp_first */
+ struct TranslationBlock *jmp_next[2];
+ struct TranslationBlock *jmp_first;
+} TranslationBlock;
+
+static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
+{
+ return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1);
+}
+
+static inline unsigned int tb_phys_hash_func(unsigned long pc)
+{
+ return pc & (CODE_GEN_PHYS_HASH_SIZE - 1);
+}
+
+TranslationBlock *tb_alloc(target_ulong pc);
+void tb_flush(CPUState *env);
+void tb_link_phys(TranslationBlock *tb,
+ target_ulong phys_pc, target_ulong phys_page2);
+
+extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+
+extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
+extern uint8_t *code_gen_ptr;
+
+#if defined(USE_DIRECT_JUMP)
+
+#if defined(__powerpc__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+ uint32_t val, *ptr;
+
+ /* patch the branch destination */
+ ptr = (uint32_t *)jmp_addr;
+ val = *ptr;
+ val = (val & ~0x03fffffc) | ((addr - jmp_addr) & 0x03fffffc);
+ *ptr = val;
+ /* flush icache */
+ asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory");
+ asm volatile ("sync" : : : "memory");
+ asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory");
+ asm volatile ("sync" : : : "memory");
+ asm volatile ("isync" : : : "memory");
+}
+#elif defined(__i386__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+ /* patch the branch destination */
+ *(uint32_t *)jmp_addr = addr - (jmp_addr + 4);
+ /* no need to flush icache explicitely */
+}
+#endif
+
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+ int n, unsigned long addr)
+{
+ unsigned long offset;
+
+ offset = tb->tb_jmp_offset[n];
+ tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
+ offset = tb->tb_jmp_offset[n + 2];
+ if (offset != 0xffff)
+ tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
+}
+
+#else
+
+/* set the jump target */
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+ int n, unsigned long addr)
+{
+ tb->tb_next[n] = addr;
+}
+
+#endif
+
+static inline void tb_add_jump(TranslationBlock *tb, int n,
+ TranslationBlock *tb_next)
+{
+ /* NOTE: this test is only needed for thread safety */
+ if (!tb->jmp_next[n]) {
+ /* patch the native jump address */
+ tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
+
+ /* add in TB jmp circular list */
+ tb->jmp_next[n] = tb_next->jmp_first;
+ tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
+ }
+}
+
+TranslationBlock *tb_find_pc(unsigned long pc_ptr);
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+#if defined(_WIN32)
+#define ASM_DATA_SECTION ".section \".data\"\n"
+#define ASM_PREVIOUS_SECTION ".section .text\n"
+#elif defined(__APPLE__)
+#define ASM_DATA_SECTION ".data\n"
+#define ASM_PREVIOUS_SECTION ".text\n"
+#else
+#define ASM_DATA_SECTION ".section \".data\"\n"
+#define ASM_PREVIOUS_SECTION ".previous\n"
+#endif
+
+#define ASM_OP_LABEL_NAME(n, opname) \
+ ASM_NAME(__op_label) #n "." ASM_NAME(opname)
+
+#if defined(__powerpc__)
+
+/* we patch the jump instruction directly */
+#define GOTO_TB(opname, tbparam, n)\
+do {\
+ asm volatile (ASM_DATA_SECTION\
+ ASM_OP_LABEL_NAME(n, opname) ":\n"\
+ ".long 1f\n"\
+ ASM_PREVIOUS_SECTION \
+ "b " ASM_NAME(__op_jmp) #n "\n"\
+ "1:\n");\
+} while (0)
+
+#elif defined(__i386__) && defined(USE_DIRECT_JUMP)
+
+/* we patch the jump instruction directly */
+#define GOTO_TB(opname, tbparam, n)\
+do {\
+ asm volatile (".section .data\n"\
+ ASM_OP_LABEL_NAME(n, opname) ":\n"\
+ ".long 1f\n"\
+ ASM_PREVIOUS_SECTION \
+ "jmp " ASM_NAME(__op_jmp) #n "\n"\
+ "1:\n");\
+} while (0)
+
+#else
+
+/* jump to next block operations (more portable code, does not need
+ cache flushing, but slower because of indirect jump) */
+#define GOTO_TB(opname, tbparam, n)\
+do {\
+ static void __attribute__((unused)) *dummy ## n = &&dummy_label ## n;\
+ static void __attribute__((unused)) *__op_label ## n \
+ __asm__(ASM_OP_LABEL_NAME(n, opname)) = &&label ## n;\
+ goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\
+label ## n: ;\
+dummy_label ## n: ;\
+} while (0)
+
+#endif
+
+extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+
+#ifdef __powerpc__
+static inline int testandset (int *p)
+{
+ int ret;
+ __asm__ __volatile__ (
+ "0: lwarx %0,0,%1\n"
+ " xor. %0,%3,%0\n"
+ " bne 1f\n"
+ " stwcx. %2,0,%1\n"
+ " bne- 0b\n"
+ "1: "
+ : "=&r" (ret)
+ : "r" (p), "r" (1), "r" (0)
+ : "cr0", "memory");
+ return ret;
+}
+#endif
+
+#ifdef __i386__
+static inline int testandset (int *p)
+{
+ long int readval = 0;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+ : "+m" (*p), "+a" (readval)
+ : "r" (1)
+ : "cc");
+ return readval;
+}
+#endif
+
+#ifdef __x86_64__
+static inline int testandset (int *p)
+{
+ long int readval = 0;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+ : "+m" (*p), "+a" (readval)
+ : "r" (1)
+ : "cc");
+ return readval;
+}
+#endif
+
+#ifdef __s390__
+static inline int testandset (int *p)
+{
+ int ret;
+
+ __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
+ " jl 0b"
+ : "=&d" (ret)
+ : "r" (1), "a" (p), "0" (*p)
+ : "cc", "memory" );
+ return ret;
+}
+#endif
+
+#ifdef __alpha__
+static inline int testandset (int *p)
+{
+ int ret;
+ unsigned long one;
+
+ __asm__ __volatile__ ("0: mov 1,%2\n"
+ " ldl_l %0,%1\n"
+ " stl_c %2,%1\n"
+ " beq %2,1f\n"
+ ".subsection 2\n"
+ "1: br 0b\n"
+ ".previous"
+ : "=r" (ret), "=m" (*p), "=r" (one)
+ : "m" (*p));
+ return ret;
+}
+#endif
+
+#ifdef __sparc__
+static inline int testandset (int *p)
+{
+ int ret;
+
+ __asm__ __volatile__("ldstub [%1], %0"
+ : "=r" (ret)
+ : "r" (p)
+ : "memory");
+
+ return (ret ? 1 : 0);
+}
+#endif
+
+#ifdef __arm__
+static inline int testandset (int *spinlock)
+{
+ register unsigned int ret;
+ __asm__ __volatile__("swp %0, %1, [%2]"
+ : "=r"(ret)
+ : "0"(1), "r"(spinlock));
+
+ return ret;
+}
+#endif
+
+#ifdef __mc68000
+static inline int testandset (int *p)
+{
+ char ret;
+ __asm__ __volatile__("tas %1; sne %0"
+ : "=r" (ret)
+ : "m" (p)
+ : "cc","memory");
+ return ret;
+}
+#endif
+
+#ifdef __ia64
+#include <ia64intrin.h>
+
+static inline int testandset (int *p)
+{
+ return __sync_lock_test_and_set (p, 1);
+}
+#endif
+
+typedef int spinlock_t;
+
+#define SPIN_LOCK_UNLOCKED 0
+
+#if defined(CONFIG_USER_ONLY)
+static inline void spin_lock(spinlock_t *lock)
+{
+ while (testandset(lock));
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int spin_trylock(spinlock_t *lock)
+{
+ return !testandset(lock);
+}
+#else
+static inline void spin_lock(spinlock_t *lock)
+{
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+}
+
+static inline int spin_trylock(spinlock_t *lock)
+{
+ return 1;
+}
+#endif
+
+extern spinlock_t tb_lock;
+
+extern int tb_invalidated_flag;
+
+#if !defined(CONFIG_USER_ONLY)
+
+void tlb_fill(target_ulong addr, int is_write, int is_user,
+ void *retaddr);
+
+#define ACCESS_TYPE 3
+#define MEMSUFFIX _code
+#define env cpu_single_env
+
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#undef env
+
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+#else
+/* NOTE: this function can trigger an exception */
+/* NOTE2: the returned address is not exactly the physical address: it
+ is the offset relative to phys_ram_base */
+static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
+{
+ int is_user, index, pd;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+#if defined(TARGET_I386)
+ is_user = ((env->hflags & HF_CPL_MASK) == 3);
+#elif defined (TARGET_PPC)
+ is_user = msr_pr;
+#elif defined (TARGET_MIPS)
+ is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
+#elif defined (TARGET_SPARC)
+ is_user = (env->psrs == 0);
+#elif defined (TARGET_ARM)
+ is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR);
+#elif defined (TARGET_SH4)
+ is_user = ((env->sr & SR_MD) == 0);
+#else
+#error unimplemented CPU
+#endif
+ if (__builtin_expect(env->tlb_table[is_user][index].addr_code !=
+ (addr & TARGET_PAGE_MASK), 0)) {
+ ldub_code(addr);
+ }
+ pd = env->tlb_table[is_user][index].addr_code & ~TARGET_PAGE_MASK;
+ if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+ cpu_abort(env, "Trying to execute code outside RAM or ROM at 0x%08lx\n", addr);
+ }
+ return addr + env->tlb_table[is_user][index].addend - (unsigned long)phys_ram_base;
+}
+#endif
+
+
+#ifdef USE_KQEMU
+#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG))
+
+int kqemu_init(CPUState *env);
+int kqemu_cpu_exec(CPUState *env);
+void kqemu_flush_page(CPUState *env, target_ulong addr);
+void kqemu_flush(CPUState *env, int global);
+void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr);
+void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr);
+void kqemu_cpu_interrupt(CPUState *env);
+void kqemu_record_dump(void);
+
+static inline int kqemu_is_ok(CPUState *env)
+{
+ return(env->kqemu_enabled &&
+ (env->cr[0] & CR0_PE_MASK) &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK) &&
+ (env->eflags & IF_MASK) &&
+ !(env->eflags & VM_MASK) &&
+ (env->kqemu_enabled == 2 ||
+ ((env->hflags & HF_CPL_MASK) == 3 &&
+ (env->eflags & IOPL_MASK) != IOPL_MASK)));
+}
+
+#endif
diff --git a/exec.c b/exec.c
new file mode 100644
index 0000000..88acff0
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,2394 @@
+/*
+ * virtual page mapping and translated block handling
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#if defined(CONFIG_USER_ONLY)
+#include <qemu.h>
+#endif
+
+//#define DEBUG_TB_INVALIDATE
+//#define DEBUG_FLUSH
+//#define DEBUG_TLB
+
+/* make various TB consistency checks */
+//#define DEBUG_TB_CHECK
+//#define DEBUG_TLB_CHECK
+
+#if !defined(CONFIG_USER_ONLY)
+/* TB consistency checks only implemented for usermode emulation. */
+#undef DEBUG_TB_CHECK
+#endif
+
+/* threshold to flush the translated code buffer */
+#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
+
+#define SMC_BITMAP_USE_THRESHOLD 10
+
+#define MMAP_AREA_START 0x00000000
+#define MMAP_AREA_END 0xa8000000
+
+#if defined(TARGET_SPARC64)
+#define TARGET_PHYS_ADDR_SPACE_BITS 41
+#elif defined(TARGET_PPC64)
+#define TARGET_PHYS_ADDR_SPACE_BITS 42
+#else
+/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#endif
+
+TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
+TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+int nb_tbs;
+/* any access to the tbs or the page table must use this lock */
+spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
+
+uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE] __attribute__((aligned (32)));
+uint8_t *code_gen_ptr;
+
+int phys_ram_size;
+int phys_ram_fd;
+uint8_t *phys_ram_base;
+uint8_t *phys_ram_dirty;
+
+CPUState *first_cpu;
+/* current CPU in the current thread. It is only valid inside
+ cpu_exec() */
+CPUState *cpu_single_env;
+
+typedef struct PageDesc {
+ /* list of TBs intersecting this ram page */
+ TranslationBlock *first_tb;
+ /* in order to optimize self modifying code, we count the number
+ of lookups we do to a given page to use a bitmap */
+ unsigned int code_write_count;
+ uint8_t *code_bitmap;
+#if defined(CONFIG_USER_ONLY)
+ unsigned long flags;
+#endif
+} PageDesc;
+
+typedef struct PhysPageDesc {
+ /* offset in host memory of the page + io_index in the low 12 bits */
+ uint32_t phys_offset;
+} PhysPageDesc;
+
+#define L2_BITS 10
+#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS)
+
+#define L1_SIZE (1 << L1_BITS)
+#define L2_SIZE (1 << L2_BITS)
+
+static void io_mem_init(void);
+
+unsigned long qemu_real_host_page_size;
+unsigned long qemu_host_page_bits;
+unsigned long qemu_host_page_size;
+unsigned long qemu_host_page_mask;
+
+/* XXX: for system emulation, it could just be an array */
+static PageDesc *l1_map[L1_SIZE];
+PhysPageDesc **l1_phys_map;
+
+/* io memory support */
+CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+static int io_mem_nb;
+
+/* log support */
+char *logfilename = "/tmp/qemu.log";
+FILE *logfile;
+int loglevel;
+
+/* statistics */
+static int tlb_flush_count;
+static int tb_flush_count;
+static int tb_phys_invalidate_count;
+
+static void page_init(void)
+{
+ /* NOTE: we can always suppose that qemu_host_page_size >=
+ TARGET_PAGE_SIZE */
+#ifdef _WIN32
+ {
+ SYSTEM_INFO system_info;
+ DWORD old_protect;
+
+ GetSystemInfo(&system_info);
+ qemu_real_host_page_size = system_info.dwPageSize;
+
+ VirtualProtect(code_gen_buffer, sizeof(code_gen_buffer),
+ PAGE_EXECUTE_READWRITE, &old_protect);
+ }
+#else
+ qemu_real_host_page_size = getpagesize();
+ {
+ unsigned long start, end;
+
+ start = (unsigned long)code_gen_buffer;
+ start &= ~(qemu_real_host_page_size - 1);
+
+ end = (unsigned long)code_gen_buffer + sizeof(code_gen_buffer);
+ end += qemu_real_host_page_size - 1;
+ end &= ~(qemu_real_host_page_size - 1);
+
+ mprotect((void *)start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+ }
+#endif
+
+ if (qemu_host_page_size == 0)
+ qemu_host_page_size = qemu_real_host_page_size;
+ if (qemu_host_page_size < TARGET_PAGE_SIZE)
+ qemu_host_page_size = TARGET_PAGE_SIZE;
+ qemu_host_page_bits = 0;
+ while ((1 << qemu_host_page_bits) < qemu_host_page_size)
+ qemu_host_page_bits++;
+ qemu_host_page_mask = ~(qemu_host_page_size - 1);
+ l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *));
+ memset(l1_phys_map, 0, L1_SIZE * sizeof(void *));
+}
+
+static inline PageDesc *page_find_alloc(unsigned int index)
+{
+ PageDesc **lp, *p;
+
+ lp = &l1_map[index >> L2_BITS];
+ p = *lp;
+ if (!p) {
+ /* allocate if not found */
+ p = qemu_malloc(sizeof(PageDesc) * L2_SIZE);
+ memset(p, 0, sizeof(PageDesc) * L2_SIZE);
+ *lp = p;
+ }
+ return p + (index & (L2_SIZE - 1));
+}
+
+static inline PageDesc *page_find(unsigned int index)
+{
+ PageDesc *p;
+
+ p = l1_map[index >> L2_BITS];
+ if (!p)
+ return 0;
+ return p + (index & (L2_SIZE - 1));
+}
+
+static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc)
+{
+ void **lp, **p;
+ PhysPageDesc *pd;
+
+ p = (void **)l1_phys_map;
+#if TARGET_PHYS_ADDR_SPACE_BITS > 32
+
+#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS)
+#error unsupported TARGET_PHYS_ADDR_SPACE_BITS
+#endif
+ lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1));
+ p = *lp;
+ if (!p) {
+ /* allocate if not found */
+ if (!alloc)
+ return NULL;
+ p = qemu_vmalloc(sizeof(void *) * L1_SIZE);
+ memset(p, 0, sizeof(void *) * L1_SIZE);
+ *lp = p;
+ }
+#endif
+ lp = p + ((index >> L2_BITS) & (L1_SIZE - 1));
+ pd = *lp;
+ if (!pd) {
+ int i;
+ /* allocate if not found */
+ if (!alloc)
+ return NULL;
+ pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE);
+ *lp = pd;
+ for (i = 0; i < L2_SIZE; i++)
+ pd[i].phys_offset = IO_MEM_UNASSIGNED;
+ }
+ return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1));
+}
+
+static inline PhysPageDesc *phys_page_find(target_phys_addr_t index)
+{
+ return phys_page_find_alloc(index, 0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void tlb_protect_code(ram_addr_t ram_addr);
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+ target_ulong vaddr);
+#endif
+
+void cpu_exec_init(CPUState *env)
+{
+ CPUState **penv;
+ int cpu_index;
+
+ if (!code_gen_ptr) {
+ code_gen_ptr = code_gen_buffer;
+ page_init();
+ io_mem_init();
+ }
+ env->next_cpu = NULL;
+ penv = &first_cpu;
+ cpu_index = 0;
+ while (*penv != NULL) {
+ penv = (CPUState **)&(*penv)->next_cpu;
+ cpu_index++;
+ }
+ env->cpu_index = cpu_index;
+ *penv = env;
+}
+
+static inline void invalidate_page_bitmap(PageDesc *p)
+{
+ if (p->code_bitmap) {
+ qemu_free(p->code_bitmap);
+ p->code_bitmap = NULL;
+ }
+ p->code_write_count = 0;
+}
+
+/* set to NULL all the 'first_tb' fields in all PageDescs */
+static void page_flush_tb(void)
+{
+ int i, j;
+ PageDesc *p;
+
+ for(i = 0; i < L1_SIZE; i++) {
+ p = l1_map[i];
+ if (p) {
+ for(j = 0; j < L2_SIZE; j++) {
+ p->first_tb = NULL;
+ invalidate_page_bitmap(p);
+ p++;
+ }
+ }
+ }
+}
+
+/* flush all the translation blocks */
+/* XXX: tb_flush is currently not thread safe */
+void tb_flush(CPUState *env1)
+{
+ CPUState *env;
+#if defined(DEBUG_FLUSH)
+ printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
+ code_gen_ptr - code_gen_buffer,
+ nb_tbs,
+ nb_tbs > 0 ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0);
+#endif
+ nb_tbs = 0;
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+ }
+
+ memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *));
+ page_flush_tb();
+
+ code_gen_ptr = code_gen_buffer;
+ /* XXX: flush processor icache at this point if cache flush is
+ expensive */
+ tb_flush_count++;
+}
+
+#ifdef DEBUG_TB_CHECK
+
+static void tb_invalidate_check(unsigned long address)
+{
+ TranslationBlock *tb;
+ int i;
+ address &= TARGET_PAGE_MASK;
+ for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+ for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+ if (!(address + TARGET_PAGE_SIZE <= tb->pc ||
+ address >= tb->pc + tb->size)) {
+ printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n",
+ address, (long)tb->pc, tb->size);
+ }
+ }
+ }
+}
+
+/* verify that all the pages have correct rights for code */
+static void tb_page_check(void)
+{
+ TranslationBlock *tb;
+ int i, flags1, flags2;
+
+ for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+ for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+ flags1 = page_get_flags(tb->pc);
+ flags2 = page_get_flags(tb->pc + tb->size - 1);
+ if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
+ printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
+ (long)tb->pc, tb->size, flags1, flags2);
+ }
+ }
+ }
+}
+
+void tb_jmp_check(TranslationBlock *tb)
+{
+ TranslationBlock *tb1;
+ unsigned int n1;
+
+ /* suppress any remaining jumps to this TB */
+ tb1 = tb->jmp_first;
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == 2)
+ break;
+ tb1 = tb1->jmp_next[n1];
+ }
+ /* check end of list */
+ if (tb1 != tb) {
+ printf("ERROR: jmp_list from 0x%08lx\n", (long)tb);
+ }
+}
+
+#endif
+
+/* invalidate one TB */
+static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb,
+ int next_offset)
+{
+ TranslationBlock *tb1;
+ for(;;) {
+ tb1 = *ptb;
+ if (tb1 == tb) {
+ *ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
+ break;
+ }
+ ptb = (TranslationBlock **)((char *)tb1 + next_offset);
+ }
+}
+
+static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb)
+{
+ TranslationBlock *tb1;
+ unsigned int n1;
+
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (tb1 == tb) {
+ *ptb = tb1->page_next[n1];
+ break;
+ }
+ ptb = &tb1->page_next[n1];
+ }
+}
+
+static inline void tb_jmp_remove(TranslationBlock *tb, int n)
+{
+ TranslationBlock *tb1, **ptb;
+ unsigned int n1;
+
+ ptb = &tb->jmp_next[n];
+ tb1 = *ptb;
+ if (tb1) {
+ /* find tb(n) in circular list */
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == n && tb1 == tb)
+ break;
+ if (n1 == 2) {
+ ptb = &tb1->jmp_first;
+ } else {
+ ptb = &tb1->jmp_next[n1];
+ }
+ }
+ /* now we can suppress tb(n) from the list */
+ *ptb = tb->jmp_next[n];
+
+ tb->jmp_next[n] = NULL;
+ }
+}
+
+/* reset the jump entry 'n' of a TB so that it is not chained to
+ another TB */
+static inline void tb_reset_jump(TranslationBlock *tb, int n)
+{
+ tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
+}
+
+static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_addr)
+{
+ CPUState *env;
+ PageDesc *p;
+ unsigned int h, n1;
+ target_ulong phys_pc;
+ TranslationBlock *tb1, *tb2;
+
+ /* remove the TB from the hash list */
+ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+ h = tb_phys_hash_func(phys_pc);
+ tb_remove(&tb_phys_hash[h], tb,
+ offsetof(TranslationBlock, phys_hash_next));
+
+ /* remove the TB from the page list */
+ if (tb->page_addr[0] != page_addr) {
+ p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
+ tb_page_remove(&p->first_tb, tb);
+ invalidate_page_bitmap(p);
+ }
+ if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) {
+ p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
+ tb_page_remove(&p->first_tb, tb);
+ invalidate_page_bitmap(p);
+ }
+
+ tb_invalidated_flag = 1;
+
+ /* remove the TB from the hash list */
+ h = tb_jmp_cache_hash_func(tb->pc);
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->tb_jmp_cache[h] == tb)
+ env->tb_jmp_cache[h] = NULL;
+ }
+
+ /* suppress this TB from the two jump lists */
+ tb_jmp_remove(tb, 0);
+ tb_jmp_remove(tb, 1);
+
+ /* suppress any remaining jumps to this TB */
+ tb1 = tb->jmp_first;
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ if (n1 == 2)
+ break;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ tb2 = tb1->jmp_next[n1];
+ tb_reset_jump(tb1, n1);
+ tb1->jmp_next[n1] = NULL;
+ tb1 = tb2;
+ }
+ tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */
+
+ tb_phys_invalidate_count++;
+}
+
+static inline void set_bits(uint8_t *tab, int start, int len)
+{
+ int end, mask, end1;
+
+ end = start + len;
+ tab += start >> 3;
+ mask = 0xff << (start & 7);
+ if ((start & ~7) == (end & ~7)) {
+ if (start < end) {
+ mask &= ~(0xff << (end & 7));
+ *tab |= mask;
+ }
+ } else {
+ *tab++ |= mask;
+ start = (start + 8) & ~7;
+ end1 = end & ~7;
+ while (start < end1) {
+ *tab++ = 0xff;
+ start += 8;
+ }
+ if (start < end) {
+ mask = ~(0xff << (end & 7));
+ *tab |= mask;
+ }
+ }
+}
+
+static void build_page_bitmap(PageDesc *p)
+{
+ int n, tb_start, tb_end;
+ TranslationBlock *tb;
+
+ p->code_bitmap = qemu_malloc(TARGET_PAGE_SIZE / 8);
+ if (!p->code_bitmap)
+ return;
+ memset(p->code_bitmap, 0, TARGET_PAGE_SIZE / 8);
+
+ tb = p->first_tb;
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+ /* NOTE: this is subtle as a TB may span two physical pages */
+ if (n == 0) {
+ /* NOTE: tb_end may be after the end of the page, but
+ it is not a problem */
+ tb_start = tb->pc & ~TARGET_PAGE_MASK;
+ tb_end = tb_start + tb->size;
+ if (tb_end > TARGET_PAGE_SIZE)
+ tb_end = TARGET_PAGE_SIZE;
+ } else {
+ tb_start = 0;
+ tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+ }
+ set_bits(p->code_bitmap, tb_start, tb_end - tb_start);
+ tb = tb->page_next[n];
+ }
+}
+
+#ifdef TARGET_HAS_PRECISE_SMC
+
+static void tb_gen_code(CPUState *env,
+ target_ulong pc, target_ulong cs_base, int flags,
+ int cflags)
+{
+ TranslationBlock *tb;
+ uint8_t *tc_ptr;
+ target_ulong phys_pc, phys_page2, virt_page2;
+ int code_gen_size;
+
+ phys_pc = get_phys_addr_code(env, pc);
+ tb = tb_alloc(pc);
+ if (!tb) {
+ /* flush must be done */
+ tb_flush(env);
+ /* cannot fail at this point */
+ tb = tb_alloc(pc);
+ }
+ tc_ptr = code_gen_ptr;
+ tb->tc_ptr = tc_ptr;
+ tb->cs_base = cs_base;
+ tb->flags = flags;
+ tb->cflags = cflags;
+ cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
+ code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+
+ /* check next page if needed */
+ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
+ phys_page2 = -1;
+ if ((pc & TARGET_PAGE_MASK) != virt_page2) {
+ phys_page2 = get_phys_addr_code(env, virt_page2);
+ }
+ tb_link_phys(tb, phys_pc, phys_page2);
+}
+#endif
+
+/* invalidate all TBs which intersect with the target physical page
+ starting in range [start;end[. NOTE: start and end must refer to
+ the same physical page. 'is_cpu_write_access' should be true if called
+ from a real cpu write access: the virtual CPU will exit the current
+ TB if code is modified inside this TB. */
+void tb_invalidate_phys_page_range(target_ulong start, target_ulong end,
+ int is_cpu_write_access)
+{
+ int n, current_tb_modified, current_tb_not_found, current_flags;
+ CPUState *env = cpu_single_env;
+ PageDesc *p;
+ TranslationBlock *tb, *tb_next, *current_tb, *saved_tb;
+ target_ulong tb_start, tb_end;
+ target_ulong current_pc, current_cs_base;
+
+ p = page_find(start >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ if (!p->code_bitmap &&
+ ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD &&
+ is_cpu_write_access) {
+ /* build code bitmap */
+ build_page_bitmap(p);
+ }
+
+ /* we remove all the TBs in the range [start, end[ */
+ /* XXX: see if in some cases it could be faster to invalidate all the code */
+ current_tb_not_found = is_cpu_write_access;
+ current_tb_modified = 0;
+ current_tb = NULL; /* avoid warning */
+ current_pc = 0; /* avoid warning */
+ current_cs_base = 0; /* avoid warning */
+ current_flags = 0; /* avoid warning */
+ tb = p->first_tb;
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+ tb_next = tb->page_next[n];
+ /* NOTE: this is subtle as a TB may span two physical pages */
+ if (n == 0) {
+ /* NOTE: tb_end may be after the end of the page, but
+ it is not a problem */
+ tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+ tb_end = tb_start + tb->size;
+ } else {
+ tb_start = tb->page_addr[1];
+ tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+ }
+ if (!(tb_end <= start || tb_start >= end)) {
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_not_found) {
+ current_tb_not_found = 0;
+ current_tb = NULL;
+ if (env->mem_write_pc) {
+ /* now we have a real cpu fault */
+ current_tb = tb_find_pc(env->mem_write_pc);
+ }
+ }
+ if (current_tb == tb &&
+ !(current_tb->cflags & CF_SINGLE_INSN)) {
+ /* If we are modifying the current TB, we must stop
+ its execution. We could be more precise by checking
+ that the modification is after the current PC, but it
+ would require a specialized function to partially
+ restore the CPU state */
+
+ current_tb_modified = 1;
+ cpu_restore_state(current_tb, env,
+ env->mem_write_pc, NULL);
+#if defined(TARGET_I386)
+ current_flags = env->hflags;
+ current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+ current_cs_base = (target_ulong)env->segs[R_CS].base;
+ current_pc = current_cs_base + env->eip;
+#else
+#error unsupported CPU
+#endif
+ }
+#endif /* TARGET_HAS_PRECISE_SMC */
+ /* we need to do that to handle the case where a signal
+ occurs while doing tb_phys_invalidate() */
+ saved_tb = NULL;
+ if (env) {
+ saved_tb = env->current_tb;
+ env->current_tb = NULL;
+ }
+ tb_phys_invalidate(tb, -1);
+ if (env) {
+ env->current_tb = saved_tb;
+ if (env->interrupt_request && env->current_tb)
+ cpu_interrupt(env, env->interrupt_request);
+ }
+ }
+ tb = tb_next;
+ }
+#if !defined(CONFIG_USER_ONLY)
+ /* if no code remaining, no need to continue to use slow writes */
+ if (!p->first_tb) {
+ invalidate_page_bitmap(p);
+ if (is_cpu_write_access) {
+ tlb_unprotect_code_phys(env, start, env->mem_write_vaddr);
+ }
+ }
+#endif
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_modified) {
+ /* we generate a block containing just the instruction
+ modifying the memory. It will ensure that it cannot modify
+ itself */
+ env->current_tb = NULL;
+ tb_gen_code(env, current_pc, current_cs_base, current_flags,
+ CF_SINGLE_INSN);
+ cpu_resume_from_signal(env, NULL);
+ }
+#endif
+}
+
+/* len must be <= 8 and start must be a multiple of len */
+static inline void tb_invalidate_phys_page_fast(target_ulong start, int len)
+{
+ PageDesc *p;
+ int offset, b;
+#if 0
+ if (1) {
+ if (loglevel) {
+ fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
+ cpu_single_env->mem_write_vaddr, len,
+ cpu_single_env->eip,
+ cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base);
+ }
+ }
+#endif
+ p = page_find(start >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ if (p->code_bitmap) {
+ offset = start & ~TARGET_PAGE_MASK;
+ b = p->code_bitmap[offset >> 3] >> (offset & 7);
+ if (b & ((1 << len) - 1))
+ goto do_invalidate;
+ } else {
+ do_invalidate:
+ tb_invalidate_phys_page_range(start, start + len, 1);
+ }
+}
+
+#if !defined(CONFIG_SOFTMMU)
+static void tb_invalidate_phys_page(target_ulong addr,
+ unsigned long pc, void *puc)
+{
+ int n, current_flags, current_tb_modified;
+ target_ulong current_pc, current_cs_base;
+ PageDesc *p;
+ TranslationBlock *tb, *current_tb;
+#ifdef TARGET_HAS_PRECISE_SMC
+ CPUState *env = cpu_single_env;
+#endif
+
+ addr &= TARGET_PAGE_MASK;
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ tb = p->first_tb;
+ current_tb_modified = 0;
+ current_tb = NULL;
+ current_pc = 0; /* avoid warning */
+ current_cs_base = 0; /* avoid warning */
+ current_flags = 0; /* avoid warning */
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (tb && pc != 0) {
+ current_tb = tb_find_pc(pc);
+ }
+#endif
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb == tb &&
+ !(current_tb->cflags & CF_SINGLE_INSN)) {
+ /* If we are modifying the current TB, we must stop
+ its execution. We could be more precise by checking
+ that the modification is after the current PC, but it
+ would require a specialized function to partially
+ restore the CPU state */
+
+ current_tb_modified = 1;
+ cpu_restore_state(current_tb, env, pc, puc);
+#if defined(TARGET_I386)
+ current_flags = env->hflags;
+ current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+ current_cs_base = (target_ulong)env->segs[R_CS].base;
+ current_pc = current_cs_base + env->eip;
+#else
+#error unsupported CPU
+#endif
+ }
+#endif /* TARGET_HAS_PRECISE_SMC */
+ tb_phys_invalidate(tb, addr);
+ tb = tb->page_next[n];
+ }
+ p->first_tb = NULL;
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_modified) {
+ /* we generate a block containing just the instruction
+ modifying the memory. It will ensure that it cannot modify
+ itself */
+ env->current_tb = NULL;
+ tb_gen_code(env, current_pc, current_cs_base, current_flags,
+ CF_SINGLE_INSN);
+ cpu_resume_from_signal(env, puc);
+ }
+#endif
+}
+#endif
+
+/* add the tb in the target page and protect it if necessary */
+static inline void tb_alloc_page(TranslationBlock *tb,
+ unsigned int n, target_ulong page_addr)
+{
+ PageDesc *p;
+ TranslationBlock *last_first_tb;
+
+ tb->page_addr[n] = page_addr;
+ p = page_find_alloc(page_addr >> TARGET_PAGE_BITS);
+ tb->page_next[n] = p->first_tb;
+ last_first_tb = p->first_tb;
+ p->first_tb = (TranslationBlock *)((long)tb | n);
+ invalidate_page_bitmap(p);
+
+#if defined(TARGET_HAS_SMC) || 1
+
+#if defined(CONFIG_USER_ONLY)
+ if (p->flags & PAGE_WRITE) {
+ target_ulong addr;
+ PageDesc *p2;
+ int prot;
+
+ /* force the host page as non writable (writes will have a
+ page fault + mprotect overhead) */
+ page_addr &= qemu_host_page_mask;
+ prot = 0;
+ for(addr = page_addr; addr < page_addr + qemu_host_page_size;
+ addr += TARGET_PAGE_SIZE) {
+
+ p2 = page_find (addr >> TARGET_PAGE_BITS);
+ if (!p2)
+ continue;
+ prot |= p2->flags;
+ p2->flags &= ~PAGE_WRITE;
+ page_get_flags(addr);
+ }
+ mprotect(g2h(page_addr), qemu_host_page_size,
+ (prot & PAGE_BITS) & ~PAGE_WRITE);
+#ifdef DEBUG_TB_INVALIDATE
+ printf("protecting code page: 0x%08lx\n",
+ page_addr);
+#endif
+ }
+#else
+ /* if some code is already present, then the pages are already
+ protected. So we handle the case where only the first TB is
+ allocated in a physical page */
+ if (!last_first_tb) {
+ tlb_protect_code(page_addr);
+ }
+#endif
+
+#endif /* TARGET_HAS_SMC */
+}
+
+/* Allocate a new translation block. Flush the translation buffer if
+ too many translation blocks or too much generated code. */
+TranslationBlock *tb_alloc(target_ulong pc)
+{
+ TranslationBlock *tb;
+
+ if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
+ (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
+ return NULL;
+ tb = &tbs[nb_tbs++];
+ tb->pc = pc;
+ tb->cflags = 0;
+ return tb;
+}
+
+/* add a new TB and link it to the physical page tables. phys_page2 is
+ (-1) to indicate that only one page contains the TB. */
+void tb_link_phys(TranslationBlock *tb,
+ target_ulong phys_pc, target_ulong phys_page2)
+{
+ unsigned int h;
+ TranslationBlock **ptb;
+
+ /* add in the physical hash table */
+ h = tb_phys_hash_func(phys_pc);
+ ptb = &tb_phys_hash[h];
+ tb->phys_hash_next = *ptb;
+ *ptb = tb;
+
+ /* add in the page list */
+ tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK);
+ if (phys_page2 != -1)
+ tb_alloc_page(tb, 1, phys_page2);
+ else
+ tb->page_addr[1] = -1;
+
+ tb->jmp_first = (TranslationBlock *)((long)tb | 2);
+ tb->jmp_next[0] = NULL;
+ tb->jmp_next[1] = NULL;
+#ifdef USE_CODE_COPY
+ tb->cflags &= ~CF_FP_USED;
+ if (tb->cflags & CF_TB_FP_USED)
+ tb->cflags |= CF_FP_USED;
+#endif
+
+ /* init original jump addresses */
+ if (tb->tb_next_offset[0] != 0xffff)
+ tb_reset_jump(tb, 0);
+ if (tb->tb_next_offset[1] != 0xffff)
+ tb_reset_jump(tb, 1);
+
+#ifdef DEBUG_TB_CHECK
+ tb_page_check();
+#endif
+}
+
+/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
+ tb[1].tc_ptr. Return NULL if not found */
+TranslationBlock *tb_find_pc(unsigned long tc_ptr)
+{
+ int m_min, m_max, m;
+ unsigned long v;
+ TranslationBlock *tb;
+
+ if (nb_tbs <= 0)
+ return NULL;
+ if (tc_ptr < (unsigned long)code_gen_buffer ||
+ tc_ptr >= (unsigned long)code_gen_ptr)
+ return NULL;
+ /* binary search (cf Knuth) */
+ m_min = 0;
+ m_max = nb_tbs - 1;
+ while (m_min <= m_max) {
+ m = (m_min + m_max) >> 1;
+ tb = &tbs[m];
+ v = (unsigned long)tb->tc_ptr;
+ if (v == tc_ptr)
+ return tb;
+ else if (tc_ptr < v) {
+ m_max = m - 1;
+ } else {
+ m_min = m + 1;
+ }
+ }
+ return &tbs[m_max];
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb);
+
+static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n)
+{
+ TranslationBlock *tb1, *tb_next, **ptb;
+ unsigned int n1;
+
+ tb1 = tb->jmp_next[n];
+ if (tb1 != NULL) {
+ /* find head of list */
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == 2)
+ break;
+ tb1 = tb1->jmp_next[n1];
+ }
+ /* we are now sure now that tb jumps to tb1 */
+ tb_next = tb1;
+
+ /* remove tb from the jmp_first list */
+ ptb = &tb_next->jmp_first;
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == n && tb1 == tb)
+ break;
+ ptb = &tb1->jmp_next[n1];
+ }
+ *ptb = tb->jmp_next[n];
+ tb->jmp_next[n] = NULL;
+
+ /* suppress the jump to next tb in generated code */
+ tb_reset_jump(tb, n);
+
+ /* suppress jumps in the tb on which we could have jumped */
+ tb_reset_jump_recursive(tb_next);
+ }
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb)
+{
+ tb_reset_jump_recursive2(tb, 0);
+ tb_reset_jump_recursive2(tb, 1);
+}
+
+#if defined(TARGET_HAS_ICE)
+static void breakpoint_invalidate(CPUState *env, target_ulong pc)
+{
+ target_ulong addr, pd;
+ ram_addr_t ram_addr;
+ PhysPageDesc *p;
+
+ addr = cpu_get_phys_page_debug(env, pc);
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+ ram_addr = (pd & TARGET_PAGE_MASK) | (pc & ~TARGET_PAGE_MASK);
+ tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0);
+}
+#endif
+
+/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
+ breakpoint is reached */
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+ int i;
+
+ for(i = 0; i < env->nb_breakpoints; i++) {
+ if (env->breakpoints[i] == pc)
+ return 0;
+ }
+
+ if (env->nb_breakpoints >= MAX_BREAKPOINTS)
+ return -1;
+ env->breakpoints[env->nb_breakpoints++] = pc;
+
+ breakpoint_invalidate(env, pc);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* remove a breakpoint */
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+ int i;
+ for(i = 0; i < env->nb_breakpoints; i++) {
+ if (env->breakpoints[i] == pc)
+ goto found;
+ }
+ return -1;
+ found:
+ env->nb_breakpoints--;
+ if (i < env->nb_breakpoints)
+ env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
+
+ breakpoint_invalidate(env, pc);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* enable or disable single step mode. EXCP_DEBUG is returned by the
+ CPU loop after each instruction */
+void cpu_single_step(CPUState *env, int enabled)
+{
+#if defined(TARGET_HAS_ICE)
+ if (env->singlestep_enabled != enabled) {
+ env->singlestep_enabled = enabled;
+ /* must flush all the translated code to avoid inconsistancies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
+#endif
+}
+
+/* enable or disable low levels log */
+void cpu_set_log(int log_flags)
+{
+ loglevel = log_flags;
+ if (loglevel && !logfile) {
+ logfile = fopen(logfilename, "w");
+ if (!logfile) {
+ perror(logfilename);
+ _exit(1);
+ }
+#if !defined(CONFIG_SOFTMMU)
+ /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
+ {
+ static uint8_t logfile_buf[4096];
+ setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
+ }
+#else
+ setvbuf(logfile, NULL, _IOLBF, 0);
+#endif
+ }
+}
+
+void cpu_set_log_filename(const char *filename)
+{
+ logfilename = strdup(filename);
+}
+
+/* mask must never be zero, except for A20 change call */
+void cpu_interrupt(CPUState *env, int mask)
+{
+ TranslationBlock *tb;
+ static int interrupt_lock;
+
+ env->interrupt_request |= mask;
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ tb = env->current_tb;
+ if (tb && !testandset(&interrupt_lock)) {
+ env->current_tb = NULL;
+ tb_reset_jump_recursive(tb);
+ interrupt_lock = 0;
+ }
+}
+
+void cpu_reset_interrupt(CPUState *env, int mask)
+{
+ env->interrupt_request &= ~mask;
+}
+
+CPULogItem cpu_log_items[] = {
+ { CPU_LOG_TB_OUT_ASM, "out_asm",
+ "show generated host assembly code for each compiled TB" },
+ { CPU_LOG_TB_IN_ASM, "in_asm",
+ "show target assembly code for each compiled TB" },
+ { CPU_LOG_TB_OP, "op",
+ "show micro ops for each compiled TB (only usable if 'in_asm' used)" },
+#ifdef TARGET_I386
+ { CPU_LOG_TB_OP_OPT, "op_opt",
+ "show micro ops after optimization for each compiled TB" },
+#endif
+ { CPU_LOG_INT, "int",
+ "show interrupts/exceptions in short format" },
+ { CPU_LOG_EXEC, "exec",
+ "show trace before each executed TB (lots of logs)" },
+ { CPU_LOG_TB_CPU, "cpu",
+ "show CPU state before bloc translation" },
+#ifdef TARGET_I386
+ { CPU_LOG_PCALL, "pcall",
+ "show protected mode far calls/returns/exceptions" },
+#endif
+#ifdef DEBUG_IOPORT
+ { CPU_LOG_IOPORT, "ioport",
+ "show all i/o ports accesses" },
+#endif
+ { 0, NULL, NULL },
+};
+
+static int cmp1(const char *s1, int n, const char *s2)
+{
+ if (strlen(s2) != n)
+ return 0;
+ return memcmp(s1, s2, n) == 0;
+}
+
+/* takes a comma separated list of log masks. Return 0 if error. */
+int cpu_str_to_log_mask(const char *str)
+{
+ CPULogItem *item;
+ int mask;
+ const char *p, *p1;
+
+ p = str;
+ mask = 0;
+ for(;;) {
+ p1 = strchr(p, ',');
+ if (!p1)
+ p1 = p + strlen(p);
+ if(cmp1(p,p1-p,"all")) {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ mask |= item->mask;
+ }
+ } else {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ if (cmp1(p, p1 - p, item->name))
+ goto found;
+ }
+ return 0;
+ }
+ found:
+ mask |= item->mask;
+ if (*p1 != ',')
+ break;
+ p = p1 + 1;
+ }
+ return mask;
+}
+
+void cpu_abort(CPUState *env, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu: fatal: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+#ifdef TARGET_I386
+ cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+ cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+ va_end(ap);
+ abort();
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* NOTE: if flush_global is true, also flush global entries (not
+ implemented yet) */
+void tlb_flush(CPUState *env, int flush_global)
+{
+ int i;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush:\n");
+#endif
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ for(i = 0; i < CPU_TLB_SIZE; i++) {
+ env->tlb_table[0][i].addr_read = -1;
+ env->tlb_table[0][i].addr_write = -1;
+ env->tlb_table[0][i].addr_code = -1;
+ env->tlb_table[1][i].addr_read = -1;
+ env->tlb_table[1][i].addr_write = -1;
+ env->tlb_table[1][i].addr_code = -1;
+ }
+
+ memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+
+#if !defined(CONFIG_SOFTMMU)
+ munmap((void *)MMAP_AREA_START, MMAP_AREA_END - MMAP_AREA_START);
+#endif
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled) {
+ kqemu_flush(env, flush_global);
+ }
+#endif
+ tlb_flush_count++;
+}
+
+static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
+{
+ if (addr == (tlb_entry->addr_read &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+ addr == (tlb_entry->addr_write &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+ addr == (tlb_entry->addr_code &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ tlb_entry->addr_read = -1;
+ tlb_entry->addr_write = -1;
+ tlb_entry->addr_code = -1;
+ }
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+ int i;
+ TranslationBlock *tb;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr);
+#endif
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_flush_entry(&env->tlb_table[0][i], addr);
+ tlb_flush_entry(&env->tlb_table[1][i], addr);
+
+ for(i = 0; i < TB_JMP_CACHE_SIZE; i++) {
+ tb = env->tb_jmp_cache[i];
+ if (tb &&
+ ((tb->pc & TARGET_PAGE_MASK) == addr ||
+ ((tb->pc + tb->size - 1) & TARGET_PAGE_MASK) == addr)) {
+ env->tb_jmp_cache[i] = NULL;
+ }
+ }
+
+#if !defined(CONFIG_SOFTMMU)
+ if (addr < MMAP_AREA_END)
+ munmap((void *)addr, TARGET_PAGE_SIZE);
+#endif
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled) {
+ kqemu_flush_page(env, addr);
+ }
+#endif
+}
+
+/* update the TLBs so that writes to code in the virtual page 'addr'
+ can be detected */
+static void tlb_protect_code(ram_addr_t ram_addr)
+{
+ cpu_physical_memory_reset_dirty(ram_addr,
+ ram_addr + TARGET_PAGE_SIZE,
+ CODE_DIRTY_FLAG);
+}
+
+/* update the TLB so that writes in physical page 'phys_addr' are no longer
+ tested for self modifying code */
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+ target_ulong vaddr)
+{
+ phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG;
+}
+
+static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry,
+ unsigned long start, unsigned long length)
+{
+ unsigned long addr;
+ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+ addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend;
+ if ((addr - start) < length) {
+ tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY;
+ }
+ }
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+ int dirty_flags)
+{
+ CPUState *env;
+ unsigned long length, start1;
+ int i, mask, len;
+ uint8_t *p;
+
+ start &= TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+
+ length = end - start;
+ if (length == 0)
+ return;
+ len = length >> TARGET_PAGE_BITS;
+#ifdef USE_KQEMU
+ /* XXX: should not depend on cpu context */
+ env = first_cpu;
+ if (env->kqemu_enabled) {
+ ram_addr_t addr;
+ addr = start;
+ for(i = 0; i < len; i++) {
+ kqemu_set_notdirty(env, addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+ }
+#endif
+ mask = ~dirty_flags;
+ p = phys_ram_dirty + (start >> TARGET_PAGE_BITS);
+ for(i = 0; i < len; i++)
+ p[i] &= mask;
+
+ /* we modify the TLB cache so that the dirty bit will be set again
+ when accessing the range */
+ start1 = start + (unsigned long)phys_ram_base;
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_reset_dirty_range(&env->tlb_table[0][i], start1, length);
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_reset_dirty_range(&env->tlb_table[1][i], start1, length);
+ }
+
+#if !defined(CONFIG_SOFTMMU)
+ /* XXX: this is expensive */
+ {
+ VirtPageDesc *p;
+ int j;
+ target_ulong addr;
+
+ for(i = 0; i < L1_SIZE; i++) {
+ p = l1_virt_map[i];
+ if (p) {
+ addr = i << (TARGET_PAGE_BITS + L2_BITS);
+ for(j = 0; j < L2_SIZE; j++) {
+ if (p->valid_tag == virt_valid_tag &&
+ p->phys_addr >= start && p->phys_addr < end &&
+ (p->prot & PROT_WRITE)) {
+ if (addr < MMAP_AREA_END) {
+ mprotect((void *)addr, TARGET_PAGE_SIZE,
+ p->prot & ~PROT_WRITE);
+ }
+ }
+ addr += TARGET_PAGE_SIZE;
+ p++;
+ }
+ }
+ }
+ }
+#endif
+}
+
+static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
+{
+ ram_addr_t ram_addr;
+
+ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+ ram_addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) +
+ tlb_entry->addend - (unsigned long)phys_ram_base;
+ if (!cpu_physical_memory_is_dirty(ram_addr)) {
+ tlb_entry->addr_write |= IO_MEM_NOTDIRTY;
+ }
+ }
+}
+
+/* update the TLB according to the current state of the dirty bits */
+void cpu_tlb_update_dirty(CPUState *env)
+{
+ int i;
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_update_dirty(&env->tlb_table[0][i]);
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_update_dirty(&env->tlb_table[1][i]);
+}
+
+static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry,
+ unsigned long start)
+{
+ unsigned long addr;
+ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) {
+ addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend;
+ if (addr == start) {
+ tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_RAM;
+ }
+ }
+}
+
+/* update the TLB corresponding to virtual page vaddr and phys addr
+ addr so that it is no longer dirty */
+static inline void tlb_set_dirty(CPUState *env,
+ unsigned long addr, target_ulong vaddr)
+{
+ int i;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_set_dirty1(&env->tlb_table[0][i], addr);
+ tlb_set_dirty1(&env->tlb_table[1][i], addr);
+}
+
+/* add a new TLB entry. At most one entry for a given virtual address
+ is permitted. Return 0 if OK or 2 if the page could not be mapped
+ (can only happen in non SOFTMMU mode for I/O pages or pages
+ conflicting with the host address space). */
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu)
+{
+ PhysPageDesc *p;
+ unsigned long pd;
+ unsigned int index;
+ target_ulong address;
+ target_phys_addr_t addend;
+ int ret;
+ CPUTLBEntry *te;
+
+ p = phys_page_find(paddr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+#if defined(DEBUG_TLB)
+ printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x u=%d smmu=%d pd=0x%08lx\n",
+ vaddr, (int)paddr, prot, is_user, is_softmmu, pd);
+#endif
+
+ ret = 0;
+#if !defined(CONFIG_SOFTMMU)
+ if (is_softmmu)
+#endif
+ {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+ /* IO memory case */
+ address = vaddr | pd;
+ addend = paddr;
+ } else {
+ /* standard memory */
+ address = vaddr;
+ addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK);
+ }
+
+ index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ addend -= vaddr;
+ te = &env->tlb_table[is_user][index];
+ te->addend = addend;
+ if (prot & PAGE_READ) {
+ te->addr_read = address;
+ } else {
+ te->addr_read = -1;
+ }
+ if (prot & PAGE_EXEC) {
+ te->addr_code = address;
+ } else {
+ te->addr_code = -1;
+ }
+ if (prot & PAGE_WRITE) {
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM ||
+ (pd & IO_MEM_ROMD)) {
+ /* write access calls the I/O callback */
+ te->addr_write = vaddr |
+ (pd & ~(TARGET_PAGE_MASK | IO_MEM_ROMD));
+ } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+ !cpu_physical_memory_is_dirty(pd)) {
+ te->addr_write = vaddr | IO_MEM_NOTDIRTY;
+ } else {
+ te->addr_write = address;
+ }
+ } else {
+ te->addr_write = -1;
+ }
+ }
+#if !defined(CONFIG_SOFTMMU)
+ else {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) {
+ /* IO access: no mapping is done as it will be handled by the
+ soft MMU */
+ if (!(env->hflags & HF_SOFTMMU_MASK))
+ ret = 2;
+ } else {
+ void *map_addr;
+
+ if (vaddr >= MMAP_AREA_END) {
+ ret = 2;
+ } else {
+ if (prot & PROT_WRITE) {
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM ||
+#if defined(TARGET_HAS_SMC) || 1
+ first_tb ||
+#endif
+ ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+ !cpu_physical_memory_is_dirty(pd))) {
+ /* ROM: we do as if code was inside */
+ /* if code is present, we only map as read only and save the
+ original mapping */
+ VirtPageDesc *vp;
+
+ vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS, 1);
+ vp->phys_addr = pd;
+ vp->prot = prot;
+ vp->valid_tag = virt_valid_tag;
+ prot &= ~PAGE_WRITE;
+ }
+ }
+ map_addr = mmap((void *)vaddr, TARGET_PAGE_SIZE, prot,
+ MAP_SHARED | MAP_FIXED, phys_ram_fd, (pd & TARGET_PAGE_MASK));
+ if (map_addr == MAP_FAILED) {
+ cpu_abort(env, "mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n",
+ paddr, vaddr);
+ }
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+ page. Return TRUE if the fault was succesfully handled. */
+int page_unprotect(target_ulong addr, unsigned long pc, void *puc)
+{
+#if !defined(CONFIG_SOFTMMU)
+ VirtPageDesc *vp;
+
+#if defined(DEBUG_TLB)
+ printf("page_unprotect: addr=0x%08x\n", addr);
+#endif
+ addr &= TARGET_PAGE_MASK;
+
+ /* if it is not mapped, no need to worry here */
+ if (addr >= MMAP_AREA_END)
+ return 0;
+ vp = virt_page_find(addr >> TARGET_PAGE_BITS);
+ if (!vp)
+ return 0;
+ /* NOTE: in this case, validate_tag is _not_ tested as it
+ validates only the code TLB */
+ if (vp->valid_tag != virt_valid_tag)
+ return 0;
+ if (!(vp->prot & PAGE_WRITE))
+ return 0;
+#if defined(DEBUG_TLB)
+ printf("page_unprotect: addr=0x%08x phys_addr=0x%08x prot=%x\n",
+ addr, vp->phys_addr, vp->prot);
+#endif
+ if (mprotect((void *)addr, TARGET_PAGE_SIZE, vp->prot) < 0)
+ cpu_abort(cpu_single_env, "error mprotect addr=0x%lx prot=%d\n",
+ (unsigned long)addr, vp->prot);
+ /* set the dirty bit */
+ phys_ram_dirty[vp->phys_addr >> TARGET_PAGE_BITS] = 0xff;
+ /* flush the code inside */
+ tb_invalidate_phys_page(vp->phys_addr, pc, puc);
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+#else
+
+void tlb_flush(CPUState *env, int flush_global)
+{
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+}
+
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu)
+{
+ return 0;
+}
+
+/* dump memory mappings */
+void page_dump(FILE *f)
+{
+ unsigned long start, end;
+ int i, j, prot, prot1;
+ PageDesc *p;
+
+ fprintf(f, "%-8s %-8s %-8s %s\n",
+ "start", "end", "size", "prot");
+ start = -1;
+ end = -1;
+ prot = 0;
+ for(i = 0; i <= L1_SIZE; i++) {
+ if (i < L1_SIZE)
+ p = l1_map[i];
+ else
+ p = NULL;
+ for(j = 0;j < L2_SIZE; j++) {
+ if (!p)
+ prot1 = 0;
+ else
+ prot1 = p[j].flags;
+ if (prot1 != prot) {
+ end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
+ if (start != -1) {
+ fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
+ start, end, end - start,
+ prot & PAGE_READ ? 'r' : '-',
+ prot & PAGE_WRITE ? 'w' : '-',
+ prot & PAGE_EXEC ? 'x' : '-');
+ }
+ if (prot1 != 0)
+ start = end;
+ else
+ start = -1;
+ prot = prot1;
+ }
+ if (!p)
+ break;
+ }
+ }
+}
+
+int page_get_flags(target_ulong address)
+{
+ PageDesc *p;
+
+ p = page_find(address >> TARGET_PAGE_BITS);
+ if (!p)
+ return 0;
+ return p->flags;
+}
+
+/* modify the flags of a page and invalidate the code if
+ necessary. The flag PAGE_WRITE_ORG is positionned automatically
+ depending on PAGE_WRITE */
+void page_set_flags(target_ulong start, target_ulong end, int flags)
+{
+ PageDesc *p;
+ target_ulong addr;
+
+ start = start & TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+ if (flags & PAGE_WRITE)
+ flags |= PAGE_WRITE_ORG;
+ spin_lock(&tb_lock);
+ for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+ p = page_find_alloc(addr >> TARGET_PAGE_BITS);
+ /* if the write protection is set, then we invalidate the code
+ inside */
+ if (!(p->flags & PAGE_WRITE) &&
+ (flags & PAGE_WRITE) &&
+ p->first_tb) {
+ tb_invalidate_phys_page(addr, 0, NULL);
+ }
+ p->flags = flags;
+ }
+ spin_unlock(&tb_lock);
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+ page. Return TRUE if the fault was succesfully handled. */
+int page_unprotect(target_ulong address, unsigned long pc, void *puc)
+{
+ unsigned int page_index, prot, pindex;
+ PageDesc *p, *p1;
+ target_ulong host_start, host_end, addr;
+
+ host_start = address & qemu_host_page_mask;
+ page_index = host_start >> TARGET_PAGE_BITS;
+ p1 = page_find(page_index);
+ if (!p1)
+ return 0;
+ host_end = host_start + qemu_host_page_size;
+ p = p1;
+ prot = 0;
+ for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot |= p->flags;
+ p++;
+ }
+ /* if the page was really writable, then we change its
+ protection back to writable */
+ if (prot & PAGE_WRITE_ORG) {
+ pindex = (address - host_start) >> TARGET_PAGE_BITS;
+ if (!(p1[pindex].flags & PAGE_WRITE)) {
+ mprotect((void *)g2h(host_start), qemu_host_page_size,
+ (prot & PAGE_BITS) | PAGE_WRITE);
+ p1[pindex].flags |= PAGE_WRITE;
+ /* and since the content will be modified, we must invalidate
+ the corresponding translated code. */
+ tb_invalidate_phys_page(address, pc, puc);
+#ifdef DEBUG_TB_CHECK
+ tb_invalidate_check(address);
+#endif
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* call this function when system calls directly modify a memory area */
+/* ??? This should be redundant now we have lock_user. */
+void page_unprotect_range(target_ulong data, target_ulong data_size)
+{
+ target_ulong start, end, addr;
+
+ start = data;
+ end = start + data_size;
+ start &= TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+ for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+ page_unprotect(addr, 0, NULL);
+ }
+}
+
+static inline void tlb_set_dirty(CPUState *env,
+ unsigned long addr, target_ulong vaddr)
+{
+}
+#endif /* defined(CONFIG_USER_ONLY) */
+
+/* register physical memory. 'size' must be a multiple of the target
+ page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
+ io memory page */
+void cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset)
+{
+ target_phys_addr_t addr, end_addr;
+ PhysPageDesc *p;
+ CPUState *env;
+
+ size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+ end_addr = start_addr + size;
+ for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
+ p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
+ p->phys_offset = phys_offset;
+ if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
+ (phys_offset & IO_MEM_ROMD))
+ phys_offset += TARGET_PAGE_SIZE;
+ }
+
+ /* since each CPU stores ram addresses in its TLB cache, we must
+ reset the modified entries */
+ /* XXX: slow ! */
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ tlb_flush(env, 1);
+ }
+}
+
+static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static CPUReadMemoryFunc *unassigned_mem_read[3] = {
+ unassigned_mem_readb,
+ unassigned_mem_readb,
+ unassigned_mem_readb,
+};
+
+static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
+ unassigned_mem_writeb,
+ unassigned_mem_writeb,
+ unassigned_mem_writeb,
+};
+
+static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ unsigned long ram_addr;
+ int dirty_flags;
+ ram_addr = addr - (unsigned long)phys_ram_base;
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 1);
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+ }
+ stb_p((uint8_t *)(long)addr, val);
+#ifdef USE_KQEMU
+ if (cpu_single_env->kqemu_enabled &&
+ (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+ kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr);
+}
+
+static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ unsigned long ram_addr;
+ int dirty_flags;
+ ram_addr = addr - (unsigned long)phys_ram_base;
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 2);
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+ }
+ stw_p((uint8_t *)(long)addr, val);
+#ifdef USE_KQEMU
+ if (cpu_single_env->kqemu_enabled &&
+ (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+ kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr);
+}
+
+static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ unsigned long ram_addr;
+ int dirty_flags;
+ ram_addr = addr - (unsigned long)phys_ram_base;
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 4);
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+ }
+ stl_p((uint8_t *)(long)addr, val);
+#ifdef USE_KQEMU
+ if (cpu_single_env->kqemu_enabled &&
+ (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+ kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr);
+}
+
+static CPUReadMemoryFunc *error_mem_read[3] = {
+ NULL, /* never used */
+ NULL, /* never used */
+ NULL, /* never used */
+};
+
+static CPUWriteMemoryFunc *notdirty_mem_write[3] = {
+ notdirty_mem_writeb,
+ notdirty_mem_writew,
+ notdirty_mem_writel,
+};
+
+static void io_mem_init(void)
+{
+ cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
+ cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write, NULL);
+ cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL);
+ io_mem_nb = 5;
+
+ /* alloc dirty bits array */
+ phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS);
+ memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS);
+}
+
+/* mem_read and mem_write are arrays of functions containing the
+ function to access byte (index 0), word (index 1) and dword (index
+ 2). All functions must be supplied. If io_index is non zero, the
+ corresponding io zone is modified. If it is zero, a new io zone is
+ allocated. The return value can be used with
+ cpu_register_physical_memory(). (-1) is returned if error. */
+int cpu_register_io_memory(int io_index,
+ CPUReadMemoryFunc **mem_read,
+ CPUWriteMemoryFunc **mem_write,
+ void *opaque)
+{
+ int i;
+
+ if (io_index <= 0) {
+ if (io_mem_nb >= IO_MEM_NB_ENTRIES)
+ return -1;
+ io_index = io_mem_nb++;
+ } else {
+ if (io_index >= IO_MEM_NB_ENTRIES)
+ return -1;
+ }
+
+ for(i = 0;i < 3; i++) {
+ io_mem_read[io_index][i] = mem_read[i];
+ io_mem_write[io_index][i] = mem_write[i];
+ }
+ io_mem_opaque[io_index] = opaque;
+ return io_index << IO_MEM_SHIFT;
+}
+
+CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index)
+{
+ return io_mem_write[io_index >> IO_MEM_SHIFT];
+}
+
+CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index)
+{
+ return io_mem_read[io_index >> IO_MEM_SHIFT];
+}
+
+/* physical memory access (slow version, mainly for debug) */
+#if defined(CONFIG_USER_ONLY)
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+ int len, int is_write)
+{
+ int l, flags;
+ target_ulong page;
+ void * p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ flags = page_get_flags(page);
+ if (!(flags & PAGE_VALID))
+ return;
+ if (is_write) {
+ if (!(flags & PAGE_WRITE))
+ return;
+ p = lock_user(addr, len, 0);
+ memcpy(p, buf, len);
+ unlock_user(p, addr, len);
+ } else {
+ if (!(flags & PAGE_READ))
+ return;
+ p = lock_user(addr, len, 1);
+ memcpy(buf, p, len);
+ unlock_user(p, addr, 0);
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+#else
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+ int len, int is_write)
+{
+ int l, io_index;
+ uint8_t *ptr;
+ uint32_t val;
+ target_phys_addr_t page;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ p = phys_page_find(page >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if (is_write) {
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ /* XXX: could force cpu_single_env to NULL to avoid
+ potential bugs */
+ if (l >= 4 && ((addr & 3) == 0)) {
+ /* 32 bit write access */
+ val = ldl_p(buf);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ l = 4;
+ } else if (l >= 2 && ((addr & 1) == 0)) {
+ /* 16 bit write access */
+ val = lduw_p(buf);
+ io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val);
+ l = 2;
+ } else {
+ /* 8 bit write access */
+ val = ldub_p(buf);
+ io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val);
+ l = 1;
+ }
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = phys_ram_base + addr1;
+ memcpy(ptr, buf, l);
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + l, 0);
+ /* set dirty bit */
+ phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+ (0xff & ~CODE_DIRTY_FLAG);
+ }
+ }
+ } else {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (l >= 4 && ((addr & 3) == 0)) {
+ /* 32 bit read access */
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+ stl_p(buf, val);
+ l = 4;
+ } else if (l >= 2 && ((addr & 1) == 0)) {
+ /* 16 bit read access */
+ val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr);
+ stw_p(buf, val);
+ l = 2;
+ } else {
+ /* 8 bit read access */
+ val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr);
+ stb_p(buf, val);
+ l = 1;
+ }
+ } else {
+ /* RAM case */
+ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ memcpy(buf, ptr, l);
+ }
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+/* used for ROM loading : can write in RAM and ROM */
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+ const uint8_t *buf, int len)
+{
+ int l;
+ uint8_t *ptr;
+ target_phys_addr_t page;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ p = phys_page_find(page >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM &&
+ (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* do nothing */
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* ROM/RAM case */
+ ptr = phys_ram_base + addr1;
+ memcpy(ptr, buf, l);
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+
+/* warning: addr must be aligned */
+uint32_t ldl_phys(target_phys_addr_t addr)
+{
+ int io_index;
+ uint8_t *ptr;
+ uint32_t val;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+ } else {
+ /* RAM case */
+ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ val = ldl_p(ptr);
+ }
+ return val;
+}
+
+/* warning: addr must be aligned */
+uint64_t ldq_phys(target_phys_addr_t addr)
+{
+ int io_index;
+ uint8_t *ptr;
+ uint64_t val;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32;
+ val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4);
+#else
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+ val |= (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4) << 32;
+#endif
+ } else {
+ /* RAM case */
+ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ val = ldq_p(ptr);
+ }
+ return val;
+}
+
+/* XXX: optimize */
+uint32_t ldub_phys(target_phys_addr_t addr)
+{
+ uint8_t val;
+ cpu_physical_memory_read(addr, &val, 1);
+ return val;
+}
+
+/* XXX: optimize */
+uint32_t lduw_phys(target_phys_addr_t addr)
+{
+ uint16_t val;
+ cpu_physical_memory_read(addr, (uint8_t *)&val, 2);
+ return tswap16(val);
+}
+
+/* warning: addr must be aligned. The ram page is not masked as dirty
+ and the code inside is not invalidated. It is useful if the dirty
+ bits are used to track modified PTEs */
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ } else {
+ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ stl_p(ptr, val);
+ }
+}
+
+/* warning: addr must be aligned */
+void stl_phys(target_phys_addr_t addr, uint32_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = phys_ram_base + addr1;
+ stl_p(ptr, val);
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+ /* set dirty bit */
+ phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+ (0xff & ~CODE_DIRTY_FLAG);
+ }
+ }
+}
+
+/* XXX: optimize */
+void stb_phys(target_phys_addr_t addr, uint32_t val)
+{
+ uint8_t v = val;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+/* XXX: optimize */
+void stw_phys(target_phys_addr_t addr, uint32_t val)
+{
+ uint16_t v = tswap16(val);
+ cpu_physical_memory_write(addr, (const uint8_t *)&v, 2);
+}
+
+/* XXX: optimize */
+void stq_phys(target_phys_addr_t addr, uint64_t val)
+{
+ val = tswap64(val);
+ cpu_physical_memory_write(addr, (const uint8_t *)&val, 8);
+}
+
+#endif
+
+/* virtual memory access for debug */
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+ uint8_t *buf, int len, int is_write)
+{
+ int l;
+ target_ulong page, phys_addr;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ phys_addr = cpu_get_phys_page_debug(env, page);
+ /* if no physical page mapped, return an error */
+ if (phys_addr == -1)
+ return -1;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK),
+ buf, l, is_write);
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+ return 0;
+}
+
+void dump_exec_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+ int i, target_code_size, max_target_code_size;
+ int direct_jmp_count, direct_jmp2_count, cross_page;
+ TranslationBlock *tb;
+
+ target_code_size = 0;
+ max_target_code_size = 0;
+ cross_page = 0;
+ direct_jmp_count = 0;
+ direct_jmp2_count = 0;
+ for(i = 0; i < nb_tbs; i++) {
+ tb = &tbs[i];
+ target_code_size += tb->size;
+ if (tb->size > max_target_code_size)
+ max_target_code_size = tb->size;
+ if (tb->page_addr[1] != -1)
+ cross_page++;
+ if (tb->tb_next_offset[0] != 0xffff) {
+ direct_jmp_count++;
+ if (tb->tb_next_offset[1] != 0xffff) {
+ direct_jmp2_count++;
+ }
+ }
+ }
+ /* XXX: avoid using doubles ? */
+ cpu_fprintf(f, "TB count %d\n", nb_tbs);
+ cpu_fprintf(f, "TB avg target size %d max=%d bytes\n",
+ nb_tbs ? target_code_size / nb_tbs : 0,
+ max_target_code_size);
+ cpu_fprintf(f, "TB avg host size %d bytes (expansion ratio: %0.1f)\n",
+ nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0,
+ target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0);
+ cpu_fprintf(f, "cross page TB count %d (%d%%)\n",
+ cross_page,
+ nb_tbs ? (cross_page * 100) / nb_tbs : 0);
+ cpu_fprintf(f, "direct jump count %d (%d%%) (2 jumps=%d %d%%)\n",
+ direct_jmp_count,
+ nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0,
+ direct_jmp2_count,
+ nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0);
+ cpu_fprintf(f, "TB flush count %d\n", tb_flush_count);
+ cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count);
+ cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _cmmu
+#define GETPC() NULL
+#define env cpu_single_env
+#define SOFTMMU_CODE_ACCESS
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#undef env
+
+#endif
diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h
new file mode 100644
index 0000000..2c8f18b
--- /dev/null
+++ b/fpu/softfloat-macros.h
@@ -0,0 +1,720 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'. If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1. The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 32, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+ bits32 z;
+
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 32 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'. If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1. The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 64, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+ bits64 z;
+
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 64 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+| _plus_ the number of bits given in `count'. The shifted result is at most
+| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
+| bits shifted off form a second 64-bit result as follows: The _last_ bit
+| shifted off is the most-significant bit of the extra result, and the other
+| 63 bits of the extra result are all zero if and only if _all_but_the_last_
+| bits shifted off were all zero. This extra result is stored in the location
+| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
+| (This routine makes more sense if `a0' and `a1' are considered to form
+| a fixed-point value with binary point between `a0' and `a1'. This fixed-
+| point value is shifted right by the number of bits given in `count', and
+| the integer part of the result is returned at the location pointed to by
+| `z0Ptr'. The fractional part of the result may be slightly corrupted as
+| described above, and is returned at the location pointed to by `z1Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift64ExtraRightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1 != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'. Any bits shifted off are lost. The value
+| of `count' can be arbitrarily large; in particular, if `count' is greater
+| than 128, the result will be 0. The result is broken into two 64-bit pieces
+| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128Right(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'. If any nonzero bits are shifted off, they
+| are ``jammed'' into the least significant bit of the result by setting the
+| least significant bit to 1. The value of `count' can be arbitrarily large;
+| in particular, if `count' is greater than 128, the result will be either
+| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+| nonzero. The result is broken into two 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128RightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else if ( count < 128 ) {
+ z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+| by 64 _plus_ the number of bits given in `count'. The shifted result is
+| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
+| off form a third 64-bit result as follows: The _last_ bit shifted off is
+| the most-significant bit of the extra result, and the other 63 bits of the
+| extra result are all zero if and only if _all_but_the_last_ bits shifted off
+| were all zero. This extra result is stored in the location pointed to by
+| `z2Ptr'. The value of `count' can be arbitrarily large.
+| (This routine makes more sense if `a0', `a1', and `a2' are considered
+| to form a fixed-point value with binary point between `a1' and `a2'. This
+| fixed-point value is shifted right by the number of bits given in `count',
+| and the integer part of the result is returned at the locations pointed to
+| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
+| corrupted as described above, and is returned at the location pointed to by
+| `z2Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128ExtraRightJamming(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z2 = a2;
+ z1 = a1;
+ z0 = a0;
+ }
+ else {
+ if ( count < 64 ) {
+ z2 = a1<<negCount;
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z2 = a1;
+ z1 = a0;
+ }
+ else {
+ a2 |= a1;
+ if ( count < 128 ) {
+ z2 = a0<<negCount;
+ z1 = a0>>( count & 63 );
+ }
+ else {
+ z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+ z1 = 0;
+ }
+ }
+ z0 = 0;
+ }
+ z2 |= ( a2 != 0 );
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+| number of bits given in `count'. Any bits shifted off are lost. The value
+| of `count' must be less than 64. The result is broken into two 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift128Left(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1<<count;
+ *z0Ptr =
+ ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+| by the number of bits given in `count'. Any bits shifted off are lost.
+| The value of `count' must be less than 64. The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift192Left(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount;
+
+ z2 = a2<<count;
+ z1 = a1<<count;
+ z0 = a0<<count;
+ if ( 0 < count ) {
+ negCount = ( ( - count ) & 63 );
+ z1 |= a2>>negCount;
+ z0 |= a1>>negCount;
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
+| any carry out is lost. The result is broken into two 64-bit pieces which
+| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z1;
+
+ z1 = a1 + b1;
+ *z1Ptr = z1;
+ *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
+| modulo 2^192, so any carry out is lost. The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 carry0, carry1;
+
+ z2 = a2 + b2;
+ carry1 = ( z2 < a2 );
+ z1 = a1 + b1;
+ carry0 = ( z1 < a1 );
+ z0 = a0 + b0;
+ z1 += carry1;
+ z0 += ( z1 < carry1 );
+ z0 += carry0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
+| 2^128, so any borrow out (carry out) is lost. The result is broken into two
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+| `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1 - b1;
+ *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
+| result is broken into three 64-bit pieces which are stored at the locations
+| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 borrow0, borrow1;
+
+ z2 = a2 - b2;
+ borrow1 = ( a2 < b2 );
+ z1 = a1 - b1;
+ borrow0 = ( a1 < b1 );
+ z0 = a0 - b0;
+ z0 -= ( z1 < borrow1 );
+ z1 -= borrow1;
+ z0 -= borrow0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
+| into two 64-bit pieces which are stored at the locations pointed to by
+| `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits32 aHigh, aLow, bHigh, bLow;
+ bits64 z0, zMiddleA, zMiddleB, z1;
+
+ aLow = a;
+ aHigh = a>>32;
+ bLow = b;
+ bHigh = b>>32;
+ z1 = ( (bits64) aLow ) * bLow;
+ zMiddleA = ( (bits64) aLow ) * bHigh;
+ zMiddleB = ( (bits64) aHigh ) * bLow;
+ z0 = ( (bits64) aHigh ) * bHigh;
+ zMiddleA += zMiddleB;
+ z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+ zMiddleA <<= 32;
+ z1 += zMiddleA;
+ z0 += ( z1 < zMiddleA );
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
+| `b' to obtain a 192-bit product. The product is broken into three 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+| `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128By64To192(
+ bits64 a0,
+ bits64 a1,
+ bits64 b,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2, more1;
+
+ mul64To128( a1, b, &z1, &z2 );
+ mul64To128( a0, b, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+| product. The product is broken into four 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128To256(
+ bits64 a0,
+ bits64 a1,
+ bits64 b0,
+ bits64 b1,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr,
+ bits64 *z3Ptr
+ )
+{
+ bits64 z0, z1, z2, z3;
+ bits64 more1, more2;
+
+ mul64To128( a1, b1, &z2, &z3 );
+ mul64To128( a1, b0, &z1, &more2 );
+ add128( z1, more2, 0, z2, &z1, &z2 );
+ mul64To128( a0, b0, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ mul64To128( a0, b1, &more1, &more2 );
+ add128( more1, more2, 0, z2, &more1, &z2 );
+ add128( z0, z1, 0, more1, &z0, &z1 );
+ *z3Ptr = z3;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the 64-bit integer quotient obtained by dividing
+| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The
+| divisor `b' must be at least 2^63. If q is the exact quotient truncated
+| toward zero, the approximation returned lies between q and q + 2 inclusive.
+| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+| unsigned integer is returned.
+*----------------------------------------------------------------------------*/
+
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+ bits64 b0, b1;
+ bits64 rem0, rem1, term0, term1;
+ bits64 z;
+
+ if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+ b0 = b>>32;
+ z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+ mul64To128( b, z, &term0, &term1 );
+ sub128( a0, a1, term0, term1, &rem0, &rem1 );
+ while ( ( (sbits64) rem0 ) < 0 ) {
+ z -= LIT64( 0x100000000 );
+ b1 = b<<32;
+ add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+ }
+ rem0 = ( rem0<<32 ) | ( rem1>>32 );
+ z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the square root of the 32-bit significand given
+| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
+| `aExp' (the least significant bit) is 1, the integer returned approximates
+| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
+| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
+| case, the approximation returned lies strictly within +/-2 of the exact
+| value.
+*----------------------------------------------------------------------------*/
+
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+ static const bits16 sqrtOddAdjustments[] = {
+ 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+ 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+ };
+ static const bits16 sqrtEvenAdjustments[] = {
+ 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+ 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+ };
+ int8 index;
+ bits32 z;
+
+ index = ( a>>27 ) & 15;
+ if ( aExp & 1 ) {
+ z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
+ z = ( ( a / z )<<14 ) + ( z<<15 );
+ a >>= 1;
+ }
+ else {
+ z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
+ z = a / z + z;
+ z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+ if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+ }
+ return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'. If `a' is zero, 32 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros32( bits32 a )
+{
+ static const int8 countLeadingZerosHigh[] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < 0x10000 ) {
+ shiftCount += 16;
+ a <<= 16;
+ }
+ if ( a < 0x1000000 ) {
+ shiftCount += 8;
+ a <<= 8;
+ }
+ shiftCount += countLeadingZerosHigh[ a>>24 ];
+ return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'. If `a' is zero, 64 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros64( bits64 a )
+{
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < ( (bits64) 1 )<<32 ) {
+ shiftCount += 32;
+ }
+ else {
+ a >>= 32;
+ }
+ shiftCount += countLeadingZeros32( a );
+ return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
+| returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 != b0 ) || ( a1 != b1 );
+
+}
+
diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c
new file mode 100644
index 0000000..bbdb3d6
--- /dev/null
+++ b/fpu/softfloat-native.c
@@ -0,0 +1,368 @@
+/* Native implementation of soft float functions. Only a single status
+ context is supported */
+#include "softfloat.h"
+#include <math.h>
+
+void set_float_rounding_mode(int val STATUS_PARAM)
+{
+ STATUS(float_rounding_mode) = val;
+#if defined(_BSD) && !defined(__APPLE__) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
+ fpsetround(val);
+#elif defined(__arm__)
+ /* nothing to do */
+#else
+ fesetround(val);
+#endif
+}
+
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM)
+{
+ STATUS(floatx80_rounding_precision) = val;
+}
+#endif
+
+#if defined(_BSD) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
+#define lrint(d) ((int32_t)rint(d))
+#define llrint(d) ((int64_t)rint(d))
+#define lrintf(f) ((int32_t)rint(f))
+#define llrintf(f) ((int64_t)rint(f))
+#define sqrtf(f) ((float)sqrt(f))
+#define remainderf(fa, fb) ((float)remainder(fa, fb))
+#define rintf(f) ((float)rint(f))
+#endif
+
+#if defined(__powerpc__)
+
+/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
+double qemu_rint(double x)
+{
+ double y = 4503599627370496.0;
+ if (fabs(x) >= y)
+ return x;
+ if (x < 0)
+ y = -y;
+ y = (x + y) - y;
+ if (y == 0.0)
+ y = copysign(y, x);
+ return y;
+}
+
+#define rint qemu_rint
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32(int v STATUS_PARAM)
+{
+ return (float32)v;
+}
+
+float64 int32_to_float64(int v STATUS_PARAM)
+{
+ return (float64)v;
+}
+
+#ifdef FLOATX80
+floatx80 int32_to_floatx80(int v STATUS_PARAM)
+{
+ return (floatx80)v;
+}
+#endif
+float32 int64_to_float32( int64_t v STATUS_PARAM)
+{
+ return (float32)v;
+}
+float64 int64_to_float64( int64_t v STATUS_PARAM)
+{
+ return (float64)v;
+}
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
+{
+ return (floatx80)v;
+}
+#endif
+
+/* XXX: this code implements the x86 behaviour, not the IEEE one. */
+#if HOST_LONG_BITS == 32
+static inline int long_to_int32(long a)
+{
+ return a;
+}
+#else
+static inline int long_to_int32(long a)
+{
+ if (a != (int32_t)a)
+ a = 0x80000000;
+ return a;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32 a STATUS_PARAM)
+{
+ return long_to_int32(lrintf(a));
+}
+int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
+{
+ return (int)a;
+}
+int64_t float32_to_int64( float32 a STATUS_PARAM)
+{
+ return llrintf(a);
+}
+
+int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM)
+{
+ return (int64_t)a;
+}
+
+float64 float32_to_float64( float32 a STATUS_PARAM)
+{
+ return a;
+}
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 a STATUS_PARAM)
+{
+ return a;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32 a STATUS_PARAM)
+{
+ return rintf(a);
+}
+
+float32 float32_rem( float32 a, float32 b STATUS_PARAM)
+{
+ return remainderf(a, b);
+}
+
+float32 float32_sqrt( float32 a STATUS_PARAM)
+{
+ return sqrtf(a);
+}
+char float32_compare( float32 a, float32 b STATUS_PARAM )
+{
+ if (a < b) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
+{
+ if (isless(a, b)) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (isgreater(a, b)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char float32_is_signaling_nan( float32 a1)
+{
+ float32u u;
+ uint32_t a;
+ u.f = a1;
+ a = u.i;
+ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 a STATUS_PARAM)
+{
+ return long_to_int32(lrint(a));
+}
+int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
+{
+ return (int)a;
+}
+int64_t float64_to_int64( float64 a STATUS_PARAM)
+{
+ return llrint(a);
+}
+int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM)
+{
+ return (int64_t)a;
+}
+float32 float64_to_float32( float64 a STATUS_PARAM)
+{
+ return a;
+}
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 a STATUS_PARAM)
+{
+ return a;
+}
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 a STATUS_PARAM)
+{
+ return a;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 a STATUS_PARAM )
+{
+#if defined(__arm__)
+ switch(STATUS(float_rounding_mode)) {
+ default:
+ case float_round_nearest_even:
+ asm("rndd %0, %1" : "=f" (a) : "f"(a));
+ break;
+ case float_round_down:
+ asm("rnddm %0, %1" : "=f" (a) : "f"(a));
+ break;
+ case float_round_up:
+ asm("rnddp %0, %1" : "=f" (a) : "f"(a));
+ break;
+ case float_round_to_zero:
+ asm("rnddz %0, %1" : "=f" (a) : "f"(a));
+ break;
+ }
+#else
+ return rint(a);
+#endif
+}
+
+float64 float64_rem( float64 a, float64 b STATUS_PARAM)
+{
+ return remainder(a, b);
+}
+
+float64 float64_sqrt( float64 a STATUS_PARAM)
+{
+ return sqrt(a);
+}
+char float64_compare( float64 a, float64 b STATUS_PARAM )
+{
+ if (a < b) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
+{
+ if (isless(a, b)) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (isgreater(a, b)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char float64_is_signaling_nan( float64 a1)
+{
+ float64u u;
+ uint64_t a;
+ u.f = a1;
+ a = u.i;
+ return
+ ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+ && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 a STATUS_PARAM)
+{
+ return long_to_int32(lrintl(a));
+}
+int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
+{
+ return (int)a;
+}
+int64_t floatx80_to_int64( floatx80 a STATUS_PARAM)
+{
+ return llrintl(a);
+}
+int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM)
+{
+ return (int64_t)a;
+}
+float32 floatx80_to_float32( floatx80 a STATUS_PARAM)
+{
+ return a;
+}
+float64 floatx80_to_float64( floatx80 a STATUS_PARAM)
+{
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM)
+{
+ return rintl(a);
+}
+floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return remainderl(a, b);
+}
+floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
+{
+ return sqrtl(a);
+}
+char floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ if (a < b) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ if (isless(a, b)) {
+ return -1;
+ } else if (a == b) {
+ return 0;
+ } else if (isgreater(a, b)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+char floatx80_is_signaling_nan( floatx80 a1)
+{
+ floatx80u u;
+ u.f = a1;
+ return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 );
+}
+
+#endif
diff --git a/fpu/softfloat-native.h b/fpu/softfloat-native.h
new file mode 100644
index 0000000..e7c08b8
--- /dev/null
+++ b/fpu/softfloat-native.h
@@ -0,0 +1,359 @@
+/* Native implementation of soft float functions */
+#include <math.h>
+
+#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
+#include <ieeefp.h>
+#define fabsf(f) ((float)fabs(f))
+#else
+#include <fenv.h>
+#endif
+
+/*
+ * Define some C99-7.12.3 classification macros and
+ * some C99-.12.4 for Solaris systems OS less than 10,
+ * or Solaris 10 systems running GCC 3.x or less.
+ * Solaris 10 with GCC4 does not need these macros as they
+ * are defined in <iso/math_c99.h> with a compiler directive
+ */
+#if defined(HOST_SOLARIS) && (( HOST_SOLARIS <= 9 ) || ( ( HOST_SOLARIS >= 10 ) && ( __GNUC__ <= 4) ))
+/*
+ * C99 7.12.3 classification macros
+ * and
+ * C99 7.12.14 comparison macros
+ *
+ * ... do not work on Solaris 10 using GNU CC 3.4.x.
+ * Try to workaround the missing / broken C99 math macros.
+ */
+
+#define isnormal(x) (fpclass(x) >= FP_NZERO)
+#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y)))
+#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y)))
+#define isless(x, y) ((!unordered(x, y)) && ((x) < (y)))
+#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y)))
+#define isunordered(x,y) unordered(x, y)
+#endif
+
+typedef float float32;
+typedef double float64;
+#ifdef FLOATX80
+typedef long double floatx80;
+#endif
+
+typedef union {
+ float32 f;
+ uint32_t i;
+} float32u;
+typedef union {
+ float64 f;
+ uint64_t i;
+} float64u;
+#ifdef FLOATX80
+typedef union {
+ floatx80 f;
+ struct {
+ uint64_t low;
+ uint16_t high;
+ } i;
+} floatx80u;
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point rounding mode.
+*----------------------------------------------------------------------------*/
+#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
+enum {
+ float_round_nearest_even = FP_RN,
+ float_round_down = FP_RM,
+ float_round_up = FP_RP,
+ float_round_to_zero = FP_RZ
+};
+#elif defined(__arm__)
+enum {
+ float_round_nearest_even = 0,
+ float_round_down = 1,
+ float_round_up = 2,
+ float_round_to_zero = 3
+};
+#else
+enum {
+ float_round_nearest_even = FE_TONEAREST,
+ float_round_down = FE_DOWNWARD,
+ float_round_up = FE_UPWARD,
+ float_round_to_zero = FE_TOWARDZERO
+};
+#endif
+
+typedef struct float_status {
+ signed char float_rounding_mode;
+#ifdef FLOATX80
+ signed char floatx80_rounding_precision;
+#endif
+} float_status;
+
+void set_float_rounding_mode(int val STATUS_PARAM);
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32( int STATUS_PARAM);
+float64 int32_to_float64( int STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( int STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( int STATUS_PARAM);
+#endif
+float32 int64_to_float32( int64_t STATUS_PARAM);
+float64 int64_to_float64( int64_t STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 int64_to_float128( int64_t STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32 STATUS_PARAM);
+int float32_to_int32_round_to_zero( float32 STATUS_PARAM);
+int64_t float32_to_int64( float32 STATUS_PARAM);
+int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM);
+float64 float32_to_float64( float32 STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32 STATUS_PARAM);
+INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM)
+{
+ return a + b;
+}
+INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM)
+{
+ return a - b;
+}
+INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM)
+{
+ return a * b;
+}
+INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
+{
+ return a / b;
+}
+float32 float32_rem( float32, float32 STATUS_PARAM);
+float32 float32_sqrt( float32 STATUS_PARAM);
+INLINE char float32_eq( float32 a, float32 b STATUS_PARAM)
+{
+ return a == b;
+}
+INLINE char float32_le( float32 a, float32 b STATUS_PARAM)
+{
+ return a <= b;
+}
+INLINE char float32_lt( float32 a, float32 b STATUS_PARAM)
+{
+ return a < b;
+}
+INLINE char float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
+{
+ return a <= b && a >= b;
+}
+INLINE char float32_le_quiet( float32 a, float32 b STATUS_PARAM)
+{
+ return islessequal(a, b);
+}
+INLINE char float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
+{
+ return isless(a, b);
+}
+INLINE char float32_unordered( float32 a, float32 b STATUS_PARAM)
+{
+ return isunordered(a, b);
+
+}
+char float32_compare( float32, float32 STATUS_PARAM );
+char float32_compare_quiet( float32, float32 STATUS_PARAM );
+char float32_is_signaling_nan( float32 );
+
+INLINE float32 float32_abs(float32 a)
+{
+ return fabsf(a);
+}
+
+INLINE float32 float32_chs(float32 a)
+{
+ return -a;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 STATUS_PARAM );
+int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
+int64_t float64_to_int64( float64 STATUS_PARAM );
+int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
+float32 float64_to_float32( float64 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 STATUS_PARAM );
+INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM)
+{
+ return a + b;
+}
+INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM)
+{
+ return a - b;
+}
+INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM)
+{
+ return a * b;
+}
+INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
+{
+ return a / b;
+}
+float64 float64_rem( float64, float64 STATUS_PARAM );
+float64 float64_sqrt( float64 STATUS_PARAM );
+INLINE char float64_eq( float64 a, float64 b STATUS_PARAM)
+{
+ return a == b;
+}
+INLINE char float64_le( float64 a, float64 b STATUS_PARAM)
+{
+ return a <= b;
+}
+INLINE char float64_lt( float64 a, float64 b STATUS_PARAM)
+{
+ return a < b;
+}
+INLINE char float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
+{
+ return a <= b && a >= b;
+}
+INLINE char float64_le_quiet( float64 a, float64 b STATUS_PARAM)
+{
+ return islessequal(a, b);
+}
+INLINE char float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
+{
+ return isless(a, b);
+
+}
+INLINE char float64_unordered( float64 a, float64 b STATUS_PARAM)
+{
+ return isunordered(a, b);
+
+}
+char float64_compare( float64, float64 STATUS_PARAM );
+char float64_compare_quiet( float64, float64 STATUS_PARAM );
+char float64_is_signaling_nan( float64 );
+
+INLINE float64 float64_abs(float64 a)
+{
+ return fabs(a);
+}
+
+INLINE float64 float64_chs(float64 a)
+{
+ return -a;
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 STATUS_PARAM );
+int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64( floatx80 STATUS_PARAM);
+int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM);
+float32 floatx80_to_float32( floatx80 STATUS_PARAM );
+float64 floatx80_to_float64( floatx80 STATUS_PARAM );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
+INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a + b;
+}
+INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a - b;
+}
+INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a * b;
+}
+INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a / b;
+}
+floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
+INLINE char floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a == b;
+}
+INLINE char floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a <= b;
+}
+INLINE char floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a < b;
+}
+INLINE char floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return a <= b && a >= b;
+}
+INLINE char floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return islessequal(a, b);
+}
+INLINE char floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return isless(a, b);
+
+}
+INLINE char floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ return isunordered(a, b);
+
+}
+char floatx80_compare( floatx80, floatx80 STATUS_PARAM );
+char floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
+char floatx80_is_signaling_nan( floatx80 );
+
+INLINE floatx80 floatx80_abs(floatx80 a)
+{
+ return fabsl(a);
+}
+
+INLINE floatx80 floatx80_chs(floatx80 a)
+{
+ return -a;
+}
+#endif
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
new file mode 100644
index 0000000..d430f58
--- /dev/null
+++ b/fpu/softfloat-specialize.h
@@ -0,0 +1,464 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+/*----------------------------------------------------------------------------
+| Underflow tininess-detection mode, statically initialized to default value.
+| (The declaration in `softfloat.h' must match the `int8' type here.)
+*----------------------------------------------------------------------------*/
+int8 float_detect_tininess = float_tininess_after_rounding;
+
+/*----------------------------------------------------------------------------
+| Raises the exceptions specified by `flags'. Floating-point traps can be
+| defined here if desired. It is currently not possible for such a trap
+| to substitute a result value. If traps are not implemented, this routine
+| should be simply `float_exception_flags |= flags;'.
+*----------------------------------------------------------------------------*/
+
+void float_raise( int8 flags STATUS_PARAM )
+{
+
+ STATUS(float_exception_flags) |= flags;
+
+}
+
+/*----------------------------------------------------------------------------
+| Internal canonical NaN format.
+*----------------------------------------------------------------------------*/
+typedef struct {
+ flag sign;
+ bits64 high, low;
+} commonNaNT;
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated single-precision NaN.
+*----------------------------------------------------------------------------*/
+#define float32_default_nan 0xFFC00000
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a NaN;
+| otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float32_is_nan( float32 a )
+{
+
+ return ( 0xFF000000 < (bits32) ( a<<1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float32_is_signaling_nan( float32 a )
+{
+
+ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM )
+{
+ commonNaNT z;
+
+ if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR );
+ z.sign = a>>31;
+ z.low = 0;
+ z.high = ( (bits64) a )<<41;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the single-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+
+ return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes two single-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float32_is_nan( a );
+ aIsSignalingNaN = float32_is_signaling_nan( a );
+ bIsNaN = float32_is_nan( b );
+ bIsSignalingNaN = float32_is_signaling_nan( b );
+ a |= 0x00400000;
+ b |= 0x00400000;
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+ if ( aIsSignalingNaN ) {
+ if ( bIsSignalingNaN ) goto returnLargerSignificand;
+ return bIsNaN ? b : a;
+ }
+ else if ( aIsNaN ) {
+ if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+ if ( (bits32) ( a<<1 ) < (bits32) ( b<<1 ) ) return b;
+ if ( (bits32) ( b<<1 ) < (bits32) ( a<<1 ) ) return a;
+ return ( a < b ) ? a : b;
+ }
+ else {
+ return b;
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated double-precision NaN.
+*----------------------------------------------------------------------------*/
+#define float64_default_nan LIT64( 0xFFF8000000000000 )
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a NaN;
+| otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float64_is_nan( float64 a )
+{
+
+ return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float64_is_signaling_nan( float64 a )
+{
+
+ return
+ ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+ && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = a>>63;
+ z.low = 0;
+ z.high = a<<12;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the double-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+
+ return
+ ( ( (bits64) a.sign )<<63 )
+ | LIT64( 0x7FF8000000000000 )
+ | ( a.high>>12 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes two double-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float64_is_nan( a );
+ aIsSignalingNaN = float64_is_signaling_nan( a );
+ bIsNaN = float64_is_nan( b );
+ bIsSignalingNaN = float64_is_signaling_nan( b );
+ a |= LIT64( 0x0008000000000000 );
+ b |= LIT64( 0x0008000000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+ if ( aIsSignalingNaN ) {
+ if ( bIsSignalingNaN ) goto returnLargerSignificand;
+ return bIsNaN ? b : a;
+ }
+ else if ( aIsNaN ) {
+ if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+ if ( (bits64) ( a<<1 ) < (bits64) ( b<<1 ) ) return b;
+ if ( (bits64) ( b<<1 ) < (bits64) ( a<<1 ) ) return a;
+ return ( a < b ) ? a : b;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision NaN. The
+| `high' and `low' values hold the most- and least-significant bits,
+| respectively.
+*----------------------------------------------------------------------------*/
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_is_nan( floatx80 a )
+{
+
+ return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_is_signaling_nan( floatx80 a )
+{
+ bits64 aLow;
+
+ aLow = a.low & ~ LIT64( 0x4000000000000000 );
+ return
+ ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (bits64) ( aLow<<1 )
+ && ( a.low == aLow );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
+| invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = a.high>>15;
+ z.low = 0;
+ z.high = a.low<<1;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the extended
+| double-precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+ floatx80 z;
+
+ z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
+ z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes two extended double-precision floating-point values `a' and `b', one
+| of which is a NaN, and returns the appropriate NaN result. If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = floatx80_is_nan( a );
+ aIsSignalingNaN = floatx80_is_signaling_nan( a );
+ bIsNaN = floatx80_is_nan( b );
+ bIsSignalingNaN = floatx80_is_signaling_nan( b );
+ a.low |= LIT64( 0xC000000000000000 );
+ b.low |= LIT64( 0xC000000000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+ if ( aIsSignalingNaN ) {
+ if ( bIsSignalingNaN ) goto returnLargerSignificand;
+ return bIsNaN ? b : a;
+ }
+ else if ( aIsNaN ) {
+ if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+ if ( a.low < b.low ) return b;
+ if ( b.low < a.low ) return a;
+ return ( a.high < b.high ) ? a : b;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated quadruple-precision NaN. The `high' and
+| `low' values hold the most- and least-significant bits, respectively.
+*----------------------------------------------------------------------------*/
+#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
+#define float128_default_nan_low LIT64( 0x0000000000000000 )
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
+| otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float128_is_nan( float128 a )
+{
+
+ return
+ ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+ && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+flag float128_is_signaling_nan( float128 a )
+{
+
+ return
+ ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+ && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = a.high>>63;
+ shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the quadruple-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+ float128 z;
+
+ shift128Right( a.high, a.low, 16, &z.high, &z.low );
+ z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes two quadruple-precision floating-point values `a' and `b', one of
+| which is a NaN, and returns the appropriate NaN result. If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float128_is_nan( a );
+ aIsSignalingNaN = float128_is_signaling_nan( a );
+ bIsNaN = float128_is_nan( b );
+ bIsSignalingNaN = float128_is_signaling_nan( b );
+ a.high |= LIT64( 0x0000800000000000 );
+ b.high |= LIT64( 0x0000800000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+ if ( aIsSignalingNaN ) {
+ if ( bIsSignalingNaN ) goto returnLargerSignificand;
+ return bIsNaN ? b : a;
+ }
+ else if ( aIsNaN ) {
+ if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+ if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b;
+ if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a;
+ return ( a.high < b.high ) ? a : b;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#endif
+
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
new file mode 100644
index 0000000..5e84620
--- /dev/null
+++ b/fpu/softfloat.c
@@ -0,0 +1,5320 @@
+
+/*============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#include "softfloat.h"
+
+/*----------------------------------------------------------------------------
+| Primitive arithmetic functions, including multi-word arithmetic, and
+| division and square root approximations. (Can be specialized to target if
+| desired.)
+*----------------------------------------------------------------------------*/
+#include "softfloat-macros.h"
+
+/*----------------------------------------------------------------------------
+| Functions and definitions to determine: (1) whether tininess for underflow
+| is detected before or after rounding by default, (2) what (if anything)
+| happens when exceptions are raised, (3) how signaling NaNs are distinguished
+| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+| are propagated from function inputs to output. These details are target-
+| specific.
+*----------------------------------------------------------------------------*/
+#include "softfloat-specialize.h"
+
+void set_float_rounding_mode(int val STATUS_PARAM)
+{
+ STATUS(float_rounding_mode) = val;
+}
+
+void set_float_exception_flags(int val STATUS_PARAM)
+{
+ STATUS(float_exception_flags) = val;
+}
+
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM)
+{
+ STATUS(floatx80_rounding_precision) = val;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+| and 7, and returns the properly rounded 32-bit integer corresponding to the
+| input. If `zSign' is 1, the input is negated before being converted to an
+| integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point input
+| is simply rounded to an integer, with the inexact exception raised if the
+| input cannot be represented exactly as an integer. However, if the fixed-
+| point input is too large, the invalid exception is raised and the largest
+| positive or negative integer is returned.
+*----------------------------------------------------------------------------*/
+
+static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ int32 z;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = absZ & 0x7F;
+ absZ = ( absZ + roundIncrement )>>7;
+ absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ z = absZ;
+ if ( zSign ) z = - z;
+ if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
+| `absZ1', with binary point between bits 63 and 64 (between the input words),
+| and returns the properly rounded 64-bit integer corresponding to the input.
+| If `zSign' is 1, the input is negated before being converted to an integer.
+| Ordinarily, the fixed-point input is simply rounded to an integer, with
+| the inexact exception raised if the input cannot be represented exactly as
+| an integer. However, if the fixed-point input is too large, the invalid
+| exception is raised and the largest positive or negative integer is
+| returned.
+*----------------------------------------------------------------------------*/
+
+static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment;
+ int64 z;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ increment = ( (sbits64) absZ1 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && absZ1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && absZ1;
+ }
+ }
+ }
+ if ( increment ) {
+ ++absZ0;
+ if ( absZ0 == 0 ) goto overflow;
+ absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven );
+ }
+ z = absZ0;
+ if ( zSign ) z = - z;
+ if ( z && ( ( z < 0 ) ^ zSign ) ) {
+ overflow:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return
+ zSign ? (sbits64) LIT64( 0x8000000000000000 )
+ : LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+ return a & 0x007FFFFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+ return ( a>>23 ) & 0xFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+ return a>>31;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal single-precision floating-point value represented
+| by the denormalized significand `aSig'. The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( aSig ) - 8;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| single-precision floating-point value, returning the result. After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result. This means that any integer portion of `zSig'
+| will be added into the exponent. Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+
+ return ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input. Ordinarily, the abstract
+| value is simply rounded and packed into the single-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal single-
+| precision floating-point number.
+| The input significand `zSig' has its binary point between bits 30
+| and 29, which is 7 bits to the left of the usual location. This shifted
+| significand must be normalized or smaller. If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding. In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x7F;
+ if ( 0xFD <= (bits16) zExp ) {
+ if ( ( 0xFD < zExp )
+ || ( ( zExp == 0xFD )
+ && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat32( zSign, 0xFF, 0 ) - ( roundIncrement == 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < 0x80000000 );
+ shift32RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x7F;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>7;
+ zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input. This routine is just like
+| `roundAndPackFloat32' except that `zSig' does not have to be normalized.
+| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( zSig ) - 1;
+ return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+ return a & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+ return ( a>>52 ) & 0x7FF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+ return a>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal double-precision floating-point value represented
+| by the denormalized significand `aSig'. The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig ) - 11;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| double-precision floating-point value, returning the result. After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result. This means that any integer portion of `zSig'
+| will be added into the exponent. Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+ return ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input. Ordinarily, the abstract
+| value is simply rounded and packed into the double-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded
+| to a subnormal number, and the underflow and inexact exceptions are raised
+| if the abstract input cannot be represented exactly as a subnormal double-
+| precision floating-point number.
+| The input significand `zSig' has its binary point between bits 62
+| and 61, which is 10 bits to the left of the usual location. This shifted
+| significand must be normalized or smaller. If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding. In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int16 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x200;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x3FF;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x3FF;
+ if ( 0x7FD <= (bits16) zExp ) {
+ if ( ( 0x7FD < zExp )
+ || ( ( zExp == 0x7FD )
+ && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat64( zSign, 0x7FF, 0 ) - ( roundIncrement == 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+ shift64RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x3FF;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>10;
+ zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input. This routine is just like
+| `roundAndPackFloat64' except that `zSig' does not have to be normalized.
+| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( zSig ) - 1;
+ return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+ return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+ return a.high & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the extended double-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+ return a.high>>15;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal extended double-precision floating-point value
+| represented by the denormalized significand `aSig'. The normalized exponent
+| and significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig );
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+| extended double-precision floating-point value, returning the result.
+*----------------------------------------------------------------------------*/
+
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+ floatx80 z;
+
+ z.low = zSig;
+ z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input. Ordinarily, the abstract value is
+| rounded and packed into the extended double-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal extended
+| double-precision floating-point number.
+| If `roundingPrecision' is 32 or 64, the result is rounded to the same
+| number of bits as single or double precision, respectively. Otherwise, the
+| result is rounded to the full precision of the extended double-precision
+| format.
+| The input significand must be normalized or smaller. If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding. The
+| handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ roundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+ int64 roundIncrement, roundMask, roundBits;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ if ( roundingPrecision == 80 ) goto precision80;
+ if ( roundingPrecision == 64 ) {
+ roundIncrement = LIT64( 0x0000000000000400 );
+ roundMask = LIT64( 0x00000000000007FF );
+ }
+ else if ( roundingPrecision == 32 ) {
+ roundIncrement = LIT64( 0x0000008000000000 );
+ roundMask = LIT64( 0x000000FFFFFFFFFF );
+ }
+ else {
+ goto precision80;
+ }
+ zSig0 |= ( zSig1 != 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = roundMask;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig0 & roundMask;
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+ ) {
+ goto overflow;
+ }
+ if ( zExp <= 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ( zSig0 <= zSig0 + roundIncrement );
+ shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+ zExp = 0;
+ roundBits = zSig0 & roundMask;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( zSig0 < roundIncrement ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ if ( zSig0 == 0 ) zExp = 0;
+ return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+ increment = ( (sbits64) zSig1 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE )
+ && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+ && increment
+ )
+ ) {
+ roundMask = 0;
+ overflow:
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( zExp <= 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ! increment
+ || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+ shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+ zExp = 0;
+ if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig1 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ if ( increment ) {
+ ++zSig0;
+ zSig0 &=
+ ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ }
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( increment ) {
+ ++zSig0;
+ if ( zSig0 == 0 ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ else {
+ zSig0 &= ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+ }
+ }
+ else {
+ if ( zSig0 == 0 ) zExp = 0;
+ }
+ return packFloatx80( zSign, zExp, zSig0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent
+| `zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input. This routine is just like
+| `roundAndPackFloatx80' except that the input significand does not have to be
+| normalized.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ normalizeRoundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 );
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ zExp -= shiftCount;
+ return
+ roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 STATUS_VAR);
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the least-significant 64 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+ return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the most-significant 48 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+ return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the quadruple-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+ return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the quadruple-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+ return a.high>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal quadruple-precision floating-point value
+| represented by the denormalized significand formed by the concatenation of
+| `aSig0' and `aSig1'. The normalized exponent is stored at the location
+| pointed to by `zExpPtr'. The most significant 49 bits of the normalized
+| significand are stored at the location pointed to by `zSig0Ptr', and the
+| least significant 64 bits of the normalized significand are stored at the
+| location pointed to by `zSig1Ptr'.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat128Subnormal(
+ bits64 aSig0,
+ bits64 aSig1,
+ int32 *zExpPtr,
+ bits64 *zSig0Ptr,
+ bits64 *zSig1Ptr
+ )
+{
+ int8 shiftCount;
+
+ if ( aSig0 == 0 ) {
+ shiftCount = countLeadingZeros64( aSig1 ) - 15;
+ if ( shiftCount < 0 ) {
+ *zSig0Ptr = aSig1>>( - shiftCount );
+ *zSig1Ptr = aSig1<<( shiftCount & 63 );
+ }
+ else {
+ *zSig0Ptr = aSig1<<shiftCount;
+ *zSig1Ptr = 0;
+ }
+ *zExpPtr = - shiftCount - 63;
+ }
+ else {
+ shiftCount = countLeadingZeros64( aSig0 ) - 15;
+ shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+ *zExpPtr = 1 - shiftCount;
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', the exponent `zExp', and the significand formed
+| by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+| floating-point value, returning the result. After being shifted into the
+| proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+| added together to form the most significant 32 bits of the result. This
+| means that any integer portion of `zSig0' will be added into the exponent.
+| Since a properly normalized significand will have an integer portion equal
+| to 1, the `zExp' input should be 1 less than the desired result exponent
+| whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+ float128 z;
+
+ z.low = zSig1;
+ z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0', `zSig1',
+| and `zSig2', and returns the proper quadruple-precision floating-point value
+| corresponding to the abstract input. Ordinarily, the abstract value is
+| simply rounded and packed into the quadruple-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal quadruple-
+| precision floating-point number.
+| The input significand must be normalized or smaller. If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding. In the
+| usual case that the input significand is normalized, `zExp' must be 1 less
+| than the ``true'' floating-point exponent. The handling of underflow and
+| overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128
+ roundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ increment = ( (sbits64) zSig2 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) zExp ) {
+ if ( ( 0x7FFD < zExp )
+ || ( ( zExp == 0x7FFD )
+ && eq128(
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF ),
+ zSig0,
+ zSig1
+ )
+ && increment
+ )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return
+ packFloat128(
+ zSign,
+ 0x7FFE,
+ LIT64( 0x0000FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ! increment
+ || lt128(
+ zSig0,
+ zSig1,
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+ zExp = 0;
+ if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig2 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ }
+ if ( zSig2 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( increment ) {
+ add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+ zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+ }
+ else {
+ if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+ }
+ return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand formed by the concatenation of `zSig0' and `zSig1', and
+| returns the proper quadruple-precision floating-point value corresponding
+| to the abstract input. This routine is just like `roundAndPackFloat128'
+| except that the input significand has fewer bits and does not have to be
+| normalized. In all cases, `zExp' must be 1 less than the ``true'' floating-
+| point exponent.
+*----------------------------------------------------------------------------*/
+
+static float128
+ normalizeRoundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 STATUS_PARAM)
+{
+ int8 shiftCount;
+ bits64 zSig2;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 ) - 15;
+ if ( 0 <= shiftCount ) {
+ zSig2 = 0;
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ }
+ else {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+ }
+ zExp -= shiftCount;
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR);
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the single-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int32_to_float32( int32 a STATUS_PARAM )
+{
+ flag zSign;
+
+ if ( a == 0 ) return 0;
+ if ( a == (sbits32) 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+ zSign = ( a < 0 );
+ return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the double-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int32_to_float64( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return 0;
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 21;
+ zSig = absA;
+ return packFloat64( zSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the extended double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 32;
+ zSig = absA;
+ return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a' to
+| the quadruple-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int32_to_float128( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig0;
+
+ if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 17;
+ zSig0 = absA;
+ return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the single-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int64_to_float32( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+
+ if ( a == 0 ) return 0;
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA ) - 40;
+ if ( 0 <= shiftCount ) {
+ return packFloat32( zSign, 0x95 - shiftCount, absA<<shiftCount );
+ }
+ else {
+ shiftCount += 7;
+ if ( shiftCount < 0 ) {
+ shift64RightJamming( absA, - shiftCount, &absA );
+ }
+ else {
+ absA <<= shiftCount;
+ }
+ return roundAndPackFloat32( zSign, 0x9C - shiftCount, absA STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the double-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int64_to_float64( int64 a STATUS_PARAM )
+{
+ flag zSign;
+
+ if ( a == 0 ) return 0;
+ if ( a == (sbits64) LIT64( 0x8000000000000000 ) ) {
+ return packFloat64( 1, 0x43E, 0 );
+ }
+ zSign = ( a < 0 );
+ return normalizeRoundAndPackFloat64( zSign, 0x43C, zSign ? - a : a STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the extended double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+
+ if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA );
+ return packFloatx80( zSign, 0x403E - shiftCount, absA<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a' to
+| the quadruple-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int64_to_float128( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+ int32 zExp;
+ bits64 zSig0, zSig1;
+
+ if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA ) + 49;
+ zExp = 0x406E - shiftCount;
+ if ( 64 <= shiftCount ) {
+ zSig1 = 0;
+ zSig0 = absA;
+ shiftCount -= 64;
+ }
+ else {
+ zSig1 = absA;
+ zSig0 = 0;
+ }
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( ( aExp == 0xFF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= 0x00800000;
+ shiftCount = 0xAF - aExp;
+ aSig64 = aSig;
+ aSig64 <<= 32;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig64, shiftCount, &aSig64 );
+ return roundAndPackInt32( aSign, aSig64 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ int32 z;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0x9E;
+ if ( 0 <= shiftCount ) {
+ if ( a != 0xCF000000 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+ }
+ return (sbits32) 0x80000000;
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig = ( aSig | 0x00800000 )<<8;
+ z = aSig>>( - shiftCount );
+ if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64, aSigExtra;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = 0xBE - aExp;
+ if ( shiftCount < 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ if ( aExp ) aSig |= 0x00800000;
+ aSig64 = aSig;
+ aSig64 <<= 40;
+ shift64ExtraRightJamming( aSig64, 0, shiftCount, &aSig64, &aSigExtra );
+ return roundAndPackInt64( aSign, aSig64, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero. If
+| `a' is a NaN, the largest positive integer is returned. Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64;
+ int64 z;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0xBE;
+ if ( 0 <= shiftCount ) {
+ if ( a != 0xDF000000 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig64 = aSig | 0x00800000;
+ aSig64 <<= 40;
+ z = aSig64>>( - shiftCount );
+ if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the double-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float32_to_float64( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ));
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the extended double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ aSig |= 0x00800000;
+ return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the double-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float32_to_float128( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the single-precision floating-point value `a' to an integer, and
+| returns the result as a single-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_round_to_int( float32 a STATUS_PARAM)
+{
+ flag aSign;
+ int16 aExp;
+ bits32 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float32 z;
+
+ aExp = extractFloat32Exp( a );
+ if ( 0x96 <= aExp ) {
+ if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+ return propagateFloat32NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp <= 0x7E ) {
+ if ( (bits32) ( a<<1 ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat32Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+ return packFloat32( aSign, 0x7F, 0 );
+ }
+ break;
+ case float_round_down:
+ return aSign ? 0xBF800000 : 0;
+ case float_round_up:
+ return aSign ? 0x80000000 : 0x3F800000;
+ }
+ return packFloat32( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x96 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != a ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the single-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 6;
+ bSig <<= 6;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x20000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x20000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+ zSig = 0x40000000 + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= 0x20000000;
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits32) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the single-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 7;
+ bSig <<= 7;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat32( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign ^ 1, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x40000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ bSig |= 0x40000000;
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x40000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ aSig |= 0x40000000;
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the single-precision floating-point values `a'
+| and `b'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_add( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat32Sigs( a, b, aSign STATUS_VAR);
+ }
+ else {
+ return subFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the single-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sub( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the single-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_mul( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig;
+ bits64 zSig64;
+ bits32 zSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x7F;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+ zSig = zSig64;
+ if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+ zSig <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the single-precision floating-point value `a'
+| by the corresponding value `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_div( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x7D;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+ if ( ( zSig & 0x3F ) == 0 ) {
+ zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 );
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the single-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_rem( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits32 aSig, bSig;
+ bits32 q;
+ bits64 aSig64, bSig64, q64;
+ bits32 alternateASig;
+ sbits32 sigMean;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig |= 0x00800000;
+ bSig |= 0x00800000;
+ if ( expDiff < 32 ) {
+ aSig <<= 8;
+ bSig <<= 8;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ if ( 0 < expDiff ) {
+ q = ( ( (bits64) aSig )<<32 ) / bSig;
+ q >>= 32 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ }
+ else {
+ if ( bSig <= aSig ) aSig -= bSig;
+ aSig64 = ( (bits64) aSig )<<40;
+ bSig64 = ( (bits64) bSig )<<40;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ aSig64 = - ( ( bSig * q64 )<<38 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ q = q64>>( 64 - expDiff );
+ bSig <<= 6;
+ aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits32) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits32) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the single-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sqrt( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits32 aSig, zSig;
+ bits64 rem, term;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, 0 STATUS_VAR );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return 0;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+ aSig = ( aSig | 0x00800000 )<<8;
+ zSig = estimateSqrt32( aExp, aSig ) + 2;
+ if ( ( zSig & 0x7F ) <= 5 ) {
+ if ( zSig < 2 ) {
+ zSig = 0x7FFFFFFF;
+ goto roundAndPack;
+ }
+ aSig >>= aExp & 1;
+ term = ( (bits64) zSig ) * zSig;
+ rem = ( ( (bits64) aSig )<<32 ) - term;
+ while ( (sbits64) rem < 0 ) {
+ --zSig;
+ rem += ( ( (bits64) zSig )<<1 ) | 1;
+ }
+ zSig |= ( rem != 0 );
+ }
+ shift32RightJamming( zSig, 1, &zSig );
+ roundAndPack:
+ return roundAndPackFloat32( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_eq( float32 a, float32 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_le( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_lt( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_le_quiet( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x42C - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( 0x41E < aExp ) {
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FF ) {
+ if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x433 - aExp;
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, aSigExtra;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x433 - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( 0x43E < aExp ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FF )
+ && ( aSig != LIT64( 0x0010000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ aSigExtra = 0;
+ aSig <<= - shiftCount;
+ }
+ else {
+ shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+ }
+ return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+ int64 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = aExp - 0x433;
+ if ( 0 <= shiftCount ) {
+ if ( 0x43E <= aExp ) {
+ if ( a != LIT64( 0xC3E0000000000000 ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FF )
+ && ( aSig != LIT64( 0x0010000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ z = aSig<<shiftCount;
+ }
+ else {
+ if ( aExp < 0x3FE ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ z = aSig>>( - shiftCount );
+ if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the single-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float64_to_float32( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+ bits32 zSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 22, &aSig );
+ zSig = aSig;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x381;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the extended double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ return
+ packFloatx80(
+ aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the quadruple-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float64_to_float128( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the double-precision floating-point value `a' to an integer, and
+| returns the result as a double-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_round_to_int( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float64 z;
+
+ aExp = extractFloat64Exp( a );
+ if ( 0x433 <= aExp ) {
+ if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+ return propagateFloat64NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp < 0x3FF ) {
+ if ( (bits64) ( a<<1 ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat64Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+ return packFloat64( aSign, 0x3FF, 0 );
+ }
+ break;
+ case float_round_down:
+ return aSign ? LIT64( 0xBFF0000000000000 ) : 0;
+ case float_round_up:
+ return
+ aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 );
+ }
+ return packFloat64( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x433 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != a ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the double-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 9;
+ bSig <<= 9;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+ zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= LIT64( 0x2000000000000000 );
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits64) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the double-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 10;
+ bSig <<= 10;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat64( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign ^ 1, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the double-precision floating-point values `a'
+| and `b'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_add( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the double-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sub( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the double-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_mul( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FF;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ zSig0 |= ( zSig1 != 0 );
+ if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+ zSig0 <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the double-precision floating-point value `a'
+| by the corresponding value `b'. The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_div( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ bits64 rem0, rem1;
+ bits64 term0, term1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FD;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = estimateDiv128To64( aSig, 0, bSig );
+ if ( ( zSig & 0x1FF ) <= 2 ) {
+ mul64To128( bSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig |= ( rem1 != 0 );
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the double-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_rem( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits64 aSig, bSig;
+ bits64 q, alternateASig;
+ sbits64 sigMean;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ aSig = - ( ( bSig>>2 ) * q );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits64) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits64) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the double-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sqrt( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits64 aSig, zSig, doubleZSig;
+ bits64 rem0, rem1, term0, term1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return 0;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+ aSig |= LIT64( 0x0010000000000000 );
+ zSig = estimateSqrt32( aExp, aSig>>21 );
+ aSig <<= 9 - ( aExp & 1 );
+ zSig = estimateDiv128To64( aSig, 0, zSig<<32 ) + ( zSig<<30 );
+ if ( ( zSig & 0x1FF ) <= 5 ) {
+ doubleZSig = zSig<<1;
+ mul64To128( zSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ doubleZSig -= 2;
+ add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 );
+ }
+ zSig |= ( ( rem0 | rem1 ) != 0 );
+ }
+ return roundAndPackFloat64( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_eq( float64 a, float64 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_le( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_lt( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise. The invalid exception is raised
+| if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_le_quiet( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 32-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode. If `a' is a NaN, the
+| largest positive integer is returned. Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ shiftCount = 0x4037 - aExp;
+ if ( shiftCount <= 0 ) shiftCount = 1;
+ shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 32-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero. If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( 0x401E < aExp ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ shiftCount = 0x403E - aExp;
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode. If `a' is a NaN,
+| the largest positive integer is returned. Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig, aSigExtra;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ shiftCount = 0x403E - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( shiftCount ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FFF )
+ && ( aSig != LIT64( 0x8000000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ aSigExtra = 0;
+ }
+ else {
+ shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+ }
+ return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero. If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig;
+ int64 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ shiftCount = aExp - 0x403E;
+ if ( 0 <= shiftCount ) {
+ aSig &= LIT64( 0x7FFFFFFFFFFFFFFF );
+ if ( ( a.high != 0xC03E ) || aSig ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0x7FFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ z = aSig>>( - shiftCount );
+ if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the single-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 floatx80_to_float32( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 33, &aSig );
+ if ( aExp || aSig ) aExp -= 0x3F81;
+ return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the double-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 floatx80_to_float64( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig, zSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shift64RightJamming( aSig, 1, &zSig );
+ if ( aExp || aSig ) aExp -= 0x3C01;
+ return roundAndPackFloat64( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the quadruple-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 floatx80_to_float128( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the extended double-precision floating-point value `a' to an integer,
+| and returns the result as an extended quadruple-precision floating-point
+| value. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ floatx80 z;
+
+ aExp = extractFloatx80Exp( a );
+ if ( 0x403E <= aExp ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+ return propagateFloatx80NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp < 0x3FFF ) {
+ if ( ( aExp == 0 )
+ && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+ return a;
+ }
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloatx80Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+ ) {
+ return
+ packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ?
+ packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+ : packFloatx80( 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloatx80( 1, 0, 0 )
+ : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ return packFloatx80( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x403E - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z.low += lastBitMask>>1;
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z.low += roundBitsMask;
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ if ( z.low == 0 ) {
+ ++z.high;
+ z.low = LIT64( 0x8000000000000000 );
+ }
+ if ( z.low != a.low ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the extended double-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the sum is
+| negated before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ return a;
+ }
+ zSig1 = 0;
+ zSig0 = aSig + bSig;
+ if ( aExp == 0 ) {
+ normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+ goto roundAndPack;
+ }
+ zExp = aExp;
+ goto shiftRight1;
+ }
+ zSig0 = aSig + bSig;
+ if ( (sbits64) zSig0 < 0 ) goto roundAndPack;
+ shiftRight1:
+ shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ zSig0 |= LIT64( 0x8000000000000000 );
+ ++zExp;
+ roundAndPack:
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the extended
+| double-precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ zSig1 = 0;
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+ sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+ sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ return
+ normalizeRoundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the extended double-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the extended double-precision floating-
+| point values `a' and `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the extended double-precision floating-
+| point values `a' and `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) goto invalid;
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FFE;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ if ( 0 < (sbits64) zSig0 ) {
+ shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ --zExp;
+ }
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the extended double-precision floating-point
+| value `a' by the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ bits64 rem0, rem1, rem2, term0, term1, term2;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ goto invalid;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FFE;
+ rem1 = 0;
+ if ( bSig <= aSig ) {
+ shift128Right( aSig, 0, 1, &aSig, &rem1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+ mul64To128( bSig, zSig0, &term0, &term1 );
+ sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, bSig );
+ if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+ mul64To128( bSig, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+ }
+ zSig1 |= ( ( rem1 | rem2 ) != 0 );
+ }
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the extended double-precision floating-point value
+| `a' with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig;
+ bits64 q, term0, term1, alternateASig0, alternateASig1;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ bSig |= LIT64( 0x8000000000000000 );
+ zSign = aSign;
+ expDiff = aExp - bExp;
+ aSig1 = 0;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+ expDiff = 0;
+ }
+ q = ( bSig <= aSig0 );
+ if ( q ) aSig0 -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ mul64To128( bSig, q, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+ while ( le128( term0, term1, aSig0, aSig1 ) ) {
+ ++q;
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ }
+ }
+ else {
+ term1 = 0;
+ term0 = bSig;
+ }
+ sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+ if ( lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ || ( eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ && ( q & 1 ) )
+ ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ zSign = ! zSign;
+ }
+ return
+ normalizeRoundAndPackFloatx80(
+ 80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the extended double-precision floating-point
+| value `a'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+ zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+ shift128Right( aSig0, 0, 2 + ( aExp & 1 ), &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+ doubleZSig0 = zSig0<<1;
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ doubleZSig0 -= 2;
+ add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+ if ( ( zSig1 & LIT64( 0x3FFFFFFFFFFFFFFF ) ) <= 5 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+ term3 |= 1;
+ term2 |= doubleZSig0;
+ add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shortShift128Left( 0, zSig1, 1, &zSig0, &zSig1 );
+ zSig0 |= doubleZSig0;
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| equal to the corresponding value `b', and 0 otherwise. The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than or equal to the corresponding value `b', and 0 otherwise. The
+| comparison is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is equal
+| to the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs
+| do not cause an exception. Otherwise, the comparison is performed according
+| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause
+| an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 32-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ aSig0 |= ( aSig1 != 0 );
+ shiftCount = 0x4028 - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+ return roundAndPackInt32( aSign, aSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 32-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero. If
+| `a' is a NaN, the largest positive integer is returned. Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1, savedASig;
+ int32 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ aSig0 |= ( aSig1 != 0 );
+ if ( 0x401E < aExp ) {
+ if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp || aSig0 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = 0x402F - aExp;
+ savedASig = aSig0;
+ aSig0 >>= shiftCount;
+ z = aSig0;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig0<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = 0x402F - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( 0x403E < aExp ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FFF )
+ && ( aSig1 || ( aSig0 != LIT64( 0x0001000000000000 ) ) )
+ )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ shortShift128Left( aSig0, aSig1, - shiftCount, &aSig0, &aSig1 );
+ }
+ else {
+ shift64ExtraRightJamming( aSig0, aSig1, shiftCount, &aSig0, &aSig1 );
+ }
+ return roundAndPackInt64( aSign, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+ int64 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = aExp - 0x402F;
+ if ( 0 < shiftCount ) {
+ if ( 0x403E <= aExp ) {
+ aSig0 &= LIT64( 0x0000FFFFFFFFFFFF );
+ if ( ( a.high == LIT64( 0xC03E000000000000 ) )
+ && ( aSig1 < LIT64( 0x0002000000000000 ) ) ) {
+ if ( aSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ else {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ z = ( aSig0<<shiftCount ) | ( aSig1>>( ( - shiftCount ) & 63 ) );
+ if ( (bits64) ( aSig1<<shiftCount ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ else {
+ if ( aExp < 0x3FFF ) {
+ if ( aExp | aSig0 | aSig1 ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return 0;
+ }
+ z = aSig0>>( - shiftCount );
+ if ( aSig1
+ || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the single-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float128_to_float32( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+ bits32 zSig;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ aSig0 |= ( aSig1 != 0 );
+ shift64RightJamming( aSig0, 18, &aSig0 );
+ zSig = aSig0;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x3F81;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float128_to_float64( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ aSig0 |= ( aSig1 != 0 );
+ if ( aExp || aSig0 ) {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aExp -= 0x3C01;
+ }
+ return roundAndPackFloat64( aSign, aExp, aSig0 STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the extended double-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float128_to_floatx80( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+ return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 STATUS_VAR );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the quadruple-precision floating-point value `a' to an integer, and
+| returns the result as a quadruple-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_round_to_int( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float128 z;
+
+ aExp = extractFloat128Exp( a );
+ if ( 0x402F <= aExp ) {
+ if ( 0x406F <= aExp ) {
+ if ( ( aExp == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+ ) {
+ return propagateFloat128NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ lastBitMask = 1;
+ lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ if ( lastBitMask ) {
+ add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else {
+ if ( (sbits64) z.low < 0 ) {
+ ++z.high;
+ if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+ }
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ }
+ else {
+ if ( aExp < 0x3FFF ) {
+ if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat128Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE )
+ && ( extractFloat128Frac0( a )
+ | extractFloat128Frac1( a ) )
+ ) {
+ return packFloat128( aSign, 0x3FFF, 0, 0 );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+ : packFloat128( 0, 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloat128( 1, 0, 0, 0 )
+ : packFloat128( 0, 0x3FFF, 0, 0 );
+ }
+ return packFloat128( aSign, 0, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x402F - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z.low = 0;
+ z.high = a.high;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z.high += lastBitMask>>1;
+ if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+ z.high &= ~ lastBitMask;
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ z.high |= ( a.low != 0 );
+ z.high += roundBitsMask;
+ }
+ }
+ z.high &= ~ roundBitsMask;
+ }
+ if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the quadruple-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ int32 expDiff;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ return a;
+ }
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 );
+ zSig2 = 0;
+ zSig0 |= LIT64( 0x0002000000000000 );
+ zExp = aExp;
+ goto shiftRight1;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ --zExp;
+ if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+ ++zExp;
+ shiftRight1:
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the quadruple-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+ int32 expDiff;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig0 < aSig0 ) goto aBigger;
+ if ( aSig0 < bSig0 ) goto bBigger;
+ if ( bSig1 < aSig1 ) goto aBigger;
+ if ( aSig1 < bSig1 ) goto bBigger;
+ return packFloat128( STATUS(float_rounding_mode) == float_round_down, 0, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+ sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+ sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the quadruple-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_add( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the quadruple-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sub( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the quadruple-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_mul( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ zExp = aExp + bExp - 0x4000;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+ mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+ add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zSig2 |= ( zSig3 != 0 );
+ if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ ++zExp;
+ }
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the quadruple-precision floating-point value
+| `a' by the corresponding value `b'. The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_div( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ goto invalid;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign, 0, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = aExp - bExp + 0x3FFD;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+ shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+ sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+ }
+ zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+ if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+ mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+ sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the quadruple-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_rem( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2;
+ bits64 allZero, alternateASig0, alternateASig1, sigMean1;
+ sbits64 sigMean0;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return a;
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ expDiff = aExp - bExp;
+ if ( expDiff < -1 ) return a;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ),
+ aSig1,
+ 15 - ( expDiff < 0 ),
+ &aSig0,
+ &aSig1
+ );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ q = le128( bSig0, bSig1, aSig0, aSig1 );
+ if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+ shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+ sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+ expDiff -= 61;
+ }
+ if ( -64 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ q >>= - expDiff;
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ expDiff += 52;
+ if ( expDiff < 0 ) {
+ shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ }
+ else {
+ shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+ }
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+ }
+ else {
+ shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ }
+ do {
+ alternateASig0 = aSig0;
+ alternateASig1 = aSig1;
+ ++q;
+ sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ } while ( 0 <= (sbits64) aSig0 );
+ add128(
+ aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 );
+ if ( ( sigMean0 < 0 )
+ || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ }
+ zSign = ( (sbits64) aSig0 < 0 );
+ if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+ return
+ normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the quadruple-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sqrt( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+ shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+ doubleZSig0 = zSig0<<1;
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ doubleZSig0 -= 2;
+ add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+ if ( ( zSig1 & 0x1FFF ) <= 5 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+ term3 |= 1;
+ term2 |= doubleZSig0;
+ add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_eq( float128 a, float128 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_le( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_lt( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_le_quiet( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+flag float128_lt_quiet( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+/* misc functions */
+float32 uint32_to_float32( unsigned int a STATUS_PARAM )
+{
+ return int64_to_float32(a STATUS_VAR);
+}
+
+float64 uint32_to_float64( unsigned int a STATUS_PARAM )
+{
+ return int64_to_float64(a STATUS_VAR);
+}
+
+unsigned int float32_to_uint32( float32 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float32_to_int64(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float32_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float64_to_uint32( float64 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float64_to_int64(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float64_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+#define COMPARE(s, nan_exp) \
+INLINE char float ## s ## _compare_internal( float ## s a, float ## s b, \
+ int is_quiet STATUS_PARAM ) \
+{ \
+ flag aSign, bSign; \
+ \
+ if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \
+ extractFloat ## s ## Frac( a ) ) || \
+ ( ( extractFloat ## s ## Exp( b ) == nan_exp ) && \
+ extractFloat ## s ## Frac( b ) )) { \
+ if (!is_quiet || \
+ float ## s ## _is_signaling_nan( a ) || \
+ float ## s ## _is_signaling_nan( b ) ) { \
+ float_raise( float_flag_invalid STATUS_VAR); \
+ } \
+ return float_relation_unordered; \
+ } \
+ aSign = extractFloat ## s ## Sign( a ); \
+ bSign = extractFloat ## s ## Sign( b ); \
+ if ( aSign != bSign ) { \
+ if ( (bits ## s) ( ( a | b )<<1 ) == 0 ) { \
+ /* zero case */ \
+ return float_relation_equal; \
+ } else { \
+ return 1 - (2 * aSign); \
+ } \
+ } else { \
+ if (a == b) { \
+ return float_relation_equal; \
+ } else { \
+ return 1 - 2 * (aSign ^ ( a < b )); \
+ } \
+ } \
+} \
+ \
+char float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \
+{ \
+ return float ## s ## _compare_internal(a, b, 0 STATUS_VAR); \
+} \
+ \
+char float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
+{ \
+ return float ## s ## _compare_internal(a, b, 1 STATUS_VAR); \
+}
+
+COMPARE(32, 0xff)
+COMPARE(64, 0x7ff)
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
new file mode 100644
index 0000000..fdc80f3
--- /dev/null
+++ b/fpu/softfloat.h
@@ -0,0 +1,398 @@
+/*============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#ifndef SOFTFLOAT_H
+#define SOFTFLOAT_H
+
+#include <inttypes.h>
+#include "config.h"
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines the most convenient type that holds
+| integers of at least as many bits as specified. For example, `uint8' should
+| be the most convenient type that can hold unsigned integers of as many as
+| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most
+| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+| to the same as `int'.
+*----------------------------------------------------------------------------*/
+typedef char flag;
+typedef uint8_t uint8;
+typedef int8_t int8;
+typedef int uint16;
+typedef int int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef uint64_t uint64;
+typedef int64_t int64;
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines a type that holds integers
+| of _exactly_ the number of bits specified. For instance, for most
+| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+| `unsigned short int' and `signed short int' (or `short int'), respectively.
+*----------------------------------------------------------------------------*/
+typedef uint8_t bits8;
+typedef int8_t sbits8;
+typedef uint16_t bits16;
+typedef int16_t sbits16;
+typedef uint32_t bits32;
+typedef int32_t sbits32;
+typedef uint64_t bits64;
+typedef int64_t sbits64;
+
+#define LIT64( a ) a##LL
+#define INLINE static inline
+
+/*----------------------------------------------------------------------------
+| The macro `FLOATX80' must be defined to enable the extended double-precision
+| floating-point format `floatx80'. If this macro is not defined, the
+| `floatx80' type will not be defined, and none of the functions that either
+| input or output the `floatx80' type will be defined. The same applies to
+| the `FLOAT128' macro and the quadruple-precision format `float128'.
+*----------------------------------------------------------------------------*/
+#ifdef CONFIG_SOFTFLOAT
+/* bit exact soft float support */
+#define FLOATX80
+#define FLOAT128
+#else
+/* native float support */
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(_BSD)
+#define FLOATX80
+#endif
+#endif /* !CONFIG_SOFTFLOAT */
+
+#define STATUS_PARAM , float_status *status
+#define STATUS(field) status->field
+#define STATUS_VAR , status
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point ordering relations
+*----------------------------------------------------------------------------*/
+enum {
+ float_relation_less = -1,
+ float_relation_equal = 0,
+ float_relation_greater = 1,
+ float_relation_unordered = 2
+};
+
+#ifdef CONFIG_SOFTFLOAT
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point types.
+*----------------------------------------------------------------------------*/
+typedef uint32_t float32;
+typedef uint64_t float64;
+#ifdef FLOATX80
+typedef struct {
+ uint64_t low;
+ uint16_t high;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ uint64_t high, low;
+#else
+ uint64_t low, high;
+#endif
+} float128;
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point underflow tininess-detection mode.
+*----------------------------------------------------------------------------*/
+enum {
+ float_tininess_after_rounding = 0,
+ float_tininess_before_rounding = 1
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point rounding mode.
+*----------------------------------------------------------------------------*/
+enum {
+ float_round_nearest_even = 0,
+ float_round_down = 1,
+ float_round_up = 2,
+ float_round_to_zero = 3
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point exception flags.
+*----------------------------------------------------------------------------*/
+enum {
+ float_flag_invalid = 1,
+ float_flag_divbyzero = 4,
+ float_flag_overflow = 8,
+ float_flag_underflow = 16,
+ float_flag_inexact = 32
+};
+
+typedef struct float_status {
+ signed char float_detect_tininess;
+ signed char float_rounding_mode;
+ signed char float_exception_flags;
+#ifdef FLOATX80
+ signed char floatx80_rounding_precision;
+#endif
+} float_status;
+
+void set_float_rounding_mode(int val STATUS_PARAM);
+void set_float_exception_flags(int val STATUS_PARAM);
+INLINE int get_float_exception_flags(float_status *status)
+{
+ return STATUS(float_exception_flags);
+}
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Routine to raise any or all of the software IEC/IEEE floating-point
+| exception flags.
+*----------------------------------------------------------------------------*/
+void float_raise( int8 flags STATUS_PARAM);
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32( int STATUS_PARAM );
+float64 int32_to_float64( int STATUS_PARAM );
+float32 uint32_to_float32( unsigned int STATUS_PARAM );
+float64 uint32_to_float64( unsigned int STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( int STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( int STATUS_PARAM );
+#endif
+float32 int64_to_float32( int64_t STATUS_PARAM );
+float64 int64_to_float64( int64_t STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 int64_to_float128( int64_t STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32 STATUS_PARAM );
+int float32_to_int32_round_to_zero( float32 STATUS_PARAM );
+unsigned int float32_to_uint32( float32 STATUS_PARAM );
+unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
+int64_t float32_to_int64( float32 STATUS_PARAM );
+int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM );
+float64 float32_to_float64( float32 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32 STATUS_PARAM );
+float32 float32_add( float32, float32 STATUS_PARAM );
+float32 float32_sub( float32, float32 STATUS_PARAM );
+float32 float32_mul( float32, float32 STATUS_PARAM );
+float32 float32_div( float32, float32 STATUS_PARAM );
+float32 float32_rem( float32, float32 STATUS_PARAM );
+float32 float32_sqrt( float32 STATUS_PARAM );
+char float32_eq( float32, float32 STATUS_PARAM );
+char float32_le( float32, float32 STATUS_PARAM );
+char float32_lt( float32, float32 STATUS_PARAM );
+char float32_eq_signaling( float32, float32 STATUS_PARAM );
+char float32_le_quiet( float32, float32 STATUS_PARAM );
+char float32_lt_quiet( float32, float32 STATUS_PARAM );
+char float32_compare( float32, float32 STATUS_PARAM );
+char float32_compare_quiet( float32, float32 STATUS_PARAM );
+char float32_is_signaling_nan( float32 );
+
+INLINE float32 float32_abs(float32 a)
+{
+ return a & 0x7fffffff;
+}
+
+INLINE float32 float32_chs(float32 a)
+{
+ return a ^ 0x80000000;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 STATUS_PARAM );
+int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
+unsigned int float64_to_uint32( float64 STATUS_PARAM );
+unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
+int64_t float64_to_int64( float64 STATUS_PARAM );
+int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
+float32 float64_to_float32( float64 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 STATUS_PARAM );
+float64 float64_add( float64, float64 STATUS_PARAM );
+float64 float64_sub( float64, float64 STATUS_PARAM );
+float64 float64_mul( float64, float64 STATUS_PARAM );
+float64 float64_div( float64, float64 STATUS_PARAM );
+float64 float64_rem( float64, float64 STATUS_PARAM );
+float64 float64_sqrt( float64 STATUS_PARAM );
+char float64_eq( float64, float64 STATUS_PARAM );
+char float64_le( float64, float64 STATUS_PARAM );
+char float64_lt( float64, float64 STATUS_PARAM );
+char float64_eq_signaling( float64, float64 STATUS_PARAM );
+char float64_le_quiet( float64, float64 STATUS_PARAM );
+char float64_lt_quiet( float64, float64 STATUS_PARAM );
+char float64_compare( float64, float64 STATUS_PARAM );
+char float64_compare_quiet( float64, float64 STATUS_PARAM );
+char float64_is_signaling_nan( float64 );
+
+INLINE float64 float64_abs(float64 a)
+{
+ return a & 0x7fffffffffffffffLL;
+}
+
+INLINE float64 float64_chs(float64 a)
+{
+ return a ^ 0x8000000000000000LL;
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 STATUS_PARAM );
+int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM );
+float32 floatx80_to_float32( floatx80 STATUS_PARAM );
+float64 floatx80_to_float64( floatx80 STATUS_PARAM );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
+floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
+char floatx80_eq( floatx80, floatx80 STATUS_PARAM );
+char floatx80_le( floatx80, floatx80 STATUS_PARAM );
+char floatx80_lt( floatx80, floatx80 STATUS_PARAM );
+char floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
+char floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
+char floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
+char floatx80_is_signaling_nan( floatx80 );
+
+INLINE floatx80 floatx80_abs(floatx80 a)
+{
+ a.high &= 0x7fff;
+ return a;
+}
+
+INLINE floatx80 floatx80_chs(floatx80 a)
+{
+ a.high ^= 0x8000;
+ return a;
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE quadruple-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float128_to_int32( float128 STATUS_PARAM );
+int float128_to_int32_round_to_zero( float128 STATUS_PARAM );
+int64_t float128_to_int64( float128 STATUS_PARAM );
+int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM );
+float32 float128_to_float32( float128 STATUS_PARAM );
+float64 float128_to_float64( float128 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float128_to_floatx80( float128 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE quadruple-precision operations.
+*----------------------------------------------------------------------------*/
+float128 float128_round_to_int( float128 STATUS_PARAM );
+float128 float128_add( float128, float128 STATUS_PARAM );
+float128 float128_sub( float128, float128 STATUS_PARAM );
+float128 float128_mul( float128, float128 STATUS_PARAM );
+float128 float128_div( float128, float128 STATUS_PARAM );
+float128 float128_rem( float128, float128 STATUS_PARAM );
+float128 float128_sqrt( float128 STATUS_PARAM );
+char float128_eq( float128, float128 STATUS_PARAM );
+char float128_le( float128, float128 STATUS_PARAM );
+char float128_lt( float128, float128 STATUS_PARAM );
+char float128_eq_signaling( float128, float128 STATUS_PARAM );
+char float128_le_quiet( float128, float128 STATUS_PARAM );
+char float128_lt_quiet( float128, float128 STATUS_PARAM );
+char float128_is_signaling_nan( float128 );
+
+INLINE float128 float128_abs(float128 a)
+{
+ a.high &= 0x7fffffffffffffffLL;
+ return a;
+}
+
+INLINE float128 float128_chs(float128 a)
+{
+ a.high ^= 0x8000000000000000LL;
+ return a;
+}
+
+#endif
+
+#else /* CONFIG_SOFTFLOAT */
+
+#include "softfloat-native.h"
+
+#endif /* !CONFIG_SOFTFLOAT */
+
+#endif /* !SOFTFLOAT_H */
diff --git a/gdbstub.c b/gdbstub.c
new file mode 100644
index 0000000..6ad393f
--- /dev/null
+++ b/gdbstub.c
@@ -0,0 +1,980 @@
+/*
+ * gdb server stub
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#ifdef CONFIG_USER_ONLY
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "qemu.h"
+#else
+#include "vl.h"
+#endif
+
+#include "qemu_socket.h"
+#ifdef _WIN32
+/* XXX: these constants may be independent of the host ones even for Unix */
+#ifndef SIGTRAP
+#define SIGTRAP 5
+#endif
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+#else
+#include <signal.h>
+#endif
+
+//#define DEBUG_GDB
+
+enum RSState {
+ RS_IDLE,
+ RS_GETLINE,
+ RS_CHKSUM1,
+ RS_CHKSUM2,
+};
+/* XXX: This is not thread safe. Do we care? */
+static int gdbserver_fd = -1;
+
+typedef struct GDBState {
+ CPUState *env; /* current CPU */
+ enum RSState state; /* parsing state */
+ int fd;
+ char line_buf[4096];
+ int line_buf_index;
+ int line_csum;
+#ifdef CONFIG_USER_ONLY
+ int running_state;
+#endif
+} GDBState;
+
+#ifdef CONFIG_USER_ONLY
+/* XXX: remove this hack. */
+static GDBState gdbserver_state;
+#endif
+
+static int get_char(GDBState *s)
+{
+ uint8_t ch;
+ int ret;
+
+ for(;;) {
+ ret = recv(s->fd, &ch, 1, 0);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ return -1;
+ } else {
+ break;
+ }
+ }
+ return ch;
+}
+
+static void put_buffer(GDBState *s, const uint8_t *buf, int len)
+{
+ int ret;
+
+ while (len > 0) {
+ ret = send(s->fd, buf, len, 0);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+}
+
+static inline int fromhex(int v)
+{
+ if (v >= '0' && v <= '9')
+ return v - '0';
+ else if (v >= 'A' && v <= 'F')
+ return v - 'A' + 10;
+ else if (v >= 'a' && v <= 'f')
+ return v - 'a' + 10;
+ else
+ return 0;
+}
+
+static inline int tohex(int v)
+{
+ if (v < 10)
+ return v + '0';
+ else
+ return v - 10 + 'a';
+}
+
+static void memtohex(char *buf, const uint8_t *mem, int len)
+{
+ int i, c;
+ char *q;
+ q = buf;
+ for(i = 0; i < len; i++) {
+ c = mem[i];
+ *q++ = tohex(c >> 4);
+ *q++ = tohex(c & 0xf);
+ }
+ *q = '\0';
+}
+
+static void hextomem(uint8_t *mem, const char *buf, int len)
+{
+ int i;
+
+ for(i = 0; i < len; i++) {
+ mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]);
+ buf += 2;
+ }
+}
+
+/* return -1 if error, 0 if OK */
+static int put_packet(GDBState *s, char *buf)
+{
+ char buf1[3];
+ int len, csum, ch, i;
+
+#ifdef DEBUG_GDB
+ printf("reply='%s'\n", buf);
+#endif
+
+ for(;;) {
+ buf1[0] = '$';
+ put_buffer(s, buf1, 1);
+ len = strlen(buf);
+ put_buffer(s, buf, len);
+ csum = 0;
+ for(i = 0; i < len; i++) {
+ csum += buf[i];
+ }
+ buf1[0] = '#';
+ buf1[1] = tohex((csum >> 4) & 0xf);
+ buf1[2] = tohex((csum) & 0xf);
+
+ put_buffer(s, buf1, 3);
+
+ ch = get_char(s);
+ if (ch < 0)
+ return -1;
+ if (ch == '+')
+ break;
+ }
+ return 0;
+}
+
+#if defined(TARGET_I386)
+
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ uint32_t *registers = (uint32_t *)mem_buf;
+ int i, fpus;
+
+ for(i = 0; i < 8; i++) {
+ registers[i] = env->regs[i];
+ }
+ registers[8] = env->eip;
+ registers[9] = env->eflags;
+ registers[10] = env->segs[R_CS].selector;
+ registers[11] = env->segs[R_SS].selector;
+ registers[12] = env->segs[R_DS].selector;
+ registers[13] = env->segs[R_ES].selector;
+ registers[14] = env->segs[R_FS].selector;
+ registers[15] = env->segs[R_GS].selector;
+ /* XXX: convert floats */
+ for(i = 0; i < 8; i++) {
+ memcpy(mem_buf + 16 * 4 + i * 10, &env->fpregs[i], 10);
+ }
+ registers[36] = env->fpuc;
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ registers[37] = fpus;
+ registers[38] = 0; /* XXX: convert tags */
+ registers[39] = 0; /* fiseg */
+ registers[40] = 0; /* fioff */
+ registers[41] = 0; /* foseg */
+ registers[42] = 0; /* fooff */
+ registers[43] = 0; /* fop */
+
+ for(i = 0; i < 16; i++)
+ tswapls(&registers[i]);
+ for(i = 36; i < 44; i++)
+ tswapls(&registers[i]);
+ return 44 * 4;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ uint32_t *registers = (uint32_t *)mem_buf;
+ int i;
+
+ for(i = 0; i < 8; i++) {
+ env->regs[i] = tswapl(registers[i]);
+ }
+ env->eip = tswapl(registers[8]);
+ env->eflags = tswapl(registers[9]);
+#if defined(CONFIG_USER_ONLY)
+#define LOAD_SEG(index, sreg)\
+ if (tswapl(registers[index]) != env->segs[sreg].selector)\
+ cpu_x86_load_seg(env, sreg, tswapl(registers[index]));
+ LOAD_SEG(10, R_CS);
+ LOAD_SEG(11, R_SS);
+ LOAD_SEG(12, R_DS);
+ LOAD_SEG(13, R_ES);
+ LOAD_SEG(14, R_FS);
+ LOAD_SEG(15, R_GS);
+#endif
+}
+
+#elif defined (TARGET_PPC)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ uint32_t *registers = (uint32_t *)mem_buf, tmp;
+ int i;
+
+ /* fill in gprs */
+ for(i = 0; i < 32; i++) {
+ registers[i] = tswapl(env->gpr[i]);
+ }
+ /* fill in fprs */
+ for (i = 0; i < 32; i++) {
+ registers[(i * 2) + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
+ registers[(i * 2) + 33] = tswapl(*((uint32_t *)&env->fpr[i] + 1));
+ }
+ /* nip, msr, ccr, lnk, ctr, xer, mq */
+ registers[96] = tswapl(env->nip);
+ registers[97] = tswapl(do_load_msr(env));
+ tmp = 0;
+ for (i = 0; i < 8; i++)
+ tmp |= env->crf[i] << (32 - ((i + 1) * 4));
+ registers[98] = tswapl(tmp);
+ registers[99] = tswapl(env->lr);
+ registers[100] = tswapl(env->ctr);
+ registers[101] = tswapl(do_load_xer(env));
+ registers[102] = 0;
+
+ return 103 * 4;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ uint32_t *registers = (uint32_t *)mem_buf;
+ int i;
+
+ /* fill in gprs */
+ for (i = 0; i < 32; i++) {
+ env->gpr[i] = tswapl(registers[i]);
+ }
+ /* fill in fprs */
+ for (i = 0; i < 32; i++) {
+ *((uint32_t *)&env->fpr[i]) = tswapl(registers[(i * 2) + 32]);
+ *((uint32_t *)&env->fpr[i] + 1) = tswapl(registers[(i * 2) + 33]);
+ }
+ /* nip, msr, ccr, lnk, ctr, xer, mq */
+ env->nip = tswapl(registers[96]);
+ do_store_msr(env, tswapl(registers[97]));
+ registers[98] = tswapl(registers[98]);
+ for (i = 0; i < 8; i++)
+ env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF;
+ env->lr = tswapl(registers[99]);
+ env->ctr = tswapl(registers[100]);
+ do_store_xer(env, tswapl(registers[101]));
+}
+#elif defined (TARGET_SPARC)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ target_ulong *registers = (target_ulong *)mem_buf;
+ int i;
+
+ /* fill in g0..g7 */
+ for(i = 0; i < 8; i++) {
+ registers[i] = tswapl(env->gregs[i]);
+ }
+ /* fill in register window */
+ for(i = 0; i < 24; i++) {
+ registers[i + 8] = tswapl(env->regwptr[i]);
+ }
+#ifndef TARGET_SPARC64
+ /* fill in fprs */
+ for (i = 0; i < 32; i++) {
+ registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
+ }
+ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+ registers[64] = tswapl(env->y);
+ {
+ target_ulong tmp;
+
+ tmp = GET_PSR(env);
+ registers[65] = tswapl(tmp);
+ }
+ registers[66] = tswapl(env->wim);
+ registers[67] = tswapl(env->tbr);
+ registers[68] = tswapl(env->pc);
+ registers[69] = tswapl(env->npc);
+ registers[70] = tswapl(env->fsr);
+ registers[71] = 0; /* csr */
+ registers[72] = 0;
+ return 73 * sizeof(target_ulong);
+#else
+ /* fill in fprs */
+ for (i = 0; i < 64; i += 2) {
+ uint64_t tmp;
+
+ tmp = (uint64_t)tswap32(*((uint32_t *)&env->fpr[i])) << 32;
+ tmp |= tswap32(*((uint32_t *)&env->fpr[i + 1]));
+ registers[i/2 + 32] = tmp;
+ }
+ registers[64] = tswapl(env->pc);
+ registers[65] = tswapl(env->npc);
+ registers[66] = tswapl(env->tstate[env->tl]);
+ registers[67] = tswapl(env->fsr);
+ registers[68] = tswapl(env->fprs);
+ registers[69] = tswapl(env->y);
+ return 70 * sizeof(target_ulong);
+#endif
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ target_ulong *registers = (target_ulong *)mem_buf;
+ int i;
+
+ /* fill in g0..g7 */
+ for(i = 0; i < 7; i++) {
+ env->gregs[i] = tswapl(registers[i]);
+ }
+ /* fill in register window */
+ for(i = 0; i < 24; i++) {
+ env->regwptr[i] = tswapl(registers[i + 8]);
+ }
+#ifndef TARGET_SPARC64
+ /* fill in fprs */
+ for (i = 0; i < 32; i++) {
+ *((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]);
+ }
+ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+ env->y = tswapl(registers[64]);
+ PUT_PSR(env, tswapl(registers[65]));
+ env->wim = tswapl(registers[66]);
+ env->tbr = tswapl(registers[67]);
+ env->pc = tswapl(registers[68]);
+ env->npc = tswapl(registers[69]);
+ env->fsr = tswapl(registers[70]);
+#else
+ for (i = 0; i < 64; i += 2) {
+ *((uint32_t *)&env->fpr[i]) = tswap32(registers[i/2 + 32] >> 32);
+ *((uint32_t *)&env->fpr[i + 1]) = tswap32(registers[i/2 + 32] & 0xffffffff);
+ }
+ env->pc = tswapl(registers[64]);
+ env->npc = tswapl(registers[65]);
+ env->tstate[env->tl] = tswapl(registers[66]);
+ env->fsr = tswapl(registers[67]);
+ env->fprs = tswapl(registers[68]);
+ env->y = tswapl(registers[69]);
+#endif
+}
+#elif defined (TARGET_ARM)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ int i;
+ uint8_t *ptr;
+
+ ptr = mem_buf;
+ /* 16 core integer registers (4 bytes each). */
+ for (i = 0; i < 16; i++)
+ {
+ *(uint32_t *)ptr = tswapl(env->regs[i]);
+ ptr += 4;
+ }
+ /* 8 FPA registers (12 bytes each), FPS (4 bytes).
+ Not yet implemented. */
+ memset (ptr, 0, 8 * 12 + 4);
+ ptr += 8 * 12 + 4;
+ /* CPSR (4 bytes). */
+ *(uint32_t *)ptr = tswapl (cpsr_read(env));
+ ptr += 4;
+
+ return ptr - mem_buf;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ int i;
+ uint8_t *ptr;
+
+ ptr = mem_buf;
+ /* Core integer registers. */
+ for (i = 0; i < 16; i++)
+ {
+ env->regs[i] = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+ }
+ /* Ignore FPA regs and scr. */
+ ptr += 8 * 12 + 4;
+ cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
+}
+#elif defined (TARGET_MIPS)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ int i;
+ uint8_t *ptr;
+
+ ptr = mem_buf;
+ for (i = 0; i < 32; i++)
+ {
+ *(uint32_t *)ptr = tswapl(env->gpr[i]);
+ ptr += 4;
+ }
+
+ *(uint32_t *)ptr = tswapl(env->CP0_Status);
+ ptr += 4;
+
+ *(uint32_t *)ptr = tswapl(env->LO);
+ ptr += 4;
+
+ *(uint32_t *)ptr = tswapl(env->HI);
+ ptr += 4;
+
+ *(uint32_t *)ptr = tswapl(env->CP0_BadVAddr);
+ ptr += 4;
+
+ *(uint32_t *)ptr = tswapl(env->CP0_Cause);
+ ptr += 4;
+
+ *(uint32_t *)ptr = tswapl(env->PC);
+ ptr += 4;
+
+ /* 32 FP registers, fsr, fir, fp. Not yet implemented. */
+
+ return ptr - mem_buf;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ int i;
+ uint8_t *ptr;
+
+ ptr = mem_buf;
+ for (i = 0; i < 32; i++)
+ {
+ env->gpr[i] = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+ }
+
+ env->CP0_Status = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ env->LO = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ env->HI = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ env->CP0_BadVAddr = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ env->CP0_Cause = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+
+ env->PC = tswapl(*(uint32_t *)ptr);
+ ptr += 4;
+}
+#elif defined (TARGET_SH4)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ uint32_t *ptr = (uint32_t *)mem_buf;
+ int i;
+
+#define SAVE(x) *ptr++=tswapl(x)
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
+ } else {
+ for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
+ }
+ for (i = 8; i < 16; i++) SAVE(env->gregs[i]);
+ SAVE (env->pc);
+ SAVE (env->pr);
+ SAVE (env->gbr);
+ SAVE (env->vbr);
+ SAVE (env->mach);
+ SAVE (env->macl);
+ SAVE (env->sr);
+ SAVE (0); /* TICKS */
+ SAVE (0); /* STALLS */
+ SAVE (0); /* CYCLES */
+ SAVE (0); /* INSTS */
+ SAVE (0); /* PLR */
+
+ return ((uint8_t *)ptr - mem_buf);
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ uint32_t *ptr = (uint32_t *)mem_buf;
+ int i;
+
+#define LOAD(x) (x)=*ptr++;
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
+ } else {
+ for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
+ }
+ for (i = 8; i < 16; i++) LOAD(env->gregs[i]);
+ LOAD (env->pc);
+ LOAD (env->pr);
+ LOAD (env->gbr);
+ LOAD (env->vbr);
+ LOAD (env->mach);
+ LOAD (env->macl);
+ LOAD (env->sr);
+}
+#else
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ return 0;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+}
+
+#endif
+
+static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
+{
+ const char *p;
+ int ch, reg_size, type;
+ char buf[4096];
+ uint8_t mem_buf[2000];
+ uint32_t *registers;
+ target_ulong addr, len;
+
+#ifdef DEBUG_GDB
+ printf("command='%s'\n", line_buf);
+#endif
+ p = line_buf;
+ ch = *p++;
+ switch(ch) {
+ case '?':
+ /* TODO: Make this return the correct value for user-mode. */
+ snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
+ put_packet(s, buf);
+ break;
+ case 'c':
+ if (*p != '\0') {
+ addr = strtoull(p, (char **)&p, 16);
+#if defined(TARGET_I386)
+ env->eip = addr;
+#elif defined (TARGET_PPC)
+ env->nip = addr;
+#elif defined (TARGET_SPARC)
+ env->pc = addr;
+ env->npc = addr + 4;
+#elif defined (TARGET_ARM)
+ env->regs[15] = addr;
+#elif defined (TARGET_SH4)
+ env->pc = addr;
+#endif
+ }
+#ifdef CONFIG_USER_ONLY
+ s->running_state = 1;
+#else
+ vm_start();
+#endif
+ return RS_IDLE;
+ case 's':
+ if (*p != '\0') {
+ addr = strtoul(p, (char **)&p, 16);
+#if defined(TARGET_I386)
+ env->eip = addr;
+#elif defined (TARGET_PPC)
+ env->nip = addr;
+#elif defined (TARGET_SPARC)
+ env->pc = addr;
+ env->npc = addr + 4;
+#elif defined (TARGET_ARM)
+ env->regs[15] = addr;
+#elif defined (TARGET_SH4)
+ env->pc = addr;
+#endif
+ }
+ cpu_single_step(env, 1);
+#ifdef CONFIG_USER_ONLY
+ s->running_state = 1;
+#else
+ vm_start();
+#endif
+ return RS_IDLE;
+ case 'g':
+ reg_size = cpu_gdb_read_registers(env, mem_buf);
+ memtohex(buf, mem_buf, reg_size);
+ put_packet(s, buf);
+ break;
+ case 'G':
+ registers = (void *)mem_buf;
+ len = strlen(p) / 2;
+ hextomem((uint8_t *)registers, p, len);
+ cpu_gdb_write_registers(env, mem_buf, len);
+ put_packet(s, "OK");
+ break;
+ case 'm':
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, NULL, 16);
+ if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) {
+ put_packet (s, "E14");
+ } else {
+ memtohex(buf, mem_buf, len);
+ put_packet(s, buf);
+ }
+ break;
+ case 'M':
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, (char **)&p, 16);
+ if (*p == ':')
+ p++;
+ hextomem(mem_buf, p, len);
+ if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
+ put_packet(s, "E14");
+ else
+ put_packet(s, "OK");
+ break;
+ case 'Z':
+ type = strtoul(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, (char **)&p, 16);
+ if (type == 0 || type == 1) {
+ if (cpu_breakpoint_insert(env, addr) < 0)
+ goto breakpoint_error;
+ put_packet(s, "OK");
+ } else {
+ breakpoint_error:
+ put_packet(s, "E22");
+ }
+ break;
+ case 'z':
+ type = strtoul(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, (char **)&p, 16);
+ if (type == 0 || type == 1) {
+ cpu_breakpoint_remove(env, addr);
+ put_packet(s, "OK");
+ } else {
+ goto breakpoint_error;
+ }
+ break;
+#ifdef CONFIG_USER_ONLY
+ case 'q':
+ if (strncmp(p, "Offsets", 7) == 0) {
+ TaskState *ts = env->opaque;
+
+ sprintf(buf, "Text=%x;Data=%x;Bss=%x", ts->info->code_offset,
+ ts->info->data_offset, ts->info->data_offset);
+ put_packet(s, buf);
+ break;
+ }
+ /* Fall through. */
+#endif
+ default:
+ // unknown_command:
+ /* put empty packet */
+ buf[0] = '\0';
+ put_packet(s, buf);
+ break;
+ }
+ return RS_IDLE;
+}
+
+extern void tb_flush(CPUState *env);
+
+#ifndef CONFIG_USER_ONLY
+static void gdb_vm_stopped(void *opaque, int reason)
+{
+ GDBState *s = opaque;
+ char buf[256];
+ int ret;
+
+ /* disable single step if it was enable */
+ cpu_single_step(s->env, 0);
+
+ if (reason == EXCP_DEBUG) {
+ tb_flush(s->env);
+ ret = SIGTRAP;
+ } else if (reason == EXCP_INTERRUPT) {
+ ret = SIGINT;
+ } else {
+ ret = 0;
+ }
+ snprintf(buf, sizeof(buf), "S%02x", ret);
+ put_packet(s, buf);
+}
+#endif
+
+static void gdb_read_byte(GDBState *s, int ch)
+{
+ CPUState *env = s->env;
+ int i, csum;
+ char reply[1];
+
+#ifndef CONFIG_USER_ONLY
+ if (vm_running) {
+ /* when the CPU is running, we cannot do anything except stop
+ it when receiving a char */
+ vm_stop(EXCP_INTERRUPT);
+ } else
+#endif
+ {
+ switch(s->state) {
+ case RS_IDLE:
+ if (ch == '$') {
+ s->line_buf_index = 0;
+ s->state = RS_GETLINE;
+ }
+ break;
+ case RS_GETLINE:
+ if (ch == '#') {
+ s->state = RS_CHKSUM1;
+ } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
+ s->state = RS_IDLE;
+ } else {
+ s->line_buf[s->line_buf_index++] = ch;
+ }
+ break;
+ case RS_CHKSUM1:
+ s->line_buf[s->line_buf_index] = '\0';
+ s->line_csum = fromhex(ch) << 4;
+ s->state = RS_CHKSUM2;
+ break;
+ case RS_CHKSUM2:
+ s->line_csum |= fromhex(ch);
+ csum = 0;
+ for(i = 0; i < s->line_buf_index; i++) {
+ csum += s->line_buf[i];
+ }
+ if (s->line_csum != (csum & 0xff)) {
+ reply[0] = '-';
+ put_buffer(s, reply, 1);
+ s->state = RS_IDLE;
+ } else {
+ reply[0] = '+';
+ put_buffer(s, reply, 1);
+ s->state = gdb_handle_packet(s, env, s->line_buf);
+ }
+ break;
+ }
+ }
+}
+
+#ifdef CONFIG_USER_ONLY
+int
+gdb_handlesig (CPUState *env, int sig)
+{
+ GDBState *s;
+ char buf[256];
+ int n;
+
+ if (gdbserver_fd < 0)
+ return sig;
+
+ s = &gdbserver_state;
+
+ /* disable single step if it was enabled */
+ cpu_single_step(env, 0);
+ tb_flush(env);
+
+ if (sig != 0)
+ {
+ snprintf(buf, sizeof(buf), "S%02x", sig);
+ put_packet(s, buf);
+ }
+
+ sig = 0;
+ s->state = RS_IDLE;
+ s->running_state = 0;
+ while (s->running_state == 0) {
+ n = read (s->fd, buf, 256);
+ if (n > 0)
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ gdb_read_byte (s, buf[i]);
+ }
+ else if (n == 0 || errno != EAGAIN)
+ {
+ /* XXX: Connection closed. Should probably wait for annother
+ connection before continuing. */
+ return sig;
+ }
+ }
+ return sig;
+}
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(CPUState *env, int code)
+{
+ GDBState *s;
+ char buf[4];
+
+ if (gdbserver_fd < 0)
+ return;
+
+ s = &gdbserver_state;
+
+ snprintf(buf, sizeof(buf), "W%02x", code);
+ put_packet(s, buf);
+}
+
+#else
+static void gdb_read(void *opaque)
+{
+ GDBState *s = opaque;
+ int i, size;
+ uint8_t buf[4096];
+
+ size = recv(s->fd, buf, sizeof(buf), 0);
+ if (size < 0)
+ return;
+ if (size == 0) {
+ /* end of connection */
+ qemu_del_vm_stop_handler(gdb_vm_stopped, s);
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ qemu_free(s);
+ vm_start();
+ } else {
+ for(i = 0; i < size; i++)
+ gdb_read_byte(s, buf[i]);
+ }
+}
+
+#endif
+
+static void gdb_accept(void *opaque)
+{
+ GDBState *s;
+ struct sockaddr_in sockaddr;
+ socklen_t len;
+ int val, fd;
+
+ for(;;) {
+ len = sizeof(sockaddr);
+ fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept");
+ return;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+
+ /* set short latency */
+ val = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+
+#ifdef CONFIG_USER_ONLY
+ s = &gdbserver_state;
+ memset (s, 0, sizeof (GDBState));
+#else
+ s = qemu_mallocz(sizeof(GDBState));
+ if (!s) {
+ close(fd);
+ return;
+ }
+#endif
+ s->env = first_cpu; /* XXX: allow to change CPU */
+ s->fd = fd;
+
+#ifdef CONFIG_USER_ONLY
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#else
+ socket_set_nonblock(fd);
+
+ /* stop the VM */
+ vm_stop(EXCP_INTERRUPT);
+
+ /* start handling I/O */
+ qemu_set_fd_handler(s->fd, gdb_read, NULL, s);
+ /* when the VM is stopped, the following callback is called */
+ qemu_add_vm_stop_handler(gdb_vm_stopped, s);
+#endif
+}
+
+static int gdbserver_open(int port)
+{
+ struct sockaddr_in sockaddr;
+ int fd, val, ret;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = 0;
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind");
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ return -1;
+ }
+#ifndef CONFIG_USER_ONLY
+ socket_set_nonblock(fd);
+#endif
+ return fd;
+}
+
+int gdbserver_start(int port)
+{
+ gdbserver_fd = gdbserver_open(port);
+ if (gdbserver_fd < 0)
+ return -1;
+ /* accept connections */
+#ifdef CONFIG_USER_ONLY
+ gdb_accept (NULL);
+#else
+ qemu_set_fd_handler(gdbserver_fd, gdb_accept, NULL, NULL);
+#endif
+ return 0;
+}
diff --git a/gdbstub.h b/gdbstub.h
new file mode 100644
index 0000000..7b42596
--- /dev/null
+++ b/gdbstub.h
@@ -0,0 +1,12 @@
+#ifndef GDBSTUB_H
+#define GDBSTUB_H
+
+#define DEFAULT_GDBSTUB_PORT 1234
+
+#ifdef CONFIG_USER_ONLY
+int gdb_handlesig (CPUState *, int);
+void gdb_exit(CPUState *, int);
+#endif
+int gdbserver_start(int);
+
+#endif
diff --git a/hw/acpi-dsdt.dsl b/hw/acpi-dsdt.dsl
new file mode 100644
index 0000000..fc4081f
--- /dev/null
+++ b/hw/acpi-dsdt.dsl
@@ -0,0 +1,559 @@
+/*
+ * QEMU ACPI DSDT ASL definition
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+DefinitionBlock (
+ "acpi-dsdt.aml", // Output Filename
+ "DSDT", // Signature
+ 0x01, // DSDT Compliance Revision
+ "QEMU", // OEMID
+ "QEMUDSDT", // TABLE ID
+ 0x1 // OEM Revision
+ )
+{
+ Scope (\)
+ {
+ /* CMOS memory access */
+ OperationRegion (CMS, SystemIO, 0x70, 0x02)
+ Field (CMS, ByteAcc, NoLock, Preserve)
+ {
+ CMSI, 8,
+ CMSD, 8
+ }
+ Method (CMRD, 1, NotSerialized)
+ {
+ Store (Arg0, CMSI)
+ Store (CMSD, Local0)
+ Return (Local0)
+ }
+
+ /* Debug Output */
+ OperationRegion (DBG, SystemIO, 0xb044, 0x04)
+ Field (DBG, DWordAcc, NoLock, Preserve)
+ {
+ DBGL, 32,
+ }
+ }
+
+
+ /* PCI Bus definition */
+ Scope(\_SB) {
+ Device(PCI0) {
+ Name (_HID, EisaId ("PNP0A03"))
+ Name (_ADR, 0x00)
+ Name (_UID, 1)
+ Name(_PRT, Package() {
+ /* PCI IRQ routing table, example from ACPI 2.0a specification,
+ section 6.2.8.1 */
+ /* Note: we provide the same info as the PCI routing
+ table of the Bochs BIOS */
+
+ // PCI Slot 0
+ Package() {0x0000ffff, 0, LNKD, 0},
+ Package() {0x0000ffff, 1, LNKA, 0},
+ Package() {0x0000ffff, 2, LNKB, 0},
+ Package() {0x0000ffff, 3, LNKC, 0},
+
+ // PCI Slot 1
+ Package() {0x0001ffff, 0, LNKA, 0},
+ Package() {0x0001ffff, 1, LNKB, 0},
+ Package() {0x0001ffff, 2, LNKC, 0},
+ Package() {0x0001ffff, 3, LNKD, 0},
+
+ // PCI Slot 2
+ Package() {0x0002ffff, 0, LNKB, 0},
+ Package() {0x0002ffff, 1, LNKC, 0},
+ Package() {0x0002ffff, 2, LNKD, 0},
+ Package() {0x0002ffff, 3, LNKA, 0},
+
+ // PCI Slot 3
+ Package() {0x0003ffff, 0, LNKC, 0},
+ Package() {0x0003ffff, 1, LNKD, 0},
+ Package() {0x0003ffff, 2, LNKA, 0},
+ Package() {0x0003ffff, 3, LNKB, 0},
+
+ // PCI Slot 4
+ Package() {0x0004ffff, 0, LNKD, 0},
+ Package() {0x0004ffff, 1, LNKA, 0},
+ Package() {0x0004ffff, 2, LNKB, 0},
+ Package() {0x0004ffff, 3, LNKC, 0},
+
+ // PCI Slot 5
+ Package() {0x0005ffff, 0, LNKA, 0},
+ Package() {0x0005ffff, 1, LNKB, 0},
+ Package() {0x0005ffff, 2, LNKC, 0},
+ Package() {0x0005ffff, 3, LNKD, 0},
+ })
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (MEMP, ResourceTemplate ()
+ {
+ WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
+ 0x0000, // Address Space Granularity
+ 0x0000, // Address Range Minimum
+ 0x00FF, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0x0100, // Address Length
+ ,, )
+ IO (Decode16,
+ 0x0CF8, // Address Range Minimum
+ 0x0CF8, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x08, // Address Length
+ )
+ WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+ 0x0000, // Address Space Granularity
+ 0x0000, // Address Range Minimum
+ 0x0CF7, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0x0CF8, // Address Length
+ ,, , TypeStatic)
+ WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+ 0x0000, // Address Space Granularity
+ 0x0D00, // Address Range Minimum
+ 0xFFFF, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0xF300, // Address Length
+ ,, , TypeStatic)
+ DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
+ 0x00000000, // Address Space Granularity
+ 0x000A0000, // Address Range Minimum
+ 0x000BFFFF, // Address Range Maximum
+ 0x00000000, // Address Translation Offset
+ 0x00020000, // Address Length
+ ,, , AddressRangeMemory, TypeStatic)
+ DWordMemory (ResourceProducer, PosDecode, MinNotFixed, MaxFixed, NonCacheable, ReadWrite,
+ 0x00000000, // Address Space Granularity
+ 0x00000000, // Address Range Minimum
+ 0xFEBFFFFF, // Address Range Maximum
+ 0x00000000, // Address Translation Offset
+ 0x00000000, // Address Length
+ ,, MEMF, AddressRangeMemory, TypeStatic)
+ })
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._MIN, PMIN)
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._MAX, PMAX)
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._LEN, PLEN)
+ /* compute available RAM */
+ Add(CMRD(0x34), ShiftLeft(CMRD(0x35), 8), Local0)
+ ShiftLeft(Local0, 16, Local0)
+ Add(Local0, 0x1000000, Local0)
+ /* update field of last region */
+ Store(Local0, PMIN)
+ Subtract (PMAX, PMIN, PLEN)
+ Increment (PLEN)
+ Return (MEMP)
+ }
+ }
+ }
+
+ Scope(\_SB.PCI0) {
+
+ /* PIIX3 ISA bridge */
+ Device (ISA) {
+ Name (_ADR, 0x00010000)
+
+ /* PIIX PCI to ISA irq remapping */
+ OperationRegion (P40C, PCI_Config, 0x60, 0x04)
+
+
+ /* Keyboard seems to be important for WinXP install */
+ Device (KBD)
+ {
+ Name (_HID, EisaId ("PNP0303"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0f)
+ }
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (TMP, ResourceTemplate ()
+ {
+ IO (Decode16,
+ 0x0060, // Address Range Minimum
+ 0x0060, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x01, // Address Length
+ )
+ IO (Decode16,
+ 0x0064, // Address Range Minimum
+ 0x0064, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x01, // Address Length
+ )
+ IRQNoFlags ()
+ {1}
+ })
+ Return (TMP)
+ }
+ }
+
+ /* PS/2 mouse */
+ Device (MOU)
+ {
+ Name (_HID, EisaId ("PNP0F13"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0f)
+ }
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (TMP, ResourceTemplate ()
+ {
+ IRQNoFlags () {12}
+ })
+ Return (TMP)
+ }
+ }
+
+ /* PS/2 floppy controller */
+ Device (FDC0)
+ {
+ Name (_HID, EisaId ("PNP0700"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0F)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x03F2, 0x03F2, 0x00, 0x04)
+ IO (Decode16, 0x03F7, 0x03F7, 0x00, 0x01)
+ IRQNoFlags () {6}
+ DMA (Compatibility, NotBusMaster, Transfer8) {2}
+ })
+ Return (BUF0)
+ }
+ }
+
+ /* Parallel port */
+ Device (LPT)
+ {
+ Name (_HID, EisaId ("PNP0400"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSA, Local0)
+ And (Local0, 0x80000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x0378, 0x0378, 0x08, 0x08)
+ IRQNoFlags () {7}
+ })
+ Return (BUF0)
+ }
+ }
+
+ /* Serial Ports */
+ Device (COM1)
+ {
+ Name (_HID, EisaId ("PNP0501"))
+ Name (_UID, 0x01)
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSC, Local0)
+ And (Local0, 0x08000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x03F8, 0x03F8, 0x00, 0x08)
+ IRQNoFlags () {4}
+ })
+ Return (BUF0)
+ }
+ }
+
+ Device (COM2)
+ {
+ Name (_HID, EisaId ("PNP0501"))
+ Name (_UID, 0x02)
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSC, Local0)
+ And (Local0, 0x80000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x02F8, 0x02F8, 0x00, 0x08)
+ IRQNoFlags () {3}
+ })
+ Return (BUF0)
+ }
+ }
+ }
+
+ /* PIIX4 PM */
+ Device (PX13) {
+ Name (_ADR, 0x00010003)
+
+ OperationRegion (P13C, PCI_Config, 0x5c, 0x24)
+ Field (P13C, DWordAcc, NoLock, Preserve)
+ {
+ DRSA, 32,
+ DRSB, 32,
+ DRSC, 32,
+ DRSE, 32,
+ DRSF, 32,
+ DRSG, 32,
+ DRSH, 32,
+ DRSI, 32,
+ DRSJ, 32
+ }
+ }
+ }
+
+ /* PCI IRQs */
+ Scope(\_SB) {
+ Field (\_SB.PCI0.ISA.P40C, ByteAcc, NoLock, Preserve)
+ {
+ PRQ0, 8,
+ PRQ1, 8,
+ PRQ2, 8,
+ PRQ3, 8
+ }
+
+ Device(LNKA){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 1)
+ Name(_PRS, ResourceTemplate(){
+ IRQ (Level, ActiveLow, Shared)
+ {3,4,5,6,7,9,10,11,12}
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ0, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ0, 0x80, PRQ0)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ IRQ (Level, ActiveLow, Shared)
+ {1}
+ })
+ CreateWordField (PRR0, 0x01, TMP)
+ Store (PRQ0, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ ShiftLeft (One, Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateWordField (Arg0, 0x01, TMP)
+ FindSetRightBit (TMP, Local0)
+ Decrement (Local0)
+ Store (Local0, PRQ0)
+ }
+ }
+ Device(LNKB){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 2)
+ Name(_PRS, ResourceTemplate(){
+ IRQ (Level, ActiveLow, Shared)
+ {3,4,5,6,7,9,10,11,12}
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ1, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ1, 0x80, PRQ1)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ IRQ (Level, ActiveLow, Shared)
+ {1}
+ })
+ CreateWordField (PRR0, 0x01, TMP)
+ Store (PRQ1, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ ShiftLeft (One, Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateWordField (Arg0, 0x01, TMP)
+ FindSetRightBit (TMP, Local0)
+ Decrement (Local0)
+ Store (Local0, PRQ1)
+ }
+ }
+ Device(LNKC){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 3)
+ Name(_PRS, ResourceTemplate(){
+ IRQ (Level, ActiveLow, Shared)
+ {3,4,5,6,7,9,10,11,12}
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ2, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ2, 0x80, PRQ2)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ IRQ (Level, ActiveLow, Shared)
+ {1}
+ })
+ CreateWordField (PRR0, 0x01, TMP)
+ Store (PRQ2, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ ShiftLeft (One, Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateWordField (Arg0, 0x01, TMP)
+ FindSetRightBit (TMP, Local0)
+ Decrement (Local0)
+ Store (Local0, PRQ2)
+ }
+ }
+ Device(LNKD){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 4)
+ Name(_PRS, ResourceTemplate(){
+ IRQ (Level, ActiveLow, Shared)
+ {3,4,5,6,7,9,10,11,12}
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ3, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ3, 0x80, PRQ3)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ IRQ (Level, ActiveLow, Shared)
+ {1}
+ })
+ CreateWordField (PRR0, 0x01, TMP)
+ Store (PRQ3, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ ShiftLeft (One, Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateWordField (Arg0, 0x01, TMP)
+ FindSetRightBit (TMP, Local0)
+ Decrement (Local0)
+ Store (Local0, PRQ3)
+ }
+ }
+ }
+
+ /* S5 = power off state */
+ Name (_S5, Package (4) {
+ 0x00, // PM1a_CNT.SLP_TYP
+ 0x00, // PM2a_CNT.SLP_TYP
+ 0x00, // reserved
+ 0x00, // reserved
+ })
+}
diff --git a/hw/acpi-dsdt.hex b/hw/acpi-dsdt.hex
new file mode 100644
index 0000000..f4f50bd
--- /dev/null
+++ b/hw/acpi-dsdt.hex
@@ -0,0 +1,278 @@
+/*
+ *
+ * Intel ACPI Component Architecture
+ * ASL Optimizing Compiler version 20060421 [Apr 29 2006]
+ * Copyright (C) 2000 - 2006 Intel Corporation
+ * Supports ACPI Specification Revision 3.0a
+ *
+ * Compilation of "/usr/local/home/bellard/qemu-current/hw/acpi-dsdt.dsl" - Wed Jun 14 20:09:53 2006
+ *
+ * C source code output
+ *
+ */
+unsigned char AmlCode[] =
+{
+ 0x44,0x53,0x44,0x54,0x32,0x08,0x00,0x00, /* 00000000 "DSDT2..." */
+ 0x01,0x5B,0x51,0x45,0x4D,0x55,0x00,0x00, /* 00000008 ".[QEMU.." */
+ 0x51,0x45,0x4D,0x55,0x44,0x53,0x44,0x54, /* 00000010 "QEMUDSDT" */
+ 0x01,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
+ 0x21,0x04,0x06,0x20,0x10,0x4F,0x04,0x5C, /* 00000020 "!.. .O.\" */
+ 0x00,0x5B,0x80,0x43,0x4D,0x53,0x5F,0x01, /* 00000028 ".[.CMS_." */
+ 0x0A,0x70,0x0A,0x02,0x5B,0x81,0x10,0x43, /* 00000030 ".p..[..C" */
+ 0x4D,0x53,0x5F,0x01,0x43,0x4D,0x53,0x49, /* 00000038 "MS_.CMSI" */
+ 0x08,0x43,0x4D,0x53,0x44,0x08,0x14,0x14, /* 00000040 ".CMSD..." */
+ 0x43,0x4D,0x52,0x44,0x01,0x70,0x68,0x43, /* 00000048 "CMRD.phC" */
+ 0x4D,0x53,0x49,0x70,0x43,0x4D,0x53,0x44, /* 00000050 "MSIpCMSD" */
+ 0x60,0xA4,0x60,0x5B,0x80,0x44,0x42,0x47, /* 00000058 "`.`[.DBG" */
+ 0x5F,0x01,0x0B,0x44,0xB0,0x0A,0x04,0x5B, /* 00000060 "_..D...[" */
+ 0x81,0x0B,0x44,0x42,0x47,0x5F,0x03,0x44, /* 00000068 "..DBG_.D" */
+ 0x42,0x47,0x4C,0x20,0x10,0x4E,0x25,0x5F, /* 00000070 "BGL .N%_" */
+ 0x53,0x42,0x5F,0x5B,0x82,0x46,0x25,0x50, /* 00000078 "SB_[.F%P" */
+ 0x43,0x49,0x30,0x08,0x5F,0x48,0x49,0x44, /* 00000080 "CI0._HID" */
+ 0x0C,0x41,0xD0,0x0A,0x03,0x08,0x5F,0x41, /* 00000088 ".A...._A" */
+ 0x44,0x52,0x00,0x08,0x5F,0x55,0x49,0x44, /* 00000090 "DR.._UID" */
+ 0x01,0x08,0x5F,0x50,0x52,0x54,0x12,0x47, /* 00000098 ".._PRT.G" */
+ 0x15,0x18,0x12,0x0B,0x04,0x0B,0xFF,0xFF, /* 000000A0 "........" */
+ 0x00,0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0B, /* 000000A8 ".LNKD..." */
+ 0x04,0x0B,0xFF,0xFF,0x01,0x4C,0x4E,0x4B, /* 000000B0 ".....LNK" */
+ 0x41,0x00,0x12,0x0C,0x04,0x0B,0xFF,0xFF, /* 000000B8 "A......." */
+ 0x0A,0x02,0x4C,0x4E,0x4B,0x42,0x00,0x12, /* 000000C0 "..LNKB.." */
+ 0x0C,0x04,0x0B,0xFF,0xFF,0x0A,0x03,0x4C, /* 000000C8 ".......L" */
+ 0x4E,0x4B,0x43,0x00,0x12,0x0D,0x04,0x0C, /* 000000D0 "NKC....." */
+ 0xFF,0xFF,0x01,0x00,0x00,0x4C,0x4E,0x4B, /* 000000D8 ".....LNK" */
+ 0x41,0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF, /* 000000E0 "A......." */
+ 0x01,0x00,0x01,0x4C,0x4E,0x4B,0x42,0x00, /* 000000E8 "...LNKB." */
+ 0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x01,0x00, /* 000000F0 "........" */
+ 0x0A,0x02,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 000000F8 "..LNKC.." */
+ 0x0E,0x04,0x0C,0xFF,0xFF,0x01,0x00,0x0A, /* 00000100 "........" */
+ 0x03,0x4C,0x4E,0x4B,0x44,0x00,0x12,0x0D, /* 00000108 ".LNKD..." */
+ 0x04,0x0C,0xFF,0xFF,0x02,0x00,0x00,0x4C, /* 00000110 ".......L" */
+ 0x4E,0x4B,0x42,0x00,0x12,0x0D,0x04,0x0C, /* 00000118 "NKB....." */
+ 0xFF,0xFF,0x02,0x00,0x01,0x4C,0x4E,0x4B, /* 00000120 ".....LNK" */
+ 0x43,0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF, /* 00000128 "C......." */
+ 0x02,0x00,0x0A,0x02,0x4C,0x4E,0x4B,0x44, /* 00000130 "....LNKD" */
+ 0x00,0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x02, /* 00000138 "........" */
+ 0x00,0x0A,0x03,0x4C,0x4E,0x4B,0x41,0x00, /* 00000140 "...LNKA." */
+ 0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x03,0x00, /* 00000148 "........" */
+ 0x00,0x4C,0x4E,0x4B,0x43,0x00,0x12,0x0D, /* 00000150 ".LNKC..." */
+ 0x04,0x0C,0xFF,0xFF,0x03,0x00,0x01,0x4C, /* 00000158 ".......L" */
+ 0x4E,0x4B,0x44,0x00,0x12,0x0E,0x04,0x0C, /* 00000160 "NKD....." */
+ 0xFF,0xFF,0x03,0x00,0x0A,0x02,0x4C,0x4E, /* 00000168 "......LN" */
+ 0x4B,0x41,0x00,0x12,0x0E,0x04,0x0C,0xFF, /* 00000170 "KA......" */
+ 0xFF,0x03,0x00,0x0A,0x03,0x4C,0x4E,0x4B, /* 00000178 ".....LNK" */
+ 0x42,0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF, /* 00000180 "B......." */
+ 0x04,0x00,0x00,0x4C,0x4E,0x4B,0x44,0x00, /* 00000188 "...LNKD." */
+ 0x12,0x0D,0x04,0x0C,0xFF,0xFF,0x04,0x00, /* 00000190 "........" */
+ 0x01,0x4C,0x4E,0x4B,0x41,0x00,0x12,0x0E, /* 00000198 ".LNKA..." */
+ 0x04,0x0C,0xFF,0xFF,0x04,0x00,0x0A,0x02, /* 000001A0 "........" */
+ 0x4C,0x4E,0x4B,0x42,0x00,0x12,0x0E,0x04, /* 000001A8 "LNKB...." */
+ 0x0C,0xFF,0xFF,0x04,0x00,0x0A,0x03,0x4C, /* 000001B0 ".......L" */
+ 0x4E,0x4B,0x43,0x00,0x12,0x0D,0x04,0x0C, /* 000001B8 "NKC....." */
+ 0xFF,0xFF,0x05,0x00,0x00,0x4C,0x4E,0x4B, /* 000001C0 ".....LNK" */
+ 0x41,0x00,0x12,0x0D,0x04,0x0C,0xFF,0xFF, /* 000001C8 "A......." */
+ 0x05,0x00,0x01,0x4C,0x4E,0x4B,0x42,0x00, /* 000001D0 "...LNKB." */
+ 0x12,0x0E,0x04,0x0C,0xFF,0xFF,0x05,0x00, /* 000001D8 "........" */
+ 0x0A,0x02,0x4C,0x4E,0x4B,0x43,0x00,0x12, /* 000001E0 "..LNKC.." */
+ 0x0E,0x04,0x0C,0xFF,0xFF,0x05,0x00,0x0A, /* 000001E8 "........" */
+ 0x03,0x4C,0x4E,0x4B,0x44,0x00,0x14,0x4C, /* 000001F0 ".LNKD..L" */
+ 0x0D,0x5F,0x43,0x52,0x53,0x00,0x08,0x4D, /* 000001F8 "._CRS..M" */
+ 0x45,0x4D,0x50,0x11,0x42,0x07,0x0A,0x6E, /* 00000200 "EMP.B..n" */
+ 0x88,0x0D,0x00,0x02,0x0C,0x00,0x00,0x00, /* 00000208 "........" */
+ 0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x01, /* 00000210 "........" */
+ 0x47,0x01,0xF8,0x0C,0xF8,0x0C,0x01,0x08, /* 00000218 "G......." */
+ 0x88,0x0D,0x00,0x01,0x0C,0x03,0x00,0x00, /* 00000220 "........" */
+ 0x00,0x00,0xF7,0x0C,0x00,0x00,0xF8,0x0C, /* 00000228 "........" */
+ 0x88,0x0D,0x00,0x01,0x0C,0x03,0x00,0x00, /* 00000230 "........" */
+ 0x00,0x0D,0xFF,0xFF,0x00,0x00,0x00,0xF3, /* 00000238 "........" */
+ 0x87,0x17,0x00,0x00,0x0C,0x03,0x00,0x00, /* 00000240 "........" */
+ 0x00,0x00,0x00,0x00,0x0A,0x00,0xFF,0xFF, /* 00000248 "........" */
+ 0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 00000250 "........" */
+ 0x02,0x00,0x87,0x17,0x00,0x00,0x08,0x01, /* 00000258 "........" */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 00000260 "........" */
+ 0xFF,0xFF,0xBF,0xFE,0x00,0x00,0x00,0x00, /* 00000268 "........" */
+ 0x00,0x00,0x00,0x00,0x79,0x00,0x8A,0x4D, /* 00000270 "....y..M" */
+ 0x45,0x4D,0x50,0x0A,0x5C,0x50,0x4D,0x49, /* 00000278 "EMP.\PMI" */
+ 0x4E,0x8A,0x4D,0x45,0x4D,0x50,0x0A,0x60, /* 00000280 "N.MEMP.`" */
+ 0x50,0x4D,0x41,0x58,0x8A,0x4D,0x45,0x4D, /* 00000288 "PMAX.MEM" */
+ 0x50,0x0A,0x68,0x50,0x4C,0x45,0x4E,0x72, /* 00000290 "P.hPLENr" */
+ 0x43,0x4D,0x52,0x44,0x0A,0x34,0x79,0x43, /* 00000298 "CMRD.4yC" */
+ 0x4D,0x52,0x44,0x0A,0x35,0x0A,0x08,0x00, /* 000002A0 "MRD.5..." */
+ 0x60,0x79,0x60,0x0A,0x10,0x60,0x72,0x60, /* 000002A8 "`y`..`r`" */
+ 0x0C,0x00,0x00,0x00,0x01,0x60,0x70,0x60, /* 000002B0 ".....`p`" */
+ 0x50,0x4D,0x49,0x4E,0x74,0x50,0x4D,0x41, /* 000002B8 "PMINtPMA" */
+ 0x58,0x50,0x4D,0x49,0x4E,0x50,0x4C,0x45, /* 000002C0 "XPMINPLE" */
+ 0x4E,0x75,0x50,0x4C,0x45,0x4E,0xA4,0x4D, /* 000002C8 "NuPLEN.M" */
+ 0x45,0x4D,0x50,0x10,0x42,0x26,0x2E,0x5F, /* 000002D0 "EMP.B&._" */
+ 0x53,0x42,0x5F,0x50,0x43,0x49,0x30,0x5B, /* 000002D8 "SB_PCI0[" */
+ 0x82,0x43,0x20,0x49,0x53,0x41,0x5F,0x08, /* 000002E0 ".C ISA_." */
+ 0x5F,0x41,0x44,0x52,0x0C,0x00,0x00,0x01, /* 000002E8 "_ADR...." */
+ 0x00,0x5B,0x80,0x50,0x34,0x30,0x43,0x02, /* 000002F0 ".[.P40C." */
+ 0x0A,0x60,0x0A,0x04,0x5B,0x82,0x44,0x04, /* 000002F8 ".`..[.D." */
+ 0x4B,0x42,0x44,0x5F,0x08,0x5F,0x48,0x49, /* 00000300 "KBD_._HI" */
+ 0x44,0x0C,0x41,0xD0,0x03,0x03,0x14,0x09, /* 00000308 "D.A....." */
+ 0x5F,0x53,0x54,0x41,0x00,0xA4,0x0A,0x0F, /* 00000310 "_STA...." */
+ 0x14,0x29,0x5F,0x43,0x52,0x53,0x00,0x08, /* 00000318 ".)_CRS.." */
+ 0x54,0x4D,0x50,0x5F,0x11,0x18,0x0A,0x15, /* 00000320 "TMP_...." */
+ 0x47,0x01,0x60,0x00,0x60,0x00,0x01,0x01, /* 00000328 "G.`.`..." */
+ 0x47,0x01,0x64,0x00,0x64,0x00,0x01,0x01, /* 00000330 "G.d.d..." */
+ 0x22,0x02,0x00,0x79,0x00,0xA4,0x54,0x4D, /* 00000338 ""..y..TM" */
+ 0x50,0x5F,0x5B,0x82,0x33,0x4D,0x4F,0x55, /* 00000340 "P_[.3MOU" */
+ 0x5F,0x08,0x5F,0x48,0x49,0x44,0x0C,0x41, /* 00000348 "_._HID.A" */
+ 0xD0,0x0F,0x13,0x14,0x09,0x5F,0x53,0x54, /* 00000350 "....._ST" */
+ 0x41,0x00,0xA4,0x0A,0x0F,0x14,0x19,0x5F, /* 00000358 "A......_" */
+ 0x43,0x52,0x53,0x00,0x08,0x54,0x4D,0x50, /* 00000360 "CRS..TMP" */
+ 0x5F,0x11,0x08,0x0A,0x05,0x22,0x00,0x10, /* 00000368 "_....".." */
+ 0x79,0x00,0xA4,0x54,0x4D,0x50,0x5F,0x5B, /* 00000370 "y..TMP_[" */
+ 0x82,0x47,0x04,0x46,0x44,0x43,0x30,0x08, /* 00000378 ".G.FDC0." */
+ 0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0,0x07, /* 00000380 "_HID.A.." */
+ 0x00,0x14,0x09,0x5F,0x53,0x54,0x41,0x00, /* 00000388 "..._STA." */
+ 0xA4,0x0A,0x0F,0x14,0x2C,0x5F,0x43,0x52, /* 00000390 "....,_CR" */
+ 0x53,0x00,0x08,0x42,0x55,0x46,0x30,0x11, /* 00000398 "S..BUF0." */
+ 0x1B,0x0A,0x18,0x47,0x01,0xF2,0x03,0xF2, /* 000003A0 "...G...." */
+ 0x03,0x00,0x04,0x47,0x01,0xF7,0x03,0xF7, /* 000003A8 "...G...." */
+ 0x03,0x00,0x01,0x22,0x40,0x00,0x2A,0x04, /* 000003B0 "..."@.*." */
+ 0x00,0x79,0x00,0xA4,0x42,0x55,0x46,0x30, /* 000003B8 ".y..BUF0" */
+ 0x5B,0x82,0x4B,0x05,0x4C,0x50,0x54,0x5F, /* 000003C0 "[.K.LPT_" */
+ 0x08,0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0, /* 000003C8 "._HID.A." */
+ 0x04,0x00,0x14,0x28,0x5F,0x53,0x54,0x41, /* 000003D0 "...(_STA" */
+ 0x00,0x70,0x5E,0x5E,0x5E,0x2E,0x50,0x58, /* 000003D8 ".p^^^.PX" */
+ 0x31,0x33,0x44,0x52,0x53,0x41,0x60,0x7B, /* 000003E0 "13DRSA`{" */
+ 0x60,0x0C,0x00,0x00,0x00,0x80,0x60,0xA0, /* 000003E8 "`.....`." */
+ 0x06,0x93,0x60,0x00,0xA4,0x00,0xA1,0x04, /* 000003F0 "..`....." */
+ 0xA4,0x0A,0x0F,0x14,0x21,0x5F,0x43,0x52, /* 000003F8 "....!_CR" */
+ 0x53,0x00,0x08,0x42,0x55,0x46,0x30,0x11, /* 00000400 "S..BUF0." */
+ 0x10,0x0A,0x0D,0x47,0x01,0x78,0x03,0x78, /* 00000408 "...G.x.x" */
+ 0x03,0x08,0x08,0x22,0x80,0x00,0x79,0x00, /* 00000410 "..."..y." */
+ 0xA4,0x42,0x55,0x46,0x30,0x5B,0x82,0x41, /* 00000418 ".BUF0[.A" */
+ 0x06,0x43,0x4F,0x4D,0x31,0x08,0x5F,0x48, /* 00000420 ".COM1._H" */
+ 0x49,0x44,0x0C,0x41,0xD0,0x05,0x01,0x08, /* 00000428 "ID.A...." */
+ 0x5F,0x55,0x49,0x44,0x01,0x14,0x28,0x5F, /* 00000430 "_UID..(_" */
+ 0x53,0x54,0x41,0x00,0x70,0x5E,0x5E,0x5E, /* 00000438 "STA.p^^^" */
+ 0x2E,0x50,0x58,0x31,0x33,0x44,0x52,0x53, /* 00000440 ".PX13DRS" */
+ 0x43,0x60,0x7B,0x60,0x0C,0x00,0x00,0x00, /* 00000448 "C`{`...." */
+ 0x08,0x60,0xA0,0x06,0x93,0x60,0x00,0xA4, /* 00000450 ".`...`.." */
+ 0x00,0xA1,0x04,0xA4,0x0A,0x0F,0x14,0x21, /* 00000458 ".......!" */
+ 0x5F,0x43,0x52,0x53,0x00,0x08,0x42,0x55, /* 00000460 "_CRS..BU" */
+ 0x46,0x30,0x11,0x10,0x0A,0x0D,0x47,0x01, /* 00000468 "F0....G." */
+ 0xF8,0x03,0xF8,0x03,0x00,0x08,0x22,0x10, /* 00000470 "......"." */
+ 0x00,0x79,0x00,0xA4,0x42,0x55,0x46,0x30, /* 00000478 ".y..BUF0" */
+ 0x5B,0x82,0x42,0x06,0x43,0x4F,0x4D,0x32, /* 00000480 "[.B.COM2" */
+ 0x08,0x5F,0x48,0x49,0x44,0x0C,0x41,0xD0, /* 00000488 "._HID.A." */
+ 0x05,0x01,0x08,0x5F,0x55,0x49,0x44,0x0A, /* 00000490 "..._UID." */
+ 0x02,0x14,0x28,0x5F,0x53,0x54,0x41,0x00, /* 00000498 "..(_STA." */
+ 0x70,0x5E,0x5E,0x5E,0x2E,0x50,0x58,0x31, /* 000004A0 "p^^^.PX1" */
+ 0x33,0x44,0x52,0x53,0x43,0x60,0x7B,0x60, /* 000004A8 "3DRSC`{`" */
+ 0x0C,0x00,0x00,0x00,0x80,0x60,0xA0,0x06, /* 000004B0 ".....`.." */
+ 0x93,0x60,0x00,0xA4,0x00,0xA1,0x04,0xA4, /* 000004B8 ".`......" */
+ 0x0A,0x0F,0x14,0x21,0x5F,0x43,0x52,0x53, /* 000004C0 "...!_CRS" */
+ 0x00,0x08,0x42,0x55,0x46,0x30,0x11,0x10, /* 000004C8 "..BUF0.." */
+ 0x0A,0x0D,0x47,0x01,0xF8,0x02,0xF8,0x02, /* 000004D0 "..G....." */
+ 0x00,0x08,0x22,0x08,0x00,0x79,0x00,0xA4, /* 000004D8 ".."..y.." */
+ 0x42,0x55,0x46,0x30,0x5B,0x82,0x40,0x05, /* 000004E0 "BUF0[.@." */
+ 0x50,0x58,0x31,0x33,0x08,0x5F,0x41,0x44, /* 000004E8 "PX13._AD" */
+ 0x52,0x0C,0x03,0x00,0x01,0x00,0x5B,0x80, /* 000004F0 "R.....[." */
+ 0x50,0x31,0x33,0x43,0x02,0x0A,0x5C,0x0A, /* 000004F8 "P13C..\." */
+ 0x24,0x5B,0x81,0x33,0x50,0x31,0x33,0x43, /* 00000500 "$[.3P13C" */
+ 0x03,0x44,0x52,0x53,0x41,0x20,0x44,0x52, /* 00000508 ".DRSA DR" */
+ 0x53,0x42,0x20,0x44,0x52,0x53,0x43,0x20, /* 00000510 "SB DRSC " */
+ 0x44,0x52,0x53,0x45,0x20,0x44,0x52,0x53, /* 00000518 "DRSE DRS" */
+ 0x46,0x20,0x44,0x52,0x53,0x47,0x20,0x44, /* 00000520 "F DRSG D" */
+ 0x52,0x53,0x48,0x20,0x44,0x52,0x53,0x49, /* 00000528 "RSH DRSI" */
+ 0x20,0x44,0x52,0x53,0x4A,0x20,0x10,0x4F, /* 00000530 " DRSJ .O" */
+ 0x2E,0x5F,0x53,0x42,0x5F,0x5B,0x81,0x24, /* 00000538 "._SB_[.$" */
+ 0x2F,0x03,0x50,0x43,0x49,0x30,0x49,0x53, /* 00000540 "/.PCI0IS" */
+ 0x41,0x5F,0x50,0x34,0x30,0x43,0x01,0x50, /* 00000548 "A_P40C.P" */
+ 0x52,0x51,0x30,0x08,0x50,0x52,0x51,0x31, /* 00000550 "RQ0.PRQ1" */
+ 0x08,0x50,0x52,0x51,0x32,0x08,0x50,0x52, /* 00000558 ".PRQ2.PR" */
+ 0x51,0x33,0x08,0x5B,0x82,0x4E,0x0A,0x4C, /* 00000560 "Q3.[.N.L" */
+ 0x4E,0x4B,0x41,0x08,0x5F,0x48,0x49,0x44, /* 00000568 "NKA._HID" */
+ 0x0C,0x41,0xD0,0x0C,0x0F,0x08,0x5F,0x55, /* 00000570 ".A...._U" */
+ 0x49,0x44,0x01,0x08,0x5F,0x50,0x52,0x53, /* 00000578 "ID.._PRS" */
+ 0x11,0x09,0x0A,0x06,0x23,0xF8,0x1E,0x18, /* 00000580 "....#..." */
+ 0x79,0x00,0x14,0x1A,0x5F,0x53,0x54,0x41, /* 00000588 "y..._STA" */
+ 0x00,0x70,0x0A,0x0B,0x60,0xA0,0x0D,0x7B, /* 00000590 ".p..`..{" */
+ 0x0A,0x80,0x50,0x52,0x51,0x30,0x61,0x70, /* 00000598 "..PRQ0ap" */
+ 0x0A,0x09,0x60,0xA4,0x60,0x14,0x11,0x5F, /* 000005A0 "..`.`.._" */
+ 0x44,0x49,0x53,0x00,0x7D,0x50,0x52,0x51, /* 000005A8 "DIS.}PRQ" */
+ 0x30,0x0A,0x80,0x50,0x52,0x51,0x30,0x14, /* 000005B0 "0..PRQ0." */
+ 0x3F,0x5F,0x43,0x52,0x53,0x00,0x08,0x50, /* 000005B8 "?_CRS..P" */
+ 0x52,0x52,0x30,0x11,0x09,0x0A,0x06,0x23, /* 000005C0 "RR0....#" */
+ 0x02,0x00,0x18,0x79,0x00,0x8B,0x50,0x52, /* 000005C8 "...y..PR" */
+ 0x52,0x30,0x01,0x54,0x4D,0x50,0x5F,0x70, /* 000005D0 "R0.TMP_p" */
+ 0x50,0x52,0x51,0x30,0x60,0xA0,0x0C,0x95, /* 000005D8 "PRQ0`..." */
+ 0x60,0x0A,0x80,0x79,0x01,0x60,0x54,0x4D, /* 000005E0 "`..y.`TM" */
+ 0x50,0x5F,0xA1,0x07,0x70,0x00,0x54,0x4D, /* 000005E8 "P_..p.TM" */
+ 0x50,0x5F,0xA4,0x50,0x52,0x52,0x30,0x14, /* 000005F0 "P_.PRR0." */
+ 0x1B,0x5F,0x53,0x52,0x53,0x01,0x8B,0x68, /* 000005F8 "._SRS..h" */
+ 0x01,0x54,0x4D,0x50,0x5F,0x82,0x54,0x4D, /* 00000600 ".TMP_.TM" */
+ 0x50,0x5F,0x60,0x76,0x60,0x70,0x60,0x50, /* 00000608 "P_`v`p`P" */
+ 0x52,0x51,0x30,0x5B,0x82,0x4F,0x0A,0x4C, /* 00000610 "RQ0[.O.L" */
+ 0x4E,0x4B,0x42,0x08,0x5F,0x48,0x49,0x44, /* 00000618 "NKB._HID" */
+ 0x0C,0x41,0xD0,0x0C,0x0F,0x08,0x5F,0x55, /* 00000620 ".A...._U" */
+ 0x49,0x44,0x0A,0x02,0x08,0x5F,0x50,0x52, /* 00000628 "ID..._PR" */
+ 0x53,0x11,0x09,0x0A,0x06,0x23,0xF8,0x1E, /* 00000630 "S....#.." */
+ 0x18,0x79,0x00,0x14,0x1A,0x5F,0x53,0x54, /* 00000638 ".y..._ST" */
+ 0x41,0x00,0x70,0x0A,0x0B,0x60,0xA0,0x0D, /* 00000640 "A.p..`.." */
+ 0x7B,0x0A,0x80,0x50,0x52,0x51,0x31,0x61, /* 00000648 "{..PRQ1a" */
+ 0x70,0x0A,0x09,0x60,0xA4,0x60,0x14,0x11, /* 00000650 "p..`.`.." */
+ 0x5F,0x44,0x49,0x53,0x00,0x7D,0x50,0x52, /* 00000658 "_DIS.}PR" */
+ 0x51,0x31,0x0A,0x80,0x50,0x52,0x51,0x31, /* 00000660 "Q1..PRQ1" */
+ 0x14,0x3F,0x5F,0x43,0x52,0x53,0x00,0x08, /* 00000668 ".?_CRS.." */
+ 0x50,0x52,0x52,0x30,0x11,0x09,0x0A,0x06, /* 00000670 "PRR0...." */
+ 0x23,0x02,0x00,0x18,0x79,0x00,0x8B,0x50, /* 00000678 "#...y..P" */
+ 0x52,0x52,0x30,0x01,0x54,0x4D,0x50,0x5F, /* 00000680 "RR0.TMP_" */
+ 0x70,0x50,0x52,0x51,0x31,0x60,0xA0,0x0C, /* 00000688 "pPRQ1`.." */
+ 0x95,0x60,0x0A,0x80,0x79,0x01,0x60,0x54, /* 00000690 ".`..y.`T" */
+ 0x4D,0x50,0x5F,0xA1,0x07,0x70,0x00,0x54, /* 00000698 "MP_..p.T" */
+ 0x4D,0x50,0x5F,0xA4,0x50,0x52,0x52,0x30, /* 000006A0 "MP_.PRR0" */
+ 0x14,0x1B,0x5F,0x53,0x52,0x53,0x01,0x8B, /* 000006A8 ".._SRS.." */
+ 0x68,0x01,0x54,0x4D,0x50,0x5F,0x82,0x54, /* 000006B0 "h.TMP_.T" */
+ 0x4D,0x50,0x5F,0x60,0x76,0x60,0x70,0x60, /* 000006B8 "MP_`v`p`" */
+ 0x50,0x52,0x51,0x31,0x5B,0x82,0x4F,0x0A, /* 000006C0 "PRQ1[.O." */
+ 0x4C,0x4E,0x4B,0x43,0x08,0x5F,0x48,0x49, /* 000006C8 "LNKC._HI" */
+ 0x44,0x0C,0x41,0xD0,0x0C,0x0F,0x08,0x5F, /* 000006D0 "D.A...._" */
+ 0x55,0x49,0x44,0x0A,0x03,0x08,0x5F,0x50, /* 000006D8 "UID..._P" */
+ 0x52,0x53,0x11,0x09,0x0A,0x06,0x23,0xF8, /* 000006E0 "RS....#." */
+ 0x1E,0x18,0x79,0x00,0x14,0x1A,0x5F,0x53, /* 000006E8 "..y..._S" */
+ 0x54,0x41,0x00,0x70,0x0A,0x0B,0x60,0xA0, /* 000006F0 "TA.p..`." */
+ 0x0D,0x7B,0x0A,0x80,0x50,0x52,0x51,0x32, /* 000006F8 ".{..PRQ2" */
+ 0x61,0x70,0x0A,0x09,0x60,0xA4,0x60,0x14, /* 00000700 "ap..`.`." */
+ 0x11,0x5F,0x44,0x49,0x53,0x00,0x7D,0x50, /* 00000708 "._DIS.}P" */
+ 0x52,0x51,0x32,0x0A,0x80,0x50,0x52,0x51, /* 00000710 "RQ2..PRQ" */
+ 0x32,0x14,0x3F,0x5F,0x43,0x52,0x53,0x00, /* 00000718 "2.?_CRS." */
+ 0x08,0x50,0x52,0x52,0x30,0x11,0x09,0x0A, /* 00000720 ".PRR0..." */
+ 0x06,0x23,0x02,0x00,0x18,0x79,0x00,0x8B, /* 00000728 ".#...y.." */
+ 0x50,0x52,0x52,0x30,0x01,0x54,0x4D,0x50, /* 00000730 "PRR0.TMP" */
+ 0x5F,0x70,0x50,0x52,0x51,0x32,0x60,0xA0, /* 00000738 "_pPRQ2`." */
+ 0x0C,0x95,0x60,0x0A,0x80,0x79,0x01,0x60, /* 00000740 "..`..y.`" */
+ 0x54,0x4D,0x50,0x5F,0xA1,0x07,0x70,0x00, /* 00000748 "TMP_..p." */
+ 0x54,0x4D,0x50,0x5F,0xA4,0x50,0x52,0x52, /* 00000750 "TMP_.PRR" */
+ 0x30,0x14,0x1B,0x5F,0x53,0x52,0x53,0x01, /* 00000758 "0.._SRS." */
+ 0x8B,0x68,0x01,0x54,0x4D,0x50,0x5F,0x82, /* 00000760 ".h.TMP_." */
+ 0x54,0x4D,0x50,0x5F,0x60,0x76,0x60,0x70, /* 00000768 "TMP_`v`p" */
+ 0x60,0x50,0x52,0x51,0x32,0x5B,0x82,0x4F, /* 00000770 "`PRQ2[.O" */
+ 0x0A,0x4C,0x4E,0x4B,0x44,0x08,0x5F,0x48, /* 00000778 ".LNKD._H" */
+ 0x49,0x44,0x0C,0x41,0xD0,0x0C,0x0F,0x08, /* 00000780 "ID.A...." */
+ 0x5F,0x55,0x49,0x44,0x0A,0x04,0x08,0x5F, /* 00000788 "_UID..._" */
+ 0x50,0x52,0x53,0x11,0x09,0x0A,0x06,0x23, /* 00000790 "PRS....#" */
+ 0xF8,0x1E,0x18,0x79,0x00,0x14,0x1A,0x5F, /* 00000798 "...y..._" */
+ 0x53,0x54,0x41,0x00,0x70,0x0A,0x0B,0x60, /* 000007A0 "STA.p..`" */
+ 0xA0,0x0D,0x7B,0x0A,0x80,0x50,0x52,0x51, /* 000007A8 "..{..PRQ" */
+ 0x33,0x61,0x70,0x0A,0x09,0x60,0xA4,0x60, /* 000007B0 "3ap..`.`" */
+ 0x14,0x11,0x5F,0x44,0x49,0x53,0x00,0x7D, /* 000007B8 ".._DIS.}" */
+ 0x50,0x52,0x51,0x33,0x0A,0x80,0x50,0x52, /* 000007C0 "PRQ3..PR" */
+ 0x51,0x33,0x14,0x3F,0x5F,0x43,0x52,0x53, /* 000007C8 "Q3.?_CRS" */
+ 0x00,0x08,0x50,0x52,0x52,0x30,0x11,0x09, /* 000007D0 "..PRR0.." */
+ 0x0A,0x06,0x23,0x02,0x00,0x18,0x79,0x00, /* 000007D8 "..#...y." */
+ 0x8B,0x50,0x52,0x52,0x30,0x01,0x54,0x4D, /* 000007E0 ".PRR0.TM" */
+ 0x50,0x5F,0x70,0x50,0x52,0x51,0x33,0x60, /* 000007E8 "P_pPRQ3`" */
+ 0xA0,0x0C,0x95,0x60,0x0A,0x80,0x79,0x01, /* 000007F0 "...`..y." */
+ 0x60,0x54,0x4D,0x50,0x5F,0xA1,0x07,0x70, /* 000007F8 "`TMP_..p" */
+ 0x00,0x54,0x4D,0x50,0x5F,0xA4,0x50,0x52, /* 00000800 ".TMP_.PR" */
+ 0x52,0x30,0x14,0x1B,0x5F,0x53,0x52,0x53, /* 00000808 "R0.._SRS" */
+ 0x01,0x8B,0x68,0x01,0x54,0x4D,0x50,0x5F, /* 00000810 "..h.TMP_" */
+ 0x82,0x54,0x4D,0x50,0x5F,0x60,0x76,0x60, /* 00000818 ".TMP_`v`" */
+ 0x70,0x60,0x50,0x52,0x51,0x33,0x08,0x5F, /* 00000820 "p`PRQ3._" */
+ 0x53,0x35,0x5F,0x12,0x06,0x04,0x00,0x00, /* 00000828 "S5_....." */
+ 0x00,0x00,
+};
diff --git a/hw/acpi.c b/hw/acpi.c
new file mode 100644
index 0000000..6c20a4e
--- /dev/null
+++ b/hw/acpi.c
@@ -0,0 +1,615 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "vl.h"
+
+//#define DEBUG
+
+/* i82731AB (PIIX4) compatible power management function */
+#define PM_FREQ 3579545
+
+/* XXX: make them variable */
+#define PM_IO_BASE 0xb000
+#define SMI_CMD_IO_ADDR 0xb040
+#define ACPI_DBG_IO_ADDR 0xb044
+
+typedef struct PIIX4PMState {
+ PCIDevice dev;
+ uint16_t pmsts;
+ uint16_t pmen;
+ uint16_t pmcntrl;
+ QEMUTimer *tmr_timer;
+ int64_t tmr_overflow_time;
+} PIIX4PMState;
+
+#define RTC_EN (1 << 10)
+#define PWRBTN_EN (1 << 8)
+#define GBL_EN (1 << 5)
+#define TMROF_EN (1 << 0)
+
+#define SCI_EN (1 << 0)
+
+#define SUS_EN (1 << 13)
+
+/* Note: only used for ACPI bios init. Could be deleted when ACPI init
+ is integrated in Bochs BIOS */
+static PIIX4PMState *piix4_pm_state;
+
+static uint32_t get_pmtmr(PIIX4PMState *s)
+{
+ uint32_t d;
+ d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
+ return d & 0xffffff;
+}
+
+static int get_pmsts(PIIX4PMState *s)
+{
+ int64_t d;
+ int pmsts;
+ pmsts = s->pmsts;
+ d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
+ if (d >= s->tmr_overflow_time)
+ s->pmsts |= TMROF_EN;
+ return pmsts;
+}
+
+static void pm_update_sci(PIIX4PMState *s)
+{
+ int sci_level, pmsts;
+ int64_t expire_time;
+
+ pmsts = get_pmsts(s);
+ sci_level = (((pmsts & s->pmen) &
+ (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
+ pci_set_irq(&s->dev, 0, sci_level);
+ /* schedule a timer interruption if needed */
+ if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) {
+ expire_time = muldiv64(s->tmr_overflow_time, ticks_per_sec, PM_FREQ);
+ qemu_mod_timer(s->tmr_timer, expire_time);
+ } else {
+ qemu_del_timer(s->tmr_timer);
+ }
+}
+
+static void pm_tmr_timer(void *opaque)
+{
+ PIIX4PMState *s = opaque;
+ pm_update_sci(s);
+}
+
+static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ PIIX4PMState *s = opaque;
+ addr &= 0x3f;
+ switch(addr) {
+ case 0x00:
+ {
+ int64_t d;
+ int pmsts;
+ pmsts = get_pmsts(s);
+ if (pmsts & val & TMROF_EN) {
+ /* if TMRSTS is reset, then compute the new overflow time */
+ d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
+ s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+ }
+ s->pmsts &= ~val;
+ pm_update_sci(s);
+ }
+ break;
+ case 0x02:
+ s->pmen = val;
+ pm_update_sci(s);
+ break;
+ case 0x04:
+ {
+ int sus_typ;
+ s->pmcntrl = val & ~(SUS_EN);
+ if (val & SUS_EN) {
+ /* change suspend type */
+ sus_typ = (val >> 10) & 3;
+ switch(sus_typ) {
+ case 0: /* soft power off */
+ qemu_system_shutdown_request();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG
+ printf("PM writew port=0x%04x val=0x%04x\n", addr, val);
+#endif
+}
+
+static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
+{
+ PIIX4PMState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x3f;
+ switch(addr) {
+ case 0x00:
+ val = get_pmsts(s);
+ break;
+ case 0x02:
+ val = s->pmen;
+ break;
+ case 0x04:
+ val = s->pmcntrl;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+#ifdef DEBUG
+ printf("PM readw port=0x%04x val=0x%04x\n", addr, val);
+#endif
+ return val;
+}
+
+static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ // PIIX4PMState *s = opaque;
+ addr &= 0x3f;
+#ifdef DEBUG
+ printf("PM writel port=0x%04x val=0x%08x\n", addr, val);
+#endif
+}
+
+static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
+{
+ PIIX4PMState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x3f;
+ switch(addr) {
+ case 0x08:
+ val = get_pmtmr(s);
+ break;
+ default:
+ val = 0;
+ break;
+ }
+#ifdef DEBUG
+ printf("PM readl port=0x%04x val=0x%08x\n", addr, val);
+#endif
+ return val;
+}
+
+static void smi_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ PIIX4PMState *s = opaque;
+#ifdef DEBUG
+ printf("SMI cmd val=0x%02x\n", val);
+#endif
+ switch(val) {
+ case 0xf0: /* ACPI disable */
+ s->pmcntrl &= ~SCI_EN;
+ break;
+ case 0xf1: /* ACPI enable */
+ s->pmcntrl |= SCI_EN;
+ break;
+ }
+}
+
+static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+#if defined(DEBUG)
+ printf("ACPI: DBG: 0x%08x\n", val);
+#endif
+}
+
+/* XXX: we still add it to the PIIX3 and we count on the fact that
+ OSes are smart enough to accept this strange configuration */
+void piix4_pm_init(PCIBus *bus, int devfn)
+{
+ PIIX4PMState *s;
+ uint8_t *pci_conf;
+ uint32_t pm_io_base;
+
+ s = (PIIX4PMState *)pci_register_device(bus,
+ "PM", sizeof(PIIX4PMState),
+ devfn, NULL, NULL);
+ pci_conf = s->dev.config;
+ pci_conf[0x00] = 0x86;
+ pci_conf[0x01] = 0x80;
+ pci_conf[0x02] = 0x13;
+ pci_conf[0x03] = 0x71;
+ pci_conf[0x08] = 0x00; // revision number
+ pci_conf[0x09] = 0x00;
+ pci_conf[0x0a] = 0x80; // other bridge device
+ pci_conf[0x0b] = 0x06; // bridge device
+ pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x3d] = 0x01; // interrupt pin 1
+
+ pm_io_base = PM_IO_BASE;
+ pci_conf[0x40] = pm_io_base | 1;
+ pci_conf[0x41] = pm_io_base >> 8;
+ register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s);
+ register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s);
+ register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s);
+ register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s);
+
+ register_ioport_write(SMI_CMD_IO_ADDR, 1, 1, smi_cmd_writeb, s);
+ register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s);
+
+ /* XXX: which specification is used ? The i82731AB has different
+ mappings */
+ pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10;
+ pci_conf[0x63] = 0x60;
+ pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) |
+ (serial_hds[1] != NULL ? 0x90 : 0);
+
+ s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
+ piix4_pm_state = s;
+}
+
+/* ACPI tables */
+/* XXX: move them in the Bochs BIOS ? */
+
+/*************************************************/
+
+/* Table structure from Linux kernel (the ACPI tables are under the
+ BSD license) */
+
+#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \
+ uint8_t signature [4]; /* ACPI signature (4 ASCII characters) */\
+ uint32_t length; /* Length of table, in bytes, including header */\
+ uint8_t revision; /* ACPI Specification minor version # */\
+ uint8_t checksum; /* To make sum of entire table == 0 */\
+ uint8_t oem_id [6]; /* OEM identification */\
+ uint8_t oem_table_id [8]; /* OEM table identification */\
+ uint32_t oem_revision; /* OEM revision number */\
+ uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */\
+ uint32_t asl_compiler_revision; /* ASL compiler revision number */
+
+
+struct acpi_table_header /* ACPI common table header */
+{
+ ACPI_TABLE_HEADER_DEF
+};
+
+struct rsdp_descriptor /* Root System Descriptor Pointer */
+{
+ uint8_t signature [8]; /* ACPI signature, contains "RSD PTR " */
+ uint8_t checksum; /* To make sum of struct == 0 */
+ uint8_t oem_id [6]; /* OEM identification */
+ uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */
+ uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */
+ uint32_t length; /* XSDT Length in bytes including hdr */
+ uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */
+ uint8_t extended_checksum; /* Checksum of entire table */
+ uint8_t reserved [3]; /* Reserved field must be 0 */
+};
+
+/*
+ * ACPI 1.0 Root System Description Table (RSDT)
+ */
+struct rsdt_descriptor_rev1
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t table_offset_entry [2]; /* Array of pointers to other */
+ /* ACPI tables */
+};
+
+/*
+ * ACPI 1.0 Firmware ACPI Control Structure (FACS)
+ */
+struct facs_descriptor_rev1
+{
+ uint8_t signature[4]; /* ACPI Signature */
+ uint32_t length; /* Length of structure, in bytes */
+ uint32_t hardware_signature; /* Hardware configuration signature */
+ uint32_t firmware_waking_vector; /* ACPI OS waking vector */
+ uint32_t global_lock; /* Global Lock */
+ uint32_t S4bios_f : 1; /* Indicates if S4BIOS support is present */
+ uint32_t reserved1 : 31; /* Must be 0 */
+ uint8_t resverved3 [40]; /* Reserved - must be zero */
+};
+
+
+/*
+ * ACPI 1.0 Fixed ACPI Description Table (FADT)
+ */
+struct fadt_descriptor_rev1
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t firmware_ctrl; /* Physical address of FACS */
+ uint32_t dsdt; /* Physical address of DSDT */
+ uint8_t model; /* System Interrupt Model */
+ uint8_t reserved1; /* Reserved */
+ uint16_t sci_int; /* System vector of SCI interrupt */
+ uint32_t smi_cmd; /* Port address of SMI command port */
+ uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */
+ uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */
+ uint8_t S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */
+ uint8_t reserved2; /* Reserved - must be zero */
+ uint32_t pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */
+ uint32_t pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */
+ uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */
+ uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */
+ uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */
+ uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */
+ uint32_t gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */
+ uint32_t gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */
+ uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */
+ uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */
+ uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */
+ uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */
+ uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */
+ uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */
+ uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */
+ uint8_t reserved3; /* Reserved */
+ uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */
+ uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */
+ uint16_t flush_size; /* Size of area read to flush caches */
+ uint16_t flush_stride; /* Stride used in flushing caches */
+ uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */
+ uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */
+ uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */
+ uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */
+ uint8_t century; /* Index to century in RTC CMOS RAM */
+ uint8_t reserved4; /* Reserved */
+ uint8_t reserved4a; /* Reserved */
+ uint8_t reserved4b; /* Reserved */
+#if 0
+ uint32_t wb_invd : 1; /* The wbinvd instruction works properly */
+ uint32_t wb_invd_flush : 1; /* The wbinvd flushes but does not invalidate */
+ uint32_t proc_c1 : 1; /* All processors support C1 state */
+ uint32_t plvl2_up : 1; /* C2 state works on MP system */
+ uint32_t pwr_button : 1; /* Power button is handled as a generic feature */
+ uint32_t sleep_button : 1; /* Sleep button is handled as a generic feature, or not present */
+ uint32_t fixed_rTC : 1; /* RTC wakeup stat not in fixed register space */
+ uint32_t rtcs4 : 1; /* RTC wakeup stat not possible from S4 */
+ uint32_t tmr_val_ext : 1; /* The tmr_val width is 32 bits (0 = 24 bits) */
+ uint32_t reserved5 : 23; /* Reserved - must be zero */
+#else
+ uint32_t flags;
+#endif
+};
+
+/*
+ * MADT values and structures
+ */
+
+/* Values for MADT PCATCompat */
+
+#define DUAL_PIC 0
+#define MULTIPLE_APIC 1
+
+
+/* Master MADT */
+
+struct multiple_apic_table
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t local_apic_address; /* Physical address of local APIC */
+#if 0
+ uint32_t PCATcompat : 1; /* A one indicates system also has dual 8259s */
+ uint32_t reserved1 : 31;
+#else
+ uint32_t flags;
+#endif
+};
+
+
+/* Values for Type in APIC_HEADER_DEF */
+
+#define APIC_PROCESSOR 0
+#define APIC_IO 1
+#define APIC_XRUPT_OVERRIDE 2
+#define APIC_NMI 3
+#define APIC_LOCAL_NMI 4
+#define APIC_ADDRESS_OVERRIDE 5
+#define APIC_IO_SAPIC 6
+#define APIC_LOCAL_SAPIC 7
+#define APIC_XRUPT_SOURCE 8
+#define APIC_RESERVED 9 /* 9 and greater are reserved */
+
+/*
+ * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE)
+ */
+#define APIC_HEADER_DEF /* Common APIC sub-structure header */\
+ uint8_t type; \
+ uint8_t length;
+
+/* Sub-structures for MADT */
+
+struct madt_processor_apic
+{
+ APIC_HEADER_DEF
+ uint8_t processor_id; /* ACPI processor id */
+ uint8_t local_apic_id; /* Processor's local APIC id */
+#if 0
+ uint32_t processor_enabled: 1; /* Processor is usable if set */
+ uint32_t reserved2 : 31; /* Reserved, must be zero */
+#else
+ uint32_t flags;
+#endif
+};
+
+struct madt_io_apic
+{
+ APIC_HEADER_DEF
+ uint8_t io_apic_id; /* I/O APIC ID */
+ uint8_t reserved; /* Reserved - must be zero */
+ uint32_t address; /* APIC physical address */
+ uint32_t interrupt; /* Global system interrupt where INTI
+ * lines start */
+};
+
+#include "acpi-dsdt.hex"
+
+static int acpi_checksum(const uint8_t *data, int len)
+{
+ int sum, i;
+ sum = 0;
+ for(i = 0; i < len; i++)
+ sum += data[i];
+ return (-sum) & 0xff;
+}
+
+static void acpi_build_table_header(struct acpi_table_header *h,
+ char *sig, int len)
+{
+ memcpy(h->signature, sig, 4);
+ h->length = cpu_to_le32(len);
+ h->revision = 0;
+ memcpy(h->oem_id, "QEMU ", 6);
+ memcpy(h->oem_table_id, "QEMU", 4);
+ memcpy(h->oem_table_id + 4, sig, 4);
+ h->oem_revision = cpu_to_le32(1);
+ memcpy(h->asl_compiler_id, "QEMU", 4);
+ h->asl_compiler_revision = cpu_to_le32(1);
+ h->checksum = acpi_checksum((void *)h, len);
+}
+
+#define ACPI_TABLES_BASE 0x000e8000
+
+/* base_addr must be a multiple of 4KB */
+void acpi_bios_init(void)
+{
+ struct rsdp_descriptor *rsdp;
+ struct rsdt_descriptor_rev1 *rsdt;
+ struct fadt_descriptor_rev1 *fadt;
+ struct facs_descriptor_rev1 *facs;
+ struct multiple_apic_table *madt;
+ uint8_t *dsdt;
+ uint32_t base_addr, rsdt_addr, fadt_addr, addr, facs_addr, dsdt_addr;
+ uint32_t pm_io_base, acpi_tables_size, madt_addr, madt_size;
+ int i;
+
+ /* compute PCI I/O addresses */
+ pm_io_base = (piix4_pm_state->dev.config[0x40] |
+ (piix4_pm_state->dev.config[0x41] << 8)) & ~0x3f;
+
+ base_addr = ACPI_TABLES_BASE;
+
+ /* reserve memory space for tables */
+ addr = base_addr;
+ rsdp = (void *)(phys_ram_base + addr);
+ addr += sizeof(*rsdp);
+
+ rsdt_addr = addr;
+ rsdt = (void *)(phys_ram_base + addr);
+ addr += sizeof(*rsdt);
+
+ fadt_addr = addr;
+ fadt = (void *)(phys_ram_base + addr);
+ addr += sizeof(*fadt);
+
+ /* XXX: FACS should be in RAM */
+ addr = (addr + 63) & ~63; /* 64 byte alignment for FACS */
+ facs_addr = addr;
+ facs = (void *)(phys_ram_base + addr);
+ addr += sizeof(*facs);
+
+ dsdt_addr = addr;
+ dsdt = (void *)(phys_ram_base + addr);
+ addr += sizeof(AmlCode);
+
+ addr = (addr + 7) & ~7;
+ madt_addr = addr;
+ madt_size = sizeof(*madt) +
+ sizeof(struct madt_processor_apic) * smp_cpus +
+ sizeof(struct madt_io_apic);
+ madt = (void *)(phys_ram_base + addr);
+ addr += madt_size;
+
+ acpi_tables_size = addr - base_addr;
+
+ cpu_register_physical_memory(base_addr, acpi_tables_size,
+ base_addr | IO_MEM_ROM);
+
+ /* RSDP */
+ memset(rsdp, 0, sizeof(*rsdp));
+ memcpy(rsdp->signature, "RSD PTR ", 8);
+ memcpy(rsdp->oem_id, "QEMU ", 6);
+ rsdp->rsdt_physical_address = cpu_to_le32(rsdt_addr);
+ rsdp->checksum = acpi_checksum((void *)rsdp, 20);
+
+ /* RSDT */
+ rsdt->table_offset_entry[0] = cpu_to_le32(fadt_addr);
+ rsdt->table_offset_entry[1] = cpu_to_le32(madt_addr);
+ acpi_build_table_header((struct acpi_table_header *)rsdt,
+ "RSDT", sizeof(*rsdt));
+
+ /* FADT */
+ memset(fadt, 0, sizeof(*fadt));
+ fadt->firmware_ctrl = cpu_to_le32(facs_addr);
+ fadt->dsdt = cpu_to_le32(dsdt_addr);
+ fadt->model = 1;
+ fadt->reserved1 = 0;
+ fadt->sci_int = cpu_to_le16(piix4_pm_state->dev.config[0x3c]);
+ fadt->smi_cmd = cpu_to_le32(SMI_CMD_IO_ADDR);
+ fadt->acpi_enable = 0xf1;
+ fadt->acpi_disable = 0xf0;
+ fadt->pm1a_evt_blk = cpu_to_le32(pm_io_base);
+ fadt->pm1a_cnt_blk = cpu_to_le32(pm_io_base + 0x04);
+ fadt->pm_tmr_blk = cpu_to_le32(pm_io_base + 0x08);
+ fadt->pm1_evt_len = 4;
+ fadt->pm1_cnt_len = 2;
+ fadt->pm_tmr_len = 4;
+ fadt->plvl2_lat = cpu_to_le16(50);
+ fadt->plvl3_lat = cpu_to_le16(50);
+ fadt->plvl3_lat = cpu_to_le16(50);
+ /* WBINVD + PROC_C1 + PWR_BUTTON + SLP_BUTTON + FIX_RTC */
+ fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 4) | (1 << 5) | (1 << 6));
+ acpi_build_table_header((struct acpi_table_header *)fadt, "FACP",
+ sizeof(*fadt));
+
+ /* FACS */
+ memset(facs, 0, sizeof(*facs));
+ memcpy(facs->signature, "FACS", 4);
+ facs->length = cpu_to_le32(sizeof(*facs));
+
+ /* DSDT */
+ memcpy(dsdt, AmlCode, sizeof(AmlCode));
+
+ /* MADT */
+ {
+ struct madt_processor_apic *apic;
+ struct madt_io_apic *io_apic;
+
+ memset(madt, 0, madt_size);
+ madt->local_apic_address = cpu_to_le32(0xfee00000);
+ madt->flags = cpu_to_le32(1);
+ apic = (void *)(madt + 1);
+ for(i=0;i<smp_cpus;i++) {
+ apic->type = APIC_PROCESSOR;
+ apic->length = sizeof(*apic);
+ apic->processor_id = i;
+ apic->local_apic_id = i;
+ apic->flags = cpu_to_le32(1);
+ apic++;
+ }
+ io_apic = (void *)apic;
+ io_apic->type = APIC_IO;
+ io_apic->length = sizeof(*io_apic);
+ io_apic->io_apic_id = smp_cpus;
+ io_apic->address = cpu_to_le32(0xfec00000);
+ io_apic->interrupt = cpu_to_le32(0);
+
+ acpi_build_table_header((struct acpi_table_header *)madt,
+ "APIC", madt_size);
+ }
+}
diff --git a/hw/adb.c b/hw/adb.c
new file mode 100644
index 0000000..8e08cb1
--- /dev/null
+++ b/hw/adb.c
@@ -0,0 +1,410 @@
+/*
+ * QEMU ADB support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* ADB commands */
+#define ADB_BUSRESET 0x00
+#define ADB_FLUSH 0x01
+#define ADB_WRITEREG 0x08
+#define ADB_READREG 0x0c
+
+/* ADB device commands */
+#define ADB_CMD_SELF_TEST 0xff
+#define ADB_CMD_CHANGE_ID 0xfe
+#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd
+#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00
+
+/* ADB default device IDs (upper 4 bits of ADB command byte) */
+#define ADB_DONGLE 1
+#define ADB_KEYBOARD 2
+#define ADB_MOUSE 3
+#define ADB_TABLET 4
+#define ADB_MODEM 5
+#define ADB_MISC 7
+
+/* error codes */
+#define ADB_RET_NOTPRESENT (-2)
+
+int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+{
+ ADBDevice *d;
+ int devaddr, cmd, i;
+
+ cmd = buf[0] & 0xf;
+ if (cmd == ADB_BUSRESET) {
+ for(i = 0; i < s->nb_devices; i++) {
+ d = &s->devices[i];
+ if (d->devreset) {
+ d->devreset(d);
+ }
+ }
+ return 0;
+ }
+ devaddr = buf[0] >> 4;
+ for(i = 0; i < s->nb_devices; i++) {
+ d = &s->devices[i];
+ if (d->devaddr == devaddr) {
+ return d->devreq(d, obuf, buf, len);
+ }
+ }
+ return ADB_RET_NOTPRESENT;
+}
+
+/* XXX: move that to cuda ? */
+int adb_poll(ADBBusState *s, uint8_t *obuf)
+{
+ ADBDevice *d;
+ int olen, i;
+ uint8_t buf[1];
+
+ olen = 0;
+ for(i = 0; i < s->nb_devices; i++) {
+ if (s->poll_index >= s->nb_devices)
+ s->poll_index = 0;
+ d = &s->devices[s->poll_index];
+ buf[0] = ADB_READREG | (d->devaddr << 4);
+ olen = adb_request(s, obuf + 1, buf, 1);
+ /* if there is data, we poll again the same device */
+ if (olen > 0) {
+ obuf[0] = buf[0];
+ olen++;
+ break;
+ }
+ s->poll_index++;
+ }
+ return olen;
+}
+
+ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
+ ADBDeviceRequest *devreq,
+ ADBDeviceReset *devreset,
+ void *opaque)
+{
+ ADBDevice *d;
+ if (s->nb_devices >= MAX_ADB_DEVICES)
+ return NULL;
+ d = &s->devices[s->nb_devices++];
+ d->bus = s;
+ d->devaddr = devaddr;
+ d->devreq = devreq;
+ d->devreset = devreset;
+ d->opaque = opaque;
+ return d;
+}
+
+/***************************************************************/
+/* Keyboard ADB device */
+
+typedef struct KBDState {
+ uint8_t data[128];
+ int rptr, wptr, count;
+} KBDState;
+
+static const uint8_t pc_to_adb_keycode[256] = {
+ 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+ 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1,
+ 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+ 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+ 84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119,
+ 61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void adb_kbd_put_keycode(void *opaque, int keycode)
+{
+ ADBDevice *d = opaque;
+ KBDState *s = d->opaque;
+
+ if (s->count < sizeof(s->data)) {
+ s->data[s->wptr] = keycode;
+ if (++s->wptr == sizeof(s->data))
+ s->wptr = 0;
+ s->count++;
+ }
+}
+
+static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
+{
+ static int ext_keycode;
+ KBDState *s = d->opaque;
+ int adb_keycode, keycode;
+ int olen;
+
+ olen = 0;
+ for(;;) {
+ if (s->count == 0)
+ break;
+ keycode = s->data[s->rptr];
+ if (++s->rptr == sizeof(s->data))
+ s->rptr = 0;
+ s->count--;
+
+ if (keycode == 0xe0) {
+ ext_keycode = 1;
+ } else {
+ if (ext_keycode)
+ adb_keycode = pc_to_adb_keycode[keycode | 0x80];
+ else
+ adb_keycode = pc_to_adb_keycode[keycode & 0x7f];
+ obuf[0] = adb_keycode | (keycode & 0x80);
+ /* NOTE: could put a second keycode if needed */
+ obuf[1] = 0xff;
+ olen = 2;
+ ext_keycode = 0;
+ break;
+ }
+ }
+ return olen;
+}
+
+static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
+ const uint8_t *buf, int len)
+{
+ KBDState *s = d->opaque;
+ int cmd, reg, olen;
+
+ if ((buf[0] & 0x0f) == ADB_FLUSH) {
+ /* flush keyboard fifo */
+ s->wptr = s->rptr = s->count = 0;
+ return 0;
+ }
+
+ cmd = buf[0] & 0xc;
+ reg = buf[0] & 0x3;
+ olen = 0;
+ switch(cmd) {
+ case ADB_WRITEREG:
+ switch(reg) {
+ case 2:
+ /* LED status */
+ break;
+ case 3:
+ switch(buf[2]) {
+ case ADB_CMD_SELF_TEST:
+ break;
+ case ADB_CMD_CHANGE_ID:
+ case ADB_CMD_CHANGE_ID_AND_ACT:
+ case ADB_CMD_CHANGE_ID_AND_ENABLE:
+ d->devaddr = buf[1] & 0xf;
+ break;
+ default:
+ /* XXX: check this */
+ d->devaddr = buf[1] & 0xf;
+ d->handler = buf[2];
+ break;
+ }
+ }
+ break;
+ case ADB_READREG:
+ switch(reg) {
+ case 0:
+ olen = adb_kbd_poll(d, obuf);
+ break;
+ case 1:
+ break;
+ case 2:
+ obuf[0] = 0x00; /* XXX: check this */
+ obuf[1] = 0x07; /* led status */
+ olen = 2;
+ break;
+ case 3:
+ obuf[0] = d->handler;
+ obuf[1] = d->devaddr;
+ olen = 2;
+ break;
+ }
+ break;
+ }
+ return olen;
+}
+
+static int adb_kbd_reset(ADBDevice *d)
+{
+ KBDState *s = d->opaque;
+
+ d->handler = 1;
+ d->devaddr = ADB_KEYBOARD;
+ memset(s, 0, sizeof(KBDState));
+
+ return 0;
+}
+
+void adb_kbd_init(ADBBusState *bus)
+{
+ ADBDevice *d;
+ KBDState *s;
+ s = qemu_mallocz(sizeof(KBDState));
+ d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request,
+ adb_kbd_reset, s);
+ adb_kbd_reset(d);
+ qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
+}
+
+/***************************************************************/
+/* Mouse ADB device */
+
+typedef struct MouseState {
+ int buttons_state, last_buttons_state;
+ int dx, dy, dz;
+} MouseState;
+
+static void adb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ ADBDevice *d = opaque;
+ MouseState *s = d->opaque;
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ s->buttons_state = buttons_state;
+}
+
+
+static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
+{
+ MouseState *s = d->opaque;
+ int dx, dy;
+
+ if (s->last_buttons_state == s->buttons_state &&
+ s->dx == 0 && s->dy == 0)
+ return 0;
+
+ dx = s->dx;
+ if (dx < -63)
+ dx = -63;
+ else if (dx > 63)
+ dx = 63;
+
+ dy = s->dy;
+ if (dy < -63)
+ dy = -63;
+ else if (dy > 63)
+ dy = 63;
+
+ s->dx -= dx;
+ s->dy -= dy;
+ s->last_buttons_state = s->buttons_state;
+
+ dx &= 0x7f;
+ dy &= 0x7f;
+
+ if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
+ dy |= 0x80;
+ if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
+ dx |= 0x80;
+
+ obuf[0] = dy;
+ obuf[1] = dx;
+ return 2;
+}
+
+static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
+ const uint8_t *buf, int len)
+{
+ MouseState *s = d->opaque;
+ int cmd, reg, olen;
+
+ if ((buf[0] & 0x0f) == ADB_FLUSH) {
+ /* flush mouse fifo */
+ s->buttons_state = s->last_buttons_state;
+ s->dx = 0;
+ s->dy = 0;
+ s->dz = 0;
+ return 0;
+ }
+
+ cmd = buf[0] & 0xc;
+ reg = buf[0] & 0x3;
+ olen = 0;
+ switch(cmd) {
+ case ADB_WRITEREG:
+ switch(reg) {
+ case 2:
+ break;
+ case 3:
+ switch(buf[2]) {
+ case ADB_CMD_SELF_TEST:
+ break;
+ case ADB_CMD_CHANGE_ID:
+ case ADB_CMD_CHANGE_ID_AND_ACT:
+ case ADB_CMD_CHANGE_ID_AND_ENABLE:
+ d->devaddr = buf[1] & 0xf;
+ break;
+ default:
+ /* XXX: check this */
+ d->devaddr = buf[1] & 0xf;
+ break;
+ }
+ }
+ break;
+ case ADB_READREG:
+ switch(reg) {
+ case 0:
+ olen = adb_mouse_poll(d, obuf);
+ break;
+ case 1:
+ break;
+ case 3:
+ obuf[0] = d->handler;
+ obuf[1] = d->devaddr;
+ olen = 2;
+ break;
+ }
+ break;
+ }
+ return olen;
+}
+
+static int adb_mouse_reset(ADBDevice *d)
+{
+ MouseState *s = d->opaque;
+
+ d->handler = 2;
+ d->devaddr = ADB_MOUSE;
+ memset(s, 0, sizeof(MouseState));
+
+ return 0;
+}
+
+void adb_mouse_init(ADBBusState *bus)
+{
+ ADBDevice *d;
+ MouseState *s;
+
+ s = qemu_mallocz(sizeof(MouseState));
+ d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request,
+ adb_mouse_reset, s);
+ adb_mouse_reset(d);
+ qemu_add_mouse_event_handler(adb_mouse_event, d, 0);
+}
diff --git a/hw/adlib.c b/hw/adlib.c
new file mode 100644
index 0000000..b47bc3e
--- /dev/null
+++ b/hw/adlib.c
@@ -0,0 +1,341 @@
+/*
+ * QEMU Proxy for OPL2/3 emulation by MAME team
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <assert.h>
+#include "vl.h"
+
+#define ADLIB_KILL_TIMERS 1
+
+#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef HAS_YMF262
+#include "ymf262.h"
+void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
+#define SHIFT 2
+#else
+#include "fmopl.h"
+#define SHIFT 1
+#endif
+
+#define IO_READ_PROTO(name) \
+ uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+ void name (void *opaque, uint32_t nport, uint32_t val)
+
+static struct {
+ int port;
+ int freq;
+} conf = {0x220, 44100};
+
+typedef struct {
+ QEMUSoundCard card;
+ int ticking[2];
+ int enabled;
+ int active;
+ int bufpos;
+#ifdef DEBUG
+ int64_t exp[2];
+#endif
+ int16_t *mixbuf;
+ uint64_t dexp[2];
+ SWVoiceOut *voice;
+ int left, pos, samples;
+ QEMUAudioTimeStamp ats;
+#ifndef HAS_YMF262
+ FM_OPL *opl;
+#endif
+} AdlibState;
+
+static AdlibState glob_adlib;
+
+static void adlib_stop_opl_timer (AdlibState *s, size_t n)
+{
+#ifdef HAS_YMF262
+ YMF262TimerOver (0, n);
+#else
+ OPLTimerOver (s->opl, n);
+#endif
+ s->ticking[n] = 0;
+}
+
+static void adlib_kill_timers (AdlibState *s)
+{
+ size_t i;
+
+ for (i = 0; i < 2; ++i) {
+ if (s->ticking[i]) {
+ uint64_t delta;
+
+ delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
+ ldebug (
+ "delta = %f dexp = %f expired => %d\n",
+ delta / 1000000.0,
+ s->dexp[i] / 1000000.0,
+ delta >= s->dexp[i]
+ );
+ if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
+ adlib_stop_opl_timer (s, i);
+ AUD_init_time_stamp_out (s->voice, &s->ats);
+ }
+ }
+ }
+}
+
+static IO_WRITE_PROTO(adlib_write)
+{
+ AdlibState *s = opaque;
+ int a = nport & 3;
+ int status;
+
+ s->active = 1;
+ AUD_set_active_out (s->voice, 1);
+
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+ status = YMF262Write (0, a, val);
+#else
+ status = OPLWrite (s->opl, a, val);
+#endif
+}
+
+static IO_READ_PROTO(adlib_read)
+{
+ AdlibState *s = opaque;
+ uint8_t data;
+ int a = nport & 3;
+
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+ data = YMF262Read (0, a);
+#else
+ data = OPLRead (s->opl, a);
+#endif
+ return data;
+}
+
+static void timer_handler (int c, double interval_Sec)
+{
+ AdlibState *s = &glob_adlib;
+ unsigned n = c & 1;
+#ifdef DEBUG
+ double interval;
+ int64_t exp;
+#endif
+
+ if (interval_Sec == 0.0) {
+ s->ticking[n] = 0;
+ return;
+ }
+
+ s->ticking[n] = 1;
+#ifdef DEBUG
+ interval = ticks_per_sec * interval_Sec;
+ exp = qemu_get_clock (vm_clock) + interval;
+ s->exp[n] = exp;
+#endif
+
+ s->dexp[n] = interval_Sec * 1000000.0;
+ AUD_init_time_stamp_out (s->voice, &s->ats);
+}
+
+static int write_audio (AdlibState *s, int samples)
+{
+ int net = 0;
+ int pos = s->pos;
+
+ while (samples) {
+ int nbytes, wbytes, wsampl;
+
+ nbytes = samples << SHIFT;
+ wbytes = AUD_write (
+ s->voice,
+ s->mixbuf + (pos << (SHIFT - 1)),
+ nbytes
+ );
+
+ if (wbytes) {
+ wsampl = wbytes >> SHIFT;
+
+ samples -= wsampl;
+ pos = (pos + wsampl) % s->samples;
+
+ net += wsampl;
+ }
+ else {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static void adlib_callback (void *opaque, int free)
+{
+ AdlibState *s = opaque;
+ int samples, net = 0, to_play, written;
+
+ samples = free >> SHIFT;
+ if (!(s->active && s->enabled) || !samples) {
+ return;
+ }
+
+ to_play = audio_MIN (s->left, samples);
+ while (to_play) {
+ written = write_audio (s, to_play);
+
+ if (written) {
+ s->left -= written;
+ samples -= written;
+ to_play -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ return;
+ }
+ }
+
+ samples = audio_MIN (samples, s->samples - s->pos);
+ if (!samples) {
+ return;
+ }
+
+#ifdef HAS_YMF262
+ YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
+#else
+ YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
+#endif
+
+ while (samples) {
+ written = write_audio (s, samples);
+
+ if (written) {
+ net += written;
+ samples -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ s->left = samples;
+ return;
+ }
+ }
+}
+
+static void Adlib_fini (AdlibState *s)
+{
+#ifdef HAS_YMF262
+ YMF262Shutdown ();
+#else
+ if (s->opl) {
+ OPLDestroy (s->opl);
+ s->opl = NULL;
+ }
+#endif
+
+ if (s->mixbuf) {
+ qemu_free (s->mixbuf);
+ }
+
+ s->active = 0;
+ s->enabled = 0;
+ AUD_remove_card (&s->card);
+}
+
+int Adlib_init (AudioState *audio)
+{
+ AdlibState *s = &glob_adlib;
+ audsettings_t as;
+
+ if (!audio) {
+ dolog ("No audio state\n");
+ return -1;
+ }
+
+#ifdef HAS_YMF262
+ if (YMF262Init (1, 14318180, conf.freq)) {
+ dolog ("YMF262Init %d failed\n", conf.freq);
+ return -1;
+ }
+ else {
+ YMF262SetTimerHandler (0, timer_handler, 0);
+ s->enabled = 1;
+ }
+#else
+ s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
+ if (!s->opl) {
+ dolog ("OPLCreate %d failed\n", conf.freq);
+ return -1;
+ }
+ else {
+ OPLSetTimerHandler (s->opl, timer_handler, 0);
+ s->enabled = 1;
+ }
+#endif
+
+ as.freq = conf.freq;
+ as.nchannels = SHIFT;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ AUD_register_card (audio, "adlib", &s->card);
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "adlib",
+ s,
+ adlib_callback,
+ &as
+ );
+ if (!s->voice) {
+ Adlib_fini (s);
+ return -1;
+ }
+
+ s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
+ s->mixbuf = qemu_mallocz (s->samples << SHIFT);
+
+ if (!s->mixbuf) {
+ dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n",
+ s->samples, 1 << SHIFT);
+ Adlib_fini (s);
+ return -1;
+ }
+
+ register_ioport_read (0x388, 4, 1, adlib_read, s);
+ register_ioport_write (0x388, 4, 1, adlib_write, s);
+
+ register_ioport_read (conf.port, 4, 1, adlib_read, s);
+ register_ioport_write (conf.port, 4, 1, adlib_write, s);
+
+ register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
+ register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
+
+ return 0;
+}
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
new file mode 100644
index 0000000..02e9824
--- /dev/null
+++ b/hw/apb_pci.c
@@ -0,0 +1,232 @@
+/*
+ * QEMU Ultrasparc APB PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+typedef target_phys_addr_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState APBState;
+
+static void pci_apb_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ APBState *s = opaque;
+ int i;
+
+ for (i = 11; i < 32; i++) {
+ if ((val & (1 << i)) != 0)
+ break;
+ }
+ s->config_reg = (1 << 16) | (val & 0x7FC) | (i << 11);
+}
+
+static uint32_t pci_apb_config_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ APBState *s = opaque;
+ uint32_t val;
+ int devfn;
+
+ devfn = (s->config_reg >> 8) & 0xFF;
+ val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC);
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_apb_config_write[] = {
+ &pci_apb_config_writel,
+ &pci_apb_config_writel,
+ &pci_apb_config_writel,
+};
+
+static CPUReadMemoryFunc *pci_apb_config_read[] = {
+ &pci_apb_config_readl,
+ &pci_apb_config_readl,
+ &pci_apb_config_readl,
+};
+
+static void apb_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ //PCIBus *s = opaque;
+
+ switch (addr & 0x3f) {
+ case 0x00: // Control/Status
+ case 0x10: // AFSR
+ case 0x18: // AFAR
+ case 0x20: // Diagnostic
+ case 0x28: // Target address space
+ // XXX
+ default:
+ break;
+ }
+}
+
+static uint32_t apb_config_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ //PCIBus *s = opaque;
+ uint32_t val;
+
+ switch (addr & 0x3f) {
+ case 0x00: // Control/Status
+ case 0x10: // AFSR
+ case 0x18: // AFAR
+ case 0x20: // Diagnostic
+ case 0x28: // Target address space
+ // XXX
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static CPUWriteMemoryFunc *apb_config_write[] = {
+ &apb_config_writel,
+ &apb_config_writel,
+ &apb_config_writel,
+};
+
+static CPUReadMemoryFunc *apb_config_read[] = {
+ &apb_config_readl,
+ &apb_config_readl,
+ &apb_config_readl,
+};
+
+static CPUWriteMemoryFunc *pci_apb_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static CPUReadMemoryFunc *pci_apb_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outb(NULL, addr & 0xffff, val);
+}
+
+static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outw(NULL, addr & 0xffff, val);
+}
+
+static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outl(NULL, addr & 0xffff, val);
+}
+
+static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = cpu_inb(NULL, addr & 0xffff);
+ return val;
+}
+
+static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = cpu_inw(NULL, addr & 0xffff);
+ return val;
+}
+
+static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = cpu_inl(NULL, addr & 0xffff);
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_apb_iowrite[] = {
+ &pci_apb_iowriteb,
+ &pci_apb_iowritew,
+ &pci_apb_iowritel,
+};
+
+static CPUReadMemoryFunc *pci_apb_ioread[] = {
+ &pci_apb_ioreadb,
+ &pci_apb_ioreadw,
+ &pci_apb_ioreadl,
+};
+
+/* ??? This is probably wrong. */
+static void pci_apb_set_irq(PCIDevice *d, void *pic, int irq_num, int level)
+{
+ pic_set_irq_new(pic, d->config[PCI_INTERRUPT_LINE], level);
+}
+
+PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base,
+ void *pic)
+{
+ APBState *s;
+ PCIDevice *d;
+ int pci_mem_config, pci_mem_data, apb_config, pci_ioport;
+
+ s = qemu_mallocz(sizeof(APBState));
+ /* Ultrasparc APB main bus */
+ s->bus = pci_register_bus(pci_apb_set_irq, pic, 0);
+
+ pci_mem_config = cpu_register_io_memory(0, pci_apb_config_read,
+ pci_apb_config_write, s);
+ apb_config = cpu_register_io_memory(0, apb_config_read,
+ apb_config_write, s);
+ pci_mem_data = cpu_register_io_memory(0, pci_apb_read,
+ pci_apb_write, s);
+ pci_ioport = cpu_register_io_memory(0, pci_apb_ioread,
+ pci_apb_iowrite, s);
+
+ cpu_register_physical_memory(special_base + 0x2000ULL, 0x40, apb_config);
+ cpu_register_physical_memory(special_base + 0x1000000ULL, 0x10, pci_mem_config);
+ cpu_register_physical_memory(special_base + 0x2000000ULL, 0x10000, pci_ioport);
+ cpu_register_physical_memory(mem_base, 0x10000000, pci_mem_data); // XXX size should be 4G-prom
+
+ d = pci_register_device(s->bus, "Advanced PCI Bus", sizeof(PCIDevice),
+ -1, NULL, NULL);
+ d->config[0x00] = 0x8e; // vendor_id : Sun
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x00; // device_id
+ d->config[0x03] = 0xa0;
+ d->config[0x04] = 0x06; // command = bus master, pci mem
+ d->config[0x05] = 0x00;
+ d->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
+ d->config[0x07] = 0x03; // status = medium devsel
+ d->config[0x08] = 0x00; // revision
+ d->config[0x09] = 0x00; // programming i/f
+ d->config[0x0A] = 0x00; // class_sub = pci host
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x0E] = 0x00; // header_type
+ return s->bus;
+}
+
+
diff --git a/hw/apic.c b/hw/apic.c
new file mode 100644
index 0000000..8f88cce
--- /dev/null
+++ b/hw/apic.c
@@ -0,0 +1,1042 @@
+/*
+ * APIC support
+ *
+ * Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "vl.h"
+
+//#define DEBUG_APIC
+//#define DEBUG_IOAPIC
+
+/* APIC Local Vector Table */
+#define APIC_LVT_TIMER 0
+#define APIC_LVT_THERMAL 1
+#define APIC_LVT_PERFORM 2
+#define APIC_LVT_LINT0 3
+#define APIC_LVT_LINT1 4
+#define APIC_LVT_ERROR 5
+#define APIC_LVT_NB 6
+
+/* APIC delivery modes */
+#define APIC_DM_FIXED 0
+#define APIC_DM_LOWPRI 1
+#define APIC_DM_SMI 2
+#define APIC_DM_NMI 4
+#define APIC_DM_INIT 5
+#define APIC_DM_SIPI 6
+#define APIC_DM_EXTINT 7
+
+/* APIC destination mode */
+#define APIC_DESTMODE_FLAT 0xf
+#define APIC_DESTMODE_CLUSTER 1
+
+#define APIC_TRIGGER_EDGE 0
+#define APIC_TRIGGER_LEVEL 1
+
+#define APIC_LVT_TIMER_PERIODIC (1<<17)
+#define APIC_LVT_MASKED (1<<16)
+#define APIC_LVT_LEVEL_TRIGGER (1<<15)
+#define APIC_LVT_REMOTE_IRR (1<<14)
+#define APIC_INPUT_POLARITY (1<<13)
+#define APIC_SEND_PENDING (1<<12)
+
+#define IOAPIC_NUM_PINS 0x18
+
+#define ESR_ILLEGAL_ADDRESS (1 << 7)
+
+#define APIC_SV_ENABLE (1 << 8)
+
+#define MAX_APICS 255
+#define MAX_APIC_WORDS 8
+
+typedef struct APICState {
+ CPUState *cpu_env;
+ uint32_t apicbase;
+ uint8_t id;
+ uint8_t arb_id;
+ uint8_t tpr;
+ uint32_t spurious_vec;
+ uint8_t log_dest;
+ uint8_t dest_mode;
+ uint32_t isr[8]; /* in service register */
+ uint32_t tmr[8]; /* trigger mode register */
+ uint32_t irr[8]; /* interrupt request register */
+ uint32_t lvt[APIC_LVT_NB];
+ uint32_t esr; /* error register */
+ uint32_t icr[2];
+
+ uint32_t divide_conf;
+ int count_shift;
+ uint32_t initial_count;
+ int64_t initial_count_load_time, next_time;
+ QEMUTimer *timer;
+} APICState;
+
+struct IOAPICState {
+ uint8_t id;
+ uint8_t ioregsel;
+
+ uint32_t irr;
+ uint64_t ioredtbl[IOAPIC_NUM_PINS];
+};
+
+static int apic_io_memory;
+static APICState *local_apics[MAX_APICS + 1];
+static int last_apic_id = 0;
+
+static void apic_init_ipi(APICState *s);
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
+static void apic_update_irq(APICState *s);
+
+/* Find first bit starting from msb. Return 0 if value = 0 */
+static int fls_bit(uint32_t value)
+{
+ unsigned int ret = 0;
+
+#if defined(HOST_I386)
+ __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value));
+ return ret;
+#else
+ if (value > 0xffff)
+ value >>= 16, ret = 16;
+ if (value > 0xff)
+ value >>= 8, ret += 8;
+ if (value > 0xf)
+ value >>= 4, ret += 4;
+ if (value > 0x3)
+ value >>= 2, ret += 2;
+ return ret + (value >> 1);
+#endif
+}
+
+/* Find first bit starting from lsb. Return 0 if value = 0 */
+static int ffs_bit(uint32_t value)
+{
+ unsigned int ret = 0;
+
+#if defined(HOST_I386)
+ __asm__ __volatile__ ("bsf %1, %0\n" : "+r" (ret) : "rm" (value));
+ return ret;
+#else
+ if (!value)
+ return 0;
+ if (!(value & 0xffff))
+ value >>= 16, ret = 16;
+ if (!(value & 0xff))
+ value >>= 8, ret += 8;
+ if (!(value & 0xf))
+ value >>= 4, ret += 4;
+ if (!(value & 0x3))
+ value >>= 2, ret += 2;
+ if (!(value & 0x1))
+ ret++;
+ return ret;
+#endif
+}
+
+static inline void set_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ tab[i] |= mask;
+}
+
+static inline void reset_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ tab[i] &= ~mask;
+}
+
+#define foreach_apic(apic, deliver_bitmask, code) \
+{\
+ int __i, __j, __mask;\
+ for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
+ __mask = deliver_bitmask[__i];\
+ if (__mask) {\
+ for(__j = 0; __j < 32; __j++) {\
+ if (__mask & (1 << __j)) {\
+ apic = local_apics[__i * 32 + __j];\
+ if (apic) {\
+ code;\
+ }\
+ }\
+ }\
+ }\
+ }\
+}
+
+static void apic_bus_deliver(const uint32_t *deliver_bitmask,
+ uint8_t delivery_mode,
+ uint8_t vector_num, uint8_t polarity,
+ uint8_t trigger_mode)
+{
+ APICState *apic_iter;
+
+ switch (delivery_mode) {
+ case APIC_DM_LOWPRI:
+ /* XXX: search for focus processor, arbitration */
+ {
+ int i, d;
+ d = -1;
+ for(i = 0; i < MAX_APIC_WORDS; i++) {
+ if (deliver_bitmask[i]) {
+ d = i * 32 + ffs_bit(deliver_bitmask[i]);
+ break;
+ }
+ }
+ if (d >= 0) {
+ apic_iter = local_apics[d];
+ if (apic_iter) {
+ apic_set_irq(apic_iter, vector_num, trigger_mode);
+ }
+ }
+ }
+ return;
+
+ case APIC_DM_FIXED:
+ break;
+
+ case APIC_DM_SMI:
+ case APIC_DM_NMI:
+ break;
+
+ case APIC_DM_INIT:
+ /* normal INIT IPI sent to processors */
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_init_ipi(apic_iter) );
+ return;
+
+ case APIC_DM_EXTINT:
+ /* handled in I/O APIC code */
+ break;
+
+ default:
+ return;
+ }
+
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_set_irq(apic_iter, vector_num, trigger_mode) );
+}
+
+void cpu_set_apic_base(CPUState *env, uint64_t val)
+{
+ APICState *s = env->apic_state;
+#ifdef DEBUG_APIC
+ printf("cpu_set_apic_base: %016" PRIx64 "\n", val);
+#endif
+ s->apicbase = (val & 0xfffff000) |
+ (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
+ /* if disabled, cannot be enabled again */
+ if (!(val & MSR_IA32_APICBASE_ENABLE)) {
+ s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
+ env->cpuid_features &= ~CPUID_APIC;
+ s->spurious_vec &= ~APIC_SV_ENABLE;
+ }
+}
+
+uint64_t cpu_get_apic_base(CPUState *env)
+{
+ APICState *s = env->apic_state;
+#ifdef DEBUG_APIC
+ printf("cpu_get_apic_base: %016" PRIx64 "\n", (uint64_t)s->apicbase);
+#endif
+ return s->apicbase;
+}
+
+void cpu_set_apic_tpr(CPUX86State *env, uint8_t val)
+{
+ APICState *s = env->apic_state;
+ s->tpr = (val & 0x0f) << 4;
+ apic_update_irq(s);
+}
+
+uint8_t cpu_get_apic_tpr(CPUX86State *env)
+{
+ APICState *s = env->apic_state;
+ return s->tpr >> 4;
+}
+
+/* return -1 if no bit is set */
+static int get_highest_priority_int(uint32_t *tab)
+{
+ int i;
+ for(i = 7; i >= 0; i--) {
+ if (tab[i] != 0) {
+ return i * 32 + fls_bit(tab[i]);
+ }
+ }
+ return -1;
+}
+
+static int apic_get_ppr(APICState *s)
+{
+ int tpr, isrv, ppr;
+
+ tpr = (s->tpr >> 4);
+ isrv = get_highest_priority_int(s->isr);
+ if (isrv < 0)
+ isrv = 0;
+ isrv >>= 4;
+ if (tpr >= isrv)
+ ppr = s->tpr;
+ else
+ ppr = isrv << 4;
+ return ppr;
+}
+
+static int apic_get_arb_pri(APICState *s)
+{
+ /* XXX: arbitration */
+ return 0;
+}
+
+/* signal the CPU if an irq is pending */
+static void apic_update_irq(APICState *s)
+{
+ int irrv, ppr;
+ if (!(s->spurious_vec & APIC_SV_ENABLE))
+ return;
+ irrv = get_highest_priority_int(s->irr);
+ if (irrv < 0)
+ return;
+ ppr = apic_get_ppr(s);
+ if (ppr && (irrv & 0xf0) <= (ppr & 0xf0))
+ return;
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+}
+
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
+{
+ set_bit(s->irr, vector_num);
+ if (trigger_mode)
+ set_bit(s->tmr, vector_num);
+ else
+ reset_bit(s->tmr, vector_num);
+ apic_update_irq(s);
+}
+
+static void apic_eoi(APICState *s)
+{
+ int isrv;
+ isrv = get_highest_priority_int(s->isr);
+ if (isrv < 0)
+ return;
+ reset_bit(s->isr, isrv);
+ /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to
+ set the remote IRR bit for level triggered interrupts. */
+ apic_update_irq(s);
+}
+
+static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
+ uint8_t dest, uint8_t dest_mode)
+{
+ APICState *apic_iter;
+ int i;
+
+ if (dest_mode == 0) {
+ if (dest == 0xff) {
+ memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
+ } else {
+ memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+ set_bit(deliver_bitmask, dest);
+ }
+ } else {
+ /* XXX: cluster mode */
+ memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+ for(i = 0; i < MAX_APICS; i++) {
+ apic_iter = local_apics[i];
+ if (apic_iter) {
+ if (apic_iter->dest_mode == 0xf) {
+ if (dest & apic_iter->log_dest)
+ set_bit(deliver_bitmask, i);
+ } else if (apic_iter->dest_mode == 0x0) {
+ if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
+ (dest & apic_iter->log_dest & 0x0f)) {
+ set_bit(deliver_bitmask, i);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void apic_init_ipi(APICState *s)
+{
+ int i;
+
+ for(i = 0; i < APIC_LVT_NB; i++)
+ s->lvt[i] = 1 << 16; /* mask LVT */
+ s->tpr = 0;
+ s->spurious_vec = 0xff;
+ s->log_dest = 0;
+ s->dest_mode = 0xf;
+ memset(s->isr, 0, sizeof(s->isr));
+ memset(s->tmr, 0, sizeof(s->tmr));
+ memset(s->irr, 0, sizeof(s->irr));
+ memset(s->lvt, 0, sizeof(s->lvt));
+ s->esr = 0;
+ memset(s->icr, 0, sizeof(s->icr));
+ s->divide_conf = 0;
+ s->count_shift = 0;
+ s->initial_count = 0;
+ s->initial_count_load_time = 0;
+ s->next_time = 0;
+}
+
+/* send a SIPI message to the CPU to start it */
+static void apic_startup(APICState *s, int vector_num)
+{
+ CPUState *env = s->cpu_env;
+ if (!(env->hflags & HF_HALTED_MASK))
+ return;
+ env->eip = 0;
+ cpu_x86_load_seg_cache(env, R_CS, vector_num << 8, vector_num << 12,
+ 0xffff, 0);
+ env->hflags &= ~HF_HALTED_MASK;
+}
+
+static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode,
+ uint8_t delivery_mode, uint8_t vector_num,
+ uint8_t polarity, uint8_t trigger_mode)
+{
+ uint32_t deliver_bitmask[MAX_APIC_WORDS];
+ int dest_shorthand = (s->icr[0] >> 18) & 3;
+ APICState *apic_iter;
+
+ switch (dest_shorthand) {
+ case 0:
+ apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+ break;
+ case 1:
+ memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
+ set_bit(deliver_bitmask, s->id);
+ break;
+ case 2:
+ memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+ break;
+ case 3:
+ memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+ reset_bit(deliver_bitmask, s->id);
+ break;
+ }
+
+ switch (delivery_mode) {
+ case APIC_DM_INIT:
+ {
+ int trig_mode = (s->icr[0] >> 15) & 1;
+ int level = (s->icr[0] >> 14) & 1;
+ if (level == 0 && trig_mode == 1) {
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_iter->arb_id = apic_iter->id );
+ return;
+ }
+ }
+ break;
+
+ case APIC_DM_SIPI:
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_startup(apic_iter, vector_num) );
+ return;
+ }
+
+ apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
+ trigger_mode);
+}
+
+int apic_get_interrupt(CPUState *env)
+{
+ APICState *s = env->apic_state;
+ int intno;
+
+ /* if the APIC is installed or enabled, we let the 8259 handle the
+ IRQs */
+ if (!s)
+ return -1;
+ if (!(s->spurious_vec & APIC_SV_ENABLE))
+ return -1;
+
+ /* XXX: spurious IRQ handling */
+ intno = get_highest_priority_int(s->irr);
+ if (intno < 0)
+ return -1;
+ reset_bit(s->irr, intno);
+ if (s->tpr && intno <= s->tpr)
+ return s->spurious_vec & 0xff;
+ set_bit(s->isr, intno);
+ apic_update_irq(s);
+ return intno;
+}
+
+static uint32_t apic_get_current_count(APICState *s)
+{
+ int64_t d;
+ uint32_t val;
+ d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ /* periodic */
+ val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
+ } else {
+ if (d >= s->initial_count)
+ val = 0;
+ else
+ val = s->initial_count - d;
+ }
+ return val;
+}
+
+static void apic_timer_update(APICState *s, int64_t current_time)
+{
+ int64_t next_time, d;
+
+ if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
+ d = (current_time - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1);
+ } else {
+ if (d >= s->initial_count)
+ goto no_timer;
+ d = (uint64_t)s->initial_count + 1;
+ }
+ next_time = s->initial_count_load_time + (d << s->count_shift);
+ qemu_mod_timer(s->timer, next_time);
+ s->next_time = next_time;
+ } else {
+ no_timer:
+ qemu_del_timer(s->timer);
+ }
+}
+
+static void apic_timer(void *opaque)
+{
+ APICState *s = opaque;
+
+ if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
+ apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE);
+ }
+ apic_timer_update(s, s->next_time);
+}
+
+static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ CPUState *env;
+ APICState *s;
+ uint32_t val;
+ int index;
+
+ env = cpu_single_env;
+ if (!env)
+ return 0;
+ s = env->apic_state;
+
+ index = (addr >> 4) & 0xff;
+ switch(index) {
+ case 0x02: /* id */
+ val = s->id << 24;
+ break;
+ case 0x03: /* version */
+ val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
+ break;
+ case 0x08:
+ val = s->tpr;
+ break;
+ case 0x09:
+ val = apic_get_arb_pri(s);
+ break;
+ case 0x0a:
+ /* ppr */
+ val = apic_get_ppr(s);
+ break;
+ case 0x0d:
+ val = s->log_dest << 24;
+ break;
+ case 0x0e:
+ val = s->dest_mode << 28;
+ break;
+ case 0x0f:
+ val = s->spurious_vec;
+ break;
+ case 0x10 ... 0x17:
+ val = s->isr[index & 7];
+ break;
+ case 0x18 ... 0x1f:
+ val = s->tmr[index & 7];
+ break;
+ case 0x20 ... 0x27:
+ val = s->irr[index & 7];
+ break;
+ case 0x28:
+ val = s->esr;
+ break;
+ case 0x30:
+ case 0x31:
+ val = s->icr[index & 1];
+ break;
+ case 0x32 ... 0x37:
+ val = s->lvt[index - 0x32];
+ break;
+ case 0x38:
+ val = s->initial_count;
+ break;
+ case 0x39:
+ val = apic_get_current_count(s);
+ break;
+ case 0x3e:
+ val = s->divide_conf;
+ break;
+ default:
+ s->esr |= ESR_ILLEGAL_ADDRESS;
+ val = 0;
+ break;
+ }
+#ifdef DEBUG_APIC
+ printf("APIC read: %08x = %08x\n", (uint32_t)addr, val);
+#endif
+ return val;
+}
+
+static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ CPUState *env;
+ APICState *s;
+ int index;
+
+ env = cpu_single_env;
+ if (!env)
+ return;
+ s = env->apic_state;
+
+#ifdef DEBUG_APIC
+ printf("APIC write: %08x = %08x\n", (uint32_t)addr, val);
+#endif
+
+ index = (addr >> 4) & 0xff;
+ switch(index) {
+ case 0x02:
+ s->id = (val >> 24);
+ break;
+ case 0x03:
+ break;
+ case 0x08:
+ s->tpr = val;
+ apic_update_irq(s);
+ break;
+ case 0x09:
+ case 0x0a:
+ break;
+ case 0x0b: /* EOI */
+ apic_eoi(s);
+ break;
+ case 0x0d:
+ s->log_dest = val >> 24;
+ break;
+ case 0x0e:
+ s->dest_mode = val >> 28;
+ break;
+ case 0x0f:
+ s->spurious_vec = val & 0x1ff;
+ apic_update_irq(s);
+ break;
+ case 0x10 ... 0x17:
+ case 0x18 ... 0x1f:
+ case 0x20 ... 0x27:
+ case 0x28:
+ break;
+ case 0x30:
+ s->icr[0] = val;
+ apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
+ (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
+ (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1);
+ break;
+ case 0x31:
+ s->icr[1] = val;
+ break;
+ case 0x32 ... 0x37:
+ {
+ int n = index - 0x32;
+ s->lvt[n] = val;
+ if (n == APIC_LVT_TIMER)
+ apic_timer_update(s, qemu_get_clock(vm_clock));
+ }
+ break;
+ case 0x38:
+ s->initial_count = val;
+ s->initial_count_load_time = qemu_get_clock(vm_clock);
+ apic_timer_update(s, s->initial_count_load_time);
+ break;
+ case 0x39:
+ break;
+ case 0x3e:
+ {
+ int v;
+ s->divide_conf = val & 0xb;
+ v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+ s->count_shift = (v + 1) & 7;
+ }
+ break;
+ default:
+ s->esr |= ESR_ILLEGAL_ADDRESS;
+ break;
+ }
+}
+
+static void apic_save(QEMUFile *f, void *opaque)
+{
+ APICState *s = opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->apicbase);
+ qemu_put_8s(f, &s->id);
+ qemu_put_8s(f, &s->arb_id);
+ qemu_put_8s(f, &s->tpr);
+ qemu_put_be32s(f, &s->spurious_vec);
+ qemu_put_8s(f, &s->log_dest);
+ qemu_put_8s(f, &s->dest_mode);
+ for (i = 0; i < 8; i++) {
+ qemu_put_be32s(f, &s->isr[i]);
+ qemu_put_be32s(f, &s->tmr[i]);
+ qemu_put_be32s(f, &s->irr[i]);
+ }
+ for (i = 0; i < APIC_LVT_NB; i++) {
+ qemu_put_be32s(f, &s->lvt[i]);
+ }
+ qemu_put_be32s(f, &s->esr);
+ qemu_put_be32s(f, &s->icr[0]);
+ qemu_put_be32s(f, &s->icr[1]);
+ qemu_put_be32s(f, &s->divide_conf);
+ qemu_put_be32s(f, &s->count_shift);
+ qemu_put_be32s(f, &s->initial_count);
+ qemu_put_be64s(f, &s->initial_count_load_time);
+ qemu_put_be64s(f, &s->next_time);
+}
+
+static int apic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ APICState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ /* XXX: what if the base changes? (registered memory regions) */
+ qemu_get_be32s(f, &s->apicbase);
+ qemu_get_8s(f, &s->id);
+ qemu_get_8s(f, &s->arb_id);
+ qemu_get_8s(f, &s->tpr);
+ qemu_get_be32s(f, &s->spurious_vec);
+ qemu_get_8s(f, &s->log_dest);
+ qemu_get_8s(f, &s->dest_mode);
+ for (i = 0; i < 8; i++) {
+ qemu_get_be32s(f, &s->isr[i]);
+ qemu_get_be32s(f, &s->tmr[i]);
+ qemu_get_be32s(f, &s->irr[i]);
+ }
+ for (i = 0; i < APIC_LVT_NB; i++) {
+ qemu_get_be32s(f, &s->lvt[i]);
+ }
+ qemu_get_be32s(f, &s->esr);
+ qemu_get_be32s(f, &s->icr[0]);
+ qemu_get_be32s(f, &s->icr[1]);
+ qemu_get_be32s(f, &s->divide_conf);
+ qemu_get_be32s(f, &s->count_shift);
+ qemu_get_be32s(f, &s->initial_count);
+ qemu_get_be64s(f, &s->initial_count_load_time);
+ qemu_get_be64s(f, &s->next_time);
+ return 0;
+}
+
+static void apic_reset(void *opaque)
+{
+ APICState *s = opaque;
+ apic_init_ipi(s);
+}
+
+static CPUReadMemoryFunc *apic_mem_read[3] = {
+ apic_mem_readb,
+ apic_mem_readw,
+ apic_mem_readl,
+};
+
+static CPUWriteMemoryFunc *apic_mem_write[3] = {
+ apic_mem_writeb,
+ apic_mem_writew,
+ apic_mem_writel,
+};
+
+int apic_init(CPUState *env)
+{
+ APICState *s;
+
+ if (last_apic_id >= MAX_APICS)
+ return -1;
+ s = qemu_mallocz(sizeof(APICState));
+ if (!s)
+ return -1;
+ env->apic_state = s;
+ apic_init_ipi(s);
+ s->id = last_apic_id++;
+ s->cpu_env = env;
+ s->apicbase = 0xfee00000 |
+ (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE;
+
+ /* XXX: mapping more APICs at the same memory location */
+ if (apic_io_memory == 0) {
+ /* NOTE: the APIC is directly connected to the CPU - it is not
+ on the global memory bus. */
+ apic_io_memory = cpu_register_io_memory(0, apic_mem_read,
+ apic_mem_write, NULL);
+ cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000,
+ apic_io_memory);
+ }
+ s->timer = qemu_new_timer(vm_clock, apic_timer, s);
+
+ register_savevm("apic", 0, 1, apic_save, apic_load, s);
+ qemu_register_reset(apic_reset, s);
+
+ local_apics[s->id] = s;
+ return 0;
+}
+
+static void ioapic_service(IOAPICState *s)
+{
+ uint8_t i;
+ uint8_t trig_mode;
+ uint8_t vector;
+ uint8_t delivery_mode;
+ uint32_t mask;
+ uint64_t entry;
+ uint8_t dest;
+ uint8_t dest_mode;
+ uint8_t polarity;
+ uint32_t deliver_bitmask[MAX_APIC_WORDS];
+
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ mask = 1 << i;
+ if (s->irr & mask) {
+ entry = s->ioredtbl[i];
+ if (!(entry & APIC_LVT_MASKED)) {
+ trig_mode = ((entry >> 15) & 1);
+ dest = entry >> 56;
+ dest_mode = (entry >> 11) & 1;
+ delivery_mode = (entry >> 8) & 7;
+ polarity = (entry >> 13) & 1;
+ if (trig_mode == APIC_TRIGGER_EDGE)
+ s->irr &= ~mask;
+ if (delivery_mode == APIC_DM_EXTINT)
+ vector = pic_read_irq(isa_pic);
+ else
+ vector = entry & 0xff;
+
+ apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+ apic_bus_deliver(deliver_bitmask, delivery_mode,
+ vector, polarity, trig_mode);
+ }
+ }
+ }
+}
+
+void ioapic_set_irq(void *opaque, int vector, int level)
+{
+ IOAPICState *s = opaque;
+
+ if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
+ uint32_t mask = 1 << vector;
+ uint64_t entry = s->ioredtbl[vector];
+
+ if ((entry >> 15) & 1) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ ioapic_service(s);
+ } else {
+ s->irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ s->irr |= mask;
+ ioapic_service(s);
+ }
+ }
+ }
+}
+
+static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ IOAPICState *s = opaque;
+ int index;
+ uint32_t val = 0;
+
+ addr &= 0xff;
+ if (addr == 0x00) {
+ val = s->ioregsel;
+ } else if (addr == 0x10) {
+ switch (s->ioregsel) {
+ case 0x00:
+ val = s->id << 24;
+ break;
+ case 0x01:
+ val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */
+ break;
+ case 0x02:
+ val = 0;
+ break;
+ default:
+ index = (s->ioregsel - 0x10) >> 1;
+ if (index >= 0 && index < IOAPIC_NUM_PINS) {
+ if (s->ioregsel & 1)
+ val = s->ioredtbl[index] >> 32;
+ else
+ val = s->ioredtbl[index] & 0xffffffff;
+ }
+ }
+#ifdef DEBUG_IOAPIC
+ printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val);
+#endif
+ }
+ return val;
+}
+
+static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IOAPICState *s = opaque;
+ int index;
+
+ addr &= 0xff;
+ if (addr == 0x00) {
+ s->ioregsel = val;
+ return;
+ } else if (addr == 0x10) {
+#ifdef DEBUG_IOAPIC
+ printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val);
+#endif
+ switch (s->ioregsel) {
+ case 0x00:
+ s->id = (val >> 24) & 0xff;
+ return;
+ case 0x01:
+ case 0x02:
+ return;
+ default:
+ index = (s->ioregsel - 0x10) >> 1;
+ if (index >= 0 && index < IOAPIC_NUM_PINS) {
+ if (s->ioregsel & 1) {
+ s->ioredtbl[index] &= 0xffffffff;
+ s->ioredtbl[index] |= (uint64_t)val << 32;
+ } else {
+ s->ioredtbl[index] &= ~0xffffffffULL;
+ s->ioredtbl[index] |= val;
+ }
+ ioapic_service(s);
+ }
+ }
+ }
+}
+
+static void ioapic_save(QEMUFile *f, void *opaque)
+{
+ IOAPICState *s = opaque;
+ int i;
+
+ qemu_put_8s(f, &s->id);
+ qemu_put_8s(f, &s->ioregsel);
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ qemu_put_be64s(f, &s->ioredtbl[i]);
+ }
+}
+
+static int ioapic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ IOAPICState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_8s(f, &s->id);
+ qemu_get_8s(f, &s->ioregsel);
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ qemu_get_be64s(f, &s->ioredtbl[i]);
+ }
+ return 0;
+}
+
+static void ioapic_reset(void *opaque)
+{
+ IOAPICState *s = opaque;
+ int i;
+
+ memset(s, 0, sizeof(*s));
+ for(i = 0; i < IOAPIC_NUM_PINS; i++)
+ s->ioredtbl[i] = 1 << 16; /* mask LVT */
+}
+
+static CPUReadMemoryFunc *ioapic_mem_read[3] = {
+ ioapic_mem_readl,
+ ioapic_mem_readl,
+ ioapic_mem_readl,
+};
+
+static CPUWriteMemoryFunc *ioapic_mem_write[3] = {
+ ioapic_mem_writel,
+ ioapic_mem_writel,
+ ioapic_mem_writel,
+};
+
+IOAPICState *ioapic_init(void)
+{
+ IOAPICState *s;
+ int io_memory;
+
+ s = qemu_mallocz(sizeof(IOAPICState));
+ if (!s)
+ return NULL;
+ ioapic_reset(s);
+ s->id = last_apic_id++;
+
+ io_memory = cpu_register_io_memory(0, ioapic_mem_read,
+ ioapic_mem_write, s);
+ cpu_register_physical_memory(0xfec00000, 0x1000, io_memory);
+
+ register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s);
+ qemu_register_reset(ioapic_reset, s);
+
+ return s;
+}
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
new file mode 100644
index 0000000..0e28d4a
--- /dev/null
+++ b/hw/arm_boot.c
@@ -0,0 +1,105 @@
+/*
+ * ARM kernel loader.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+#define INITRD_LOAD_ADDR 0x00800000
+
+/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+static uint32_t bootloader[] = {
+ 0xe3a00000, /* mov r0, #0 */
+ 0xe3a01000, /* mov r1, #0x?? */
+ 0xe3811c00, /* orr r1, r1, #0x??00 */
+ 0xe59f2000, /* ldr r2, [pc, #0] */
+ 0xe59ff000, /* ldr pc, [pc, #0] */
+ 0, /* Address of kernel args. Set by integratorcp_init. */
+ 0 /* Kernel entry point. Set by integratorcp_init. */
+};
+
+static void set_kernel_args(uint32_t ram_size, int initrd_size,
+ const char *kernel_cmdline)
+{
+ uint32_t *p;
+
+ p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
+ /* ATAG_CORE */
+ stl_raw(p++, 5);
+ stl_raw(p++, 0x54410001);
+ stl_raw(p++, 1);
+ stl_raw(p++, 0x1000);
+ stl_raw(p++, 0);
+ /* ATAG_MEM */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54410002);
+ stl_raw(p++, ram_size);
+ stl_raw(p++, 0);
+ if (initrd_size) {
+ /* ATAG_INITRD2 */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54420005);
+ stl_raw(p++, INITRD_LOAD_ADDR);
+ stl_raw(p++, initrd_size);
+ }
+ if (kernel_cmdline && *kernel_cmdline) {
+ /* ATAG_CMDLINE */
+ int cmdline_size;
+
+ cmdline_size = strlen(kernel_cmdline);
+ memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
+ cmdline_size = (cmdline_size >> 2) + 1;
+ stl_raw(p++, cmdline_size + 2);
+ stl_raw(p++, 0x54410009);
+ p += cmdline_size;
+ }
+ /* ATAG_END */
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+}
+
+void arm_load_kernel(int ram_size, const char *kernel_filename,
+ const char *kernel_cmdline, const char *initrd_filename,
+ int board_id)
+{
+ int kernel_size;
+ int initrd_size;
+ int n;
+
+ /* Load the kernel. */
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+ kernel_size = load_image(kernel_filename,
+ phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+ if (initrd_filename) {
+ initrd_size = load_image(initrd_filename,
+ phys_ram_base + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+ bootloader[1] |= board_id & 0xff;
+ bootloader[2] |= (board_id >> 8) & 0xff;
+ bootloader[5] = KERNEL_ARGS_ADDR;
+ bootloader[6] = KERNEL_LOAD_ADDR;
+ for (n = 0; n < sizeof(bootloader) / 4; n++)
+ stl_raw(phys_ram_base + (n * 4), bootloader[n]);
+ set_kernel_args(ram_size, initrd_size, kernel_cmdline);
+}
+
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
new file mode 100644
index 0000000..fbc2d67
--- /dev/null
+++ b/hw/arm_pic.c
@@ -0,0 +1,73 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_set_irq(int irq, int level)
+{
+ cpu_abort(cpu_single_env, "pic_set_irq");
+}
+
+void pic_info(void)
+{
+}
+
+void irq_info(void)
+{
+}
+
+
+void pic_set_irq_new(void *opaque, int irq, int level)
+{
+ arm_pic_handler *p = (arm_pic_handler *)opaque;
+ /* Call the real handler. */
+ (*p)(opaque, irq, level);
+}
+
+/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
+ Input 0 is IRQ and input 1 is FIQ. */
+typedef struct
+{
+ arm_pic_handler handler;
+ CPUState *cpu_env;
+} arm_pic_cpu_state;
+
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque;
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ if (level)
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ if (level)
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+ break;
+ default:
+ cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n",
+ irq);
+ }
+}
+
+void *arm_pic_init_cpu(CPUState *env)
+{
+ arm_pic_cpu_state *s;
+
+ s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state));
+ s->handler = arm_pic_cpu_handler;
+ s->cpu_env = env;
+ return s;
+}
diff --git a/hw/arm_pic.h b/hw/arm_pic.h
new file mode 100644
index 0000000..b299149
--- /dev/null
+++ b/hw/arm_pic.h
@@ -0,0 +1,27 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Arm hardware uses a wide variety of interrupt handling hardware.
+ * This provides a generic framework for connecting interrupt sources and
+ * inputs.
+ */
+
+#ifndef ARM_INTERRUPT_H
+#define ARM_INTERRUPT_H 1
+
+/* The first element of an individual PIC state structures should
+ be a pointer to the handler routine. */
+typedef void (*arm_pic_handler)(void *opaque, int irq, int level);
+
+/* The CPU is also modeled as an interrupt controller. */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+void *arm_pic_init_cpu(CPUState *env);
+
+#endif /* !ARM_INTERRUPT_H */
+
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
new file mode 100644
index 0000000..a97d73e
--- /dev/null
+++ b/hw/arm_timer.c
@@ -0,0 +1,383 @@
+/*
+ * ARM PrimeCell Timer modules.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* Common timer implementation. */
+
+#define TIMER_CTRL_ONESHOT (1 << 0)
+#define TIMER_CTRL_32BIT (1 << 1)
+#define TIMER_CTRL_DIV1 (0 << 2)
+#define TIMER_CTRL_DIV16 (1 << 2)
+#define TIMER_CTRL_DIV256 (2 << 2)
+#define TIMER_CTRL_IE (1 << 5)
+#define TIMER_CTRL_PERIODIC (1 << 6)
+#define TIMER_CTRL_ENABLE (1 << 7)
+
+typedef struct {
+ int64_t next_time;
+ int64_t expires;
+ int64_t loaded;
+ QEMUTimer *timer;
+ uint32_t control;
+ uint32_t count;
+ uint32_t limit;
+ int raw_freq;
+ int freq;
+ int int_level;
+ void *pic;
+ int irq;
+} arm_timer_state;
+
+/* Calculate the new expiry time of the given timer. */
+
+static void arm_timer_reload(arm_timer_state *s)
+{
+ int64_t delay;
+
+ s->loaded = s->expires;
+ delay = muldiv64(s->count, ticks_per_sec, s->freq);
+ if (delay == 0)
+ delay = 1;
+ s->expires += delay;
+}
+
+/* Check all active timers, and schedule the next timer interrupt. */
+
+static void arm_timer_update(arm_timer_state *s, int64_t now)
+{
+ int64_t next;
+
+ /* Ignore disabled timers. */
+ if ((s->control & TIMER_CTRL_ENABLE) == 0)
+ return;
+ /* Ignore expired one-shot timers. */
+ if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))
+ return;
+ if (s->expires - now <= 0) {
+ /* Timer has expired. */
+ s->int_level = 1;
+ if (s->control & TIMER_CTRL_ONESHOT) {
+ /* One-shot. */
+ s->count = 0;
+ } else {
+ if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
+ /* Free running. */
+ if (s->control & TIMER_CTRL_32BIT)
+ s->count = 0xffffffff;
+ else
+ s->count = 0xffff;
+ } else {
+ /* Periodic. */
+ s->count = s->limit;
+ }
+ }
+ }
+ while (s->expires - now <= 0) {
+ arm_timer_reload(s);
+ }
+ /* Update interrupts. */
+ if (s->int_level && (s->control & TIMER_CTRL_IE)) {
+ pic_set_irq_new(s->pic, s->irq, 1);
+ } else {
+ pic_set_irq_new(s->pic, s->irq, 0);
+ }
+
+ next = now;
+ if (next - s->expires < 0)
+ next = s->expires;
+
+ /* Schedule the next timer interrupt. */
+ if (next == now) {
+ qemu_del_timer(s->timer);
+ s->next_time = 0;
+ } else if (next != s->next_time) {
+ qemu_mod_timer(s->timer, next);
+ s->next_time = next;
+ }
+}
+
+/* Return the current value of the timer. */
+static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)
+{
+ int64_t elapsed;
+ int64_t period;
+
+ if (s->count == 0)
+ return 0;
+ if ((s->control & TIMER_CTRL_ENABLE) == 0)
+ return s->count;
+ elapsed = now - s->loaded;
+ period = s->expires - s->loaded;
+ /* If the timer should have expired then return 0. This can happen
+ when the host timer signal doesnt occur immediately. It's better to
+ have a timer appear to sit at zero for a while than have it wrap
+ around before the guest interrupt is raised. */
+ /* ??? Could we trigger the interrupt here? */
+ if (elapsed > period)
+ return 0;
+ /* We need to calculate count * elapsed / period without overfowing.
+ Scale both elapsed and period so they fit in a 32-bit int. */
+ while (period != (int32_t)period) {
+ period >>= 1;
+ elapsed >>= 1;
+ }
+ return ((uint64_t)s->count * (uint64_t)(int32_t)elapsed)
+ / (int32_t)period;
+}
+
+uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ case 6: /* TimerBGLoad */
+ return s->limit;
+ case 1: /* TimerValue */
+ return arm_timer_getcount(s, qemu_get_clock(vm_clock));
+ case 2: /* TimerControl */
+ return s->control;
+ case 4: /* TimerRIS */
+ return s->int_level;
+ case 5: /* TimerMIS */
+ if ((s->control & TIMER_CTRL_IE) == 0)
+ return 0;
+ return s->int_level;
+ default:
+ cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void arm_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+ int64_t now;
+
+ now = qemu_get_clock(vm_clock);
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ s->limit = value;
+ s->count = value;
+ s->expires = now;
+ arm_timer_reload(s);
+ break;
+ case 1: /* TimerValue */
+ /* ??? Linux seems to want to write to this readonly register.
+ Ignore it. */
+ break;
+ case 2: /* TimerControl */
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Pause the timer if it is running. This may cause some
+ inaccuracy dure to rounding, but avoids a whole lot of other
+ messyness. */
+ s->count = arm_timer_getcount(s, now);
+ }
+ s->control = value;
+ s->freq = s->raw_freq;
+ /* ??? Need to recalculate expiry time after changing divisor. */
+ switch ((value >> 2) & 3) {
+ case 1: s->freq >>= 4; break;
+ case 2: s->freq >>= 8; break;
+ }
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Restart the timer if still enabled. */
+ s->expires = now;
+ arm_timer_reload(s);
+ }
+ break;
+ case 3: /* TimerIntClr */
+ s->int_level = 0;
+ break;
+ case 6: /* TimerBGLoad */
+ s->limit = value;
+ break;
+ default:
+ cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);
+ }
+ arm_timer_update(s, now);
+}
+
+static void arm_timer_tick(void *opaque)
+{
+ int64_t now;
+
+ now = qemu_get_clock(vm_clock);
+ arm_timer_update((arm_timer_state *)opaque, now);
+}
+
+static void *arm_timer_init(uint32_t freq, void *pic, int irq)
+{
+ arm_timer_state *s;
+
+ s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
+ s->pic = pic;
+ s->irq = irq;
+ s->raw_freq = s->freq = 1000000;
+ s->control = TIMER_CTRL_IE;
+ s->count = 0xffffffff;
+
+ s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* ARM PrimeCell SP804 dual timer module.
+ Docs for this device don't seem to be publicly available. This
+ implementation is based on gueswork, the linux kernel sources and the
+ Integrator/CP timer modules. */
+
+typedef struct {
+ /* Include a pseudo-PIC device to merge the two interrupt sources. */
+ arm_pic_handler handler;
+ void *timer[2];
+ int level[2];
+ uint32_t base;
+ /* The output PIC device. */
+ void *pic;
+ int irq;
+} sp804_state;
+
+static void sp804_set_irq(void *opaque, int irq, int level)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ s->level[irq] = level;
+ pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);
+}
+
+static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ offset -= s->base;
+ if (offset < 0x20) {
+ return arm_timer_read(s->timer[0], offset);
+ } else {
+ return arm_timer_read(s->timer[1], offset - 0x20);
+ }
+}
+
+static void sp804_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ offset -= s->base;
+ if (offset < 0x20) {
+ arm_timer_write(s->timer[0], offset, value);
+ } else {
+ arm_timer_write(s->timer[1], offset - 0x20, value);
+ }
+}
+
+static CPUReadMemoryFunc *sp804_readfn[] = {
+ sp804_read,
+ sp804_read,
+ sp804_read
+};
+
+static CPUWriteMemoryFunc *sp804_writefn[] = {
+ sp804_write,
+ sp804_write,
+ sp804_write
+};
+
+void sp804_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ sp804_state *s;
+
+ s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
+ s->handler = sp804_set_irq;
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ /* ??? The timers are actually configurable between 32kHz and 1MHz, but
+ we don't implement that. */
+ s->timer[0] = arm_timer_init(1000000, s, 0);
+ s->timer[1] = arm_timer_init(1000000, s, 1);
+ iomemtype = cpu_register_io_memory(0, sp804_readfn,
+ sp804_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+}
+
+
+/* Integrator/CP timer module. */
+
+typedef struct {
+ void *timer[3];
+ uint32_t base;
+} icp_pit_state;
+
+static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ offset -= s->base;
+ n = offset >> 8;
+ if (n > 3)
+ cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
+
+ return arm_timer_read(s->timer[n], offset & 0xff);
+}
+
+static void icp_pit_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ offset -= s->base;
+ n = offset >> 8;
+ if (n > 3)
+ cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
+
+ arm_timer_write(s->timer[n], offset & 0xff, value);
+}
+
+
+static CPUReadMemoryFunc *icp_pit_readfn[] = {
+ icp_pit_read,
+ icp_pit_read,
+ icp_pit_read
+};
+
+static CPUWriteMemoryFunc *icp_pit_writefn[] = {
+ icp_pit_write,
+ icp_pit_write,
+ icp_pit_write
+};
+
+void icp_pit_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ icp_pit_state *s;
+
+ s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
+ s->base = base;
+ /* Timer 0 runs at the system clock speed (40MHz). */
+ s->timer[0] = arm_timer_init(40000000, pic, irq);
+ /* The other two timers run at 1MHz. */
+ s->timer[1] = arm_timer_init(1000000, pic, irq + 1);
+ s->timer[2] = arm_timer_init(1000000, pic, irq + 2);
+
+ iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
+ icp_pit_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/cdrom.c b/hw/cdrom.c
new file mode 100644
index 0000000..a43b417
--- /dev/null
+++ b/hw/cdrom.c
@@ -0,0 +1,156 @@
+/*
+ * QEMU ATAPI CD-ROM Emulator
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
+ here. */
+
+#include <vl.h>
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+
+/* same toc as bochs. Return -1 if error or the toc length */
+/* XXX: check this */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+{
+ uint8_t *q;
+ int len;
+
+ if (start_track > 1 && start_track != 0xaa)
+ return -1;
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ if (start_track <= 1) {
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 1; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ /* sector 0 */
+ cpu_to_be32wu((uint32_t *)q, 0);
+ q += 4;
+ }
+ }
+ /* lead out track */
+ *q++ = 0; /* reserved */
+ *q++ = 0x16; /* ADR, control */
+ *q++ = 0xaa; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
+
+/* mostly same info as PearPc */
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
+{
+ uint8_t *q;
+ int len;
+
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa0; /* lead-in */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* first track */
+ *q++ = 0x00; /* disk type */
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa1;
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* last track */
+ *q++ = 0x00;
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa2; /* lead-out */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 0; /* track number */
+ *q++ = 1; /* point */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0;
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
+
+
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
new file mode 100644
index 0000000..d186d79
--- /dev/null
+++ b/hw/cirrus_vga.c
@@ -0,0 +1,3193 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * Reference: Finn Thogersons' VGADOC4b
+ * available at http://home.worldonline.dk/~finth/
+ */
+#include "vl.h"
+#include "vga_int.h"
+
+/*
+ * TODO:
+ * - destination write mask support not complete (bits 5..7)
+ * - optimize linear mappings
+ * - optimize bitblt functions
+ */
+
+//#define DEBUG_CIRRUS
+//#define DEBUG_BITBLT
+
+/***************************************
+ *
+ * definitions
+ *
+ ***************************************/
+
+#define qemu_MIN(a,b) ((a) < (b) ? (a) : (b))
+
+// ID
+#define CIRRUS_ID_CLGD5422 (0x23<<2)
+#define CIRRUS_ID_CLGD5426 (0x24<<2)
+#define CIRRUS_ID_CLGD5424 (0x25<<2)
+#define CIRRUS_ID_CLGD5428 (0x26<<2)
+#define CIRRUS_ID_CLGD5430 (0x28<<2)
+#define CIRRUS_ID_CLGD5434 (0x2A<<2)
+#define CIRRUS_ID_CLGD5436 (0x2B<<2)
+#define CIRRUS_ID_CLGD5446 (0x2E<<2)
+
+// sequencer 0x07
+#define CIRRUS_SR7_BPP_VGA 0x00
+#define CIRRUS_SR7_BPP_SVGA 0x01
+#define CIRRUS_SR7_BPP_MASK 0x0e
+#define CIRRUS_SR7_BPP_8 0x00
+#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02
+#define CIRRUS_SR7_BPP_24 0x04
+#define CIRRUS_SR7_BPP_16 0x06
+#define CIRRUS_SR7_BPP_32 0x08
+#define CIRRUS_SR7_ISAADDR_MASK 0xe0
+
+// sequencer 0x0f
+#define CIRRUS_MEMSIZE_512k 0x08
+#define CIRRUS_MEMSIZE_1M 0x10
+#define CIRRUS_MEMSIZE_2M 0x18
+#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled.
+
+// sequencer 0x12
+#define CIRRUS_CURSOR_SHOW 0x01
+#define CIRRUS_CURSOR_HIDDENPEL 0x02
+#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear
+
+// sequencer 0x17
+#define CIRRUS_BUSTYPE_VLBFAST 0x10
+#define CIRRUS_BUSTYPE_PCI 0x20
+#define CIRRUS_BUSTYPE_VLBSLOW 0x30
+#define CIRRUS_BUSTYPE_ISA 0x38
+#define CIRRUS_MMIO_ENABLE 0x04
+#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared.
+#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
+
+// control 0x0b
+#define CIRRUS_BANKING_DUAL 0x01
+#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k
+
+// control 0x30
+#define CIRRUS_BLTMODE_BACKWARDS 0x01
+#define CIRRUS_BLTMODE_MEMSYSDEST 0x02
+#define CIRRUS_BLTMODE_MEMSYSSRC 0x04
+#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08
+#define CIRRUS_BLTMODE_PATTERNCOPY 0x40
+#define CIRRUS_BLTMODE_COLOREXPAND 0x80
+#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30
+#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00
+#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10
+#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20
+#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30
+
+// control 0x31
+#define CIRRUS_BLT_BUSY 0x01
+#define CIRRUS_BLT_START 0x02
+#define CIRRUS_BLT_RESET 0x04
+#define CIRRUS_BLT_FIFOUSED 0x10
+#define CIRRUS_BLT_AUTOSTART 0x80
+
+// control 0x32
+#define CIRRUS_ROP_0 0x00
+#define CIRRUS_ROP_SRC_AND_DST 0x05
+#define CIRRUS_ROP_NOP 0x06
+#define CIRRUS_ROP_SRC_AND_NOTDST 0x09
+#define CIRRUS_ROP_NOTDST 0x0b
+#define CIRRUS_ROP_SRC 0x0d
+#define CIRRUS_ROP_1 0x0e
+#define CIRRUS_ROP_NOTSRC_AND_DST 0x50
+#define CIRRUS_ROP_SRC_XOR_DST 0x59
+#define CIRRUS_ROP_SRC_OR_DST 0x6d
+#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90
+#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95
+#define CIRRUS_ROP_SRC_OR_NOTDST 0xad
+#define CIRRUS_ROP_NOTSRC 0xd0
+#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6
+#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda
+
+#define CIRRUS_ROP_NOP_INDEX 2
+#define CIRRUS_ROP_SRC_INDEX 5
+
+// control 0x33
+#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04
+#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02
+#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01
+
+// memory-mapped IO
+#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword
+#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword
+#define CIRRUS_MMIO_BLTWIDTH 0x08 // word
+#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word
+#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word
+#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word
+#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword
+#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword
+#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte
+#define CIRRUS_MMIO_BLTMODE 0x18 // byte
+#define CIRRUS_MMIO_BLTROP 0x1a // byte
+#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word?
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word?
+#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word
+#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte
+#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word
+#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word
+#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word
+#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word
+#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte
+#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte
+#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte
+
+// PCI 0x00: vendor, 0x02: device
+#define PCI_VENDOR_CIRRUS 0x1013
+#define PCI_DEVICE_CLGD5462 0x00d0
+#define PCI_DEVICE_CLGD5465 0x00d6
+
+// PCI 0x04: command(word), 0x06(word): status
+#define PCI_COMMAND_IOACCESS 0x0001
+#define PCI_COMMAND_MEMACCESS 0x0002
+#define PCI_COMMAND_BUSMASTER 0x0004
+#define PCI_COMMAND_SPECIALCYCLE 0x0008
+#define PCI_COMMAND_MEMWRITEINVALID 0x0010
+#define PCI_COMMAND_PALETTESNOOPING 0x0020
+#define PCI_COMMAND_PARITYDETECTION 0x0040
+#define PCI_COMMAND_ADDRESSDATASTEPPING 0x0080
+#define PCI_COMMAND_SERR 0x0100
+#define PCI_COMMAND_BACKTOBACKTRANS 0x0200
+// PCI 0x08, 0xff000000 (0x09-0x0b:class,0x08:rev)
+#define PCI_CLASS_BASE_DISPLAY 0x03
+// PCI 0x08, 0x00ff0000
+#define PCI_CLASS_SUB_VGA 0x00
+// PCI 0x0c, 0x00ff0000 (0x0c:cacheline,0x0d:latency,0x0e:headertype,0x0f:Built-in self test)
+#define PCI_CLASS_HEADERTYPE_00h 0x00
+// 0x10-0x3f (headertype 00h)
+// PCI 0x10,0x14,0x18,0x1c,0x20,0x24: base address mapping registers
+// 0x10: MEMBASE, 0x14: IOBASE(hard-coded in XFree86 3.x)
+#define PCI_MAP_MEM 0x0
+#define PCI_MAP_IO 0x1
+#define PCI_MAP_MEM_ADDR_MASK (~0xf)
+#define PCI_MAP_IO_ADDR_MASK (~0x3)
+#define PCI_MAP_MEMFLAGS_32BIT 0x0
+#define PCI_MAP_MEMFLAGS_32BIT_1M 0x1
+#define PCI_MAP_MEMFLAGS_64BIT 0x4
+#define PCI_MAP_MEMFLAGS_CACHEABLE 0x8
+// PCI 0x28: cardbus CIS pointer
+// PCI 0x2c: subsystem vendor id, 0x2e: subsystem id
+// PCI 0x30: expansion ROM base address
+#define PCI_ROMBIOS_ENABLED 0x1
+// PCI 0x34: 0xffffff00=reserved, 0x000000ff=capabilities pointer
+// PCI 0x38: reserved
+// PCI 0x3c: 0x3c=int-line, 0x3d=int-pin, 0x3e=min-gnt, 0x3f=maax-lat
+
+#define CIRRUS_PNPMMIO_SIZE 0x1000
+
+
+/* I/O and memory hook */
+#define CIRRUS_HOOK_NOT_HANDLED 0
+#define CIRRUS_HOOK_HANDLED 1
+
+struct CirrusVGAState;
+typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
+ uint8_t * dst, const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight);
+typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch, int width, int height);
+
+typedef struct CirrusVGAState {
+ VGA_STATE_COMMON
+
+ int cirrus_linear_io_addr;
+ int cirrus_linear_bitblt_io_addr;
+ int cirrus_mmio_io_addr;
+ uint32_t cirrus_addr_mask;
+ uint32_t linear_mmio_mask;
+ uint8_t cirrus_shadow_gr0;
+ uint8_t cirrus_shadow_gr1;
+ uint8_t cirrus_hidden_dac_lockindex;
+ uint8_t cirrus_hidden_dac_data;
+ uint32_t cirrus_bank_base[2];
+ uint32_t cirrus_bank_limit[2];
+ uint8_t cirrus_hidden_palette[48];
+ uint32_t hw_cursor_x;
+ uint32_t hw_cursor_y;
+ int cirrus_blt_pixelwidth;
+ int cirrus_blt_width;
+ int cirrus_blt_height;
+ int cirrus_blt_dstpitch;
+ int cirrus_blt_srcpitch;
+ uint32_t cirrus_blt_fgcol;
+ uint32_t cirrus_blt_bgcol;
+ uint32_t cirrus_blt_dstaddr;
+ uint32_t cirrus_blt_srcaddr;
+ uint8_t cirrus_blt_mode;
+ uint8_t cirrus_blt_modeext;
+ cirrus_bitblt_rop_t cirrus_rop;
+#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
+ uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
+ uint8_t *cirrus_srcptr;
+ uint8_t *cirrus_srcptr_end;
+ uint32_t cirrus_srccounter;
+ /* hwcursor display state */
+ int last_hw_cursor_size;
+ int last_hw_cursor_x;
+ int last_hw_cursor_y;
+ int last_hw_cursor_y_start;
+ int last_hw_cursor_y_end;
+ int real_vram_size; /* XXX: suppress that */
+ CPUWriteMemoryFunc **cirrus_linear_write;
+} CirrusVGAState;
+
+typedef struct PCICirrusVGAState {
+ PCIDevice dev;
+ CirrusVGAState cirrus_vga;
+} PCICirrusVGAState;
+
+static uint8_t rop_to_index[256];
+
+/***************************************
+ *
+ * prototypes.
+ *
+ ***************************************/
+
+
+static void cirrus_bitblt_reset(CirrusVGAState *s);
+static void cirrus_update_memory_access(CirrusVGAState *s);
+
+/***************************************
+ *
+ * raster operations
+ *
+ ***************************************/
+
+static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+}
+
+static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
+ uint8_t *dst,
+ int dstpitch, int bltwidth,int bltheight)
+{
+}
+
+#define ROP_NAME 0
+#define ROP_OP(d, s) d = 0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_dst
+#define ROP_OP(d, s) d = (s) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_notdst
+#define ROP_OP(d, s) d = (s) & (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notdst
+#define ROP_OP(d, s) d = ~(d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src
+#define ROP_OP(d, s) d = s
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME 1
+#define ROP_OP(d, s) d = ~0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_dst
+#define ROP_OP(d, s) d = (~(s)) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_xor_dst
+#define ROP_OP(d, s) d = (s) ^ (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_dst
+#define ROP_OP(d, s) d = (s) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_notdst
+#define ROP_OP(d, s) d = (~(s)) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_notxor_dst
+#define ROP_OP(d, s) d = ~((s) ^ (d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_notdst
+#define ROP_OP(d, s) d = (s) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc
+#define ROP_OP(d, s) d = (~(s))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_dst
+#define ROP_OP(d, s) d = (~(s)) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_notdst
+#define ROP_OP(d, s) d = (~(s)) & (~(d))
+#include "cirrus_vga_rop.h"
+
+static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
+ cirrus_bitblt_rop_fwd_0,
+ cirrus_bitblt_rop_fwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_fwd_src_and_notdst,
+ cirrus_bitblt_rop_fwd_notdst,
+ cirrus_bitblt_rop_fwd_src,
+ cirrus_bitblt_rop_fwd_1,
+ cirrus_bitblt_rop_fwd_notsrc_and_dst,
+ cirrus_bitblt_rop_fwd_src_xor_dst,
+ cirrus_bitblt_rop_fwd_src_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_fwd_src_notxor_dst,
+ cirrus_bitblt_rop_fwd_src_or_notdst,
+ cirrus_bitblt_rop_fwd_notsrc,
+ cirrus_bitblt_rop_fwd_notsrc_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_and_notdst,
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
+ cirrus_bitblt_rop_bkwd_0,
+ cirrus_bitblt_rop_bkwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_bkwd_src_and_notdst,
+ cirrus_bitblt_rop_bkwd_notdst,
+ cirrus_bitblt_rop_bkwd_src,
+ cirrus_bitblt_rop_bkwd_1,
+ cirrus_bitblt_rop_bkwd_notsrc_and_dst,
+ cirrus_bitblt_rop_bkwd_src_xor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_bkwd_src_notxor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_notdst,
+ cirrus_bitblt_rop_bkwd_notsrc,
+ cirrus_bitblt_rop_bkwd_notsrc_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
+};
+
+#define ROP2(name) {\
+ name ## _8,\
+ name ## _16,\
+ name ## _24,\
+ name ## _32,\
+ }
+
+#define ROP_NOP2(func) {\
+ func,\
+ func,\
+ func,\
+ func,\
+ }
+
+static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
+ ROP2(cirrus_patternfill_0),
+ ROP2(cirrus_patternfill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_patternfill_src_and_notdst),
+ ROP2(cirrus_patternfill_notdst),
+ ROP2(cirrus_patternfill_src),
+ ROP2(cirrus_patternfill_1),
+ ROP2(cirrus_patternfill_notsrc_and_dst),
+ ROP2(cirrus_patternfill_src_xor_dst),
+ ROP2(cirrus_patternfill_src_or_dst),
+ ROP2(cirrus_patternfill_notsrc_or_notdst),
+ ROP2(cirrus_patternfill_src_notxor_dst),
+ ROP2(cirrus_patternfill_src_or_notdst),
+ ROP2(cirrus_patternfill_notsrc),
+ ROP2(cirrus_patternfill_notsrc_or_dst),
+ ROP2(cirrus_patternfill_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
+ ROP2(cirrus_colorexpand_transp_0),
+ ROP2(cirrus_colorexpand_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_transp_notdst),
+ ROP2(cirrus_colorexpand_transp_src),
+ ROP2(cirrus_colorexpand_transp_1),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_transp_notsrc),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
+ ROP2(cirrus_colorexpand_0),
+ ROP2(cirrus_colorexpand_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_src_and_notdst),
+ ROP2(cirrus_colorexpand_notdst),
+ ROP2(cirrus_colorexpand_src),
+ ROP2(cirrus_colorexpand_1),
+ ROP2(cirrus_colorexpand_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_src_xor_dst),
+ ROP2(cirrus_colorexpand_src_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_src_notxor_dst),
+ ROP2(cirrus_colorexpand_src_or_notdst),
+ ROP2(cirrus_colorexpand_notsrc),
+ ROP2(cirrus_colorexpand_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_transp_0),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src),
+ ROP2(cirrus_colorexpand_pattern_transp_1),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_0),
+ ROP2(cirrus_colorexpand_pattern_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_notdst),
+ ROP2(cirrus_colorexpand_pattern_src),
+ ROP2(cirrus_colorexpand_pattern_1),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_notsrc),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
+};
+
+static const cirrus_fill_t cirrus_fill[16][4] = {
+ ROP2(cirrus_fill_0),
+ ROP2(cirrus_fill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_fill_nop),
+ ROP2(cirrus_fill_src_and_notdst),
+ ROP2(cirrus_fill_notdst),
+ ROP2(cirrus_fill_src),
+ ROP2(cirrus_fill_1),
+ ROP2(cirrus_fill_notsrc_and_dst),
+ ROP2(cirrus_fill_src_xor_dst),
+ ROP2(cirrus_fill_src_or_dst),
+ ROP2(cirrus_fill_notsrc_or_notdst),
+ ROP2(cirrus_fill_src_notxor_dst),
+ ROP2(cirrus_fill_src_or_notdst),
+ ROP2(cirrus_fill_notsrc),
+ ROP2(cirrus_fill_notsrc_or_dst),
+ ROP2(cirrus_fill_notsrc_and_notdst),
+};
+
+static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr1 | (s->gr[0x11] << 8);
+ s->cirrus_blt_fgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
+ (s->gr[0x11] << 8) | (s->gr[0x13] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr1 | (s->gr[0x11] << 8) |
+ (s->gr[0x13] << 16) | (s->gr[0x15] << 24);
+ s->cirrus_blt_fgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr0 | (s->gr[0x10] << 8);
+ s->cirrus_blt_bgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
+ (s->gr[0x10] << 8) | (s->gr[0x12] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr0 | (s->gr[0x10] << 8) |
+ (s->gr[0x12] << 16) | (s->gr[0x14] << 24);
+ s->cirrus_blt_bgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
+ int off_pitch, int bytesperline,
+ int lines)
+{
+ int y;
+ int off_cur;
+ int off_cur_end;
+
+ for (y = 0; y < lines; y++) {
+ off_cur = off_begin;
+ off_cur_end = off_cur + bytesperline;
+ off_cur &= TARGET_PAGE_MASK;
+ while (off_cur < off_cur_end) {
+ cpu_physical_memory_set_dirty(s->vram_offset + off_cur);
+ off_cur += TARGET_PAGE_SIZE;
+ }
+ off_begin += off_pitch;
+ }
+}
+
+static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
+ const uint8_t * src)
+{
+ uint8_t *dst;
+
+ dst = s->vram_ptr + s->cirrus_blt_dstaddr;
+ (*s->cirrus_rop) (s, dst, src,
+ s->cirrus_blt_dstpitch, 0,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ return 1;
+}
+
+/* fill */
+
+static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
+{
+ cirrus_fill_t rop_func;
+
+ rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ cirrus_bitblt_reset(s);
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (video-to-video)
+ *
+ ***************************************/
+
+static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
+{
+ return cirrus_bitblt_common_patterncopy(s,
+ s->vram_ptr +
+ (s->cirrus_blt_srcaddr & ~7));
+}
+
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
+{
+ int sx, sy;
+ int dx, dy;
+ int width, height;
+ int depth;
+ int notify = 0;
+
+ depth = s->get_bpp((VGAState *)s) / 8;
+ s->get_resolution((VGAState *)s, &width, &height);
+
+ /* extra x, y */
+ sx = (src % (width * depth)) / depth;
+ sy = (src / (width * depth));
+ dx = (dst % (width *depth)) / depth;
+ dy = (dst / (width * depth));
+
+ /* normalize width */
+ w /= depth;
+
+ /* if we're doing a backward copy, we have to adjust
+ our x/y to be the upper left corner (instead of the lower
+ right corner) */
+ if (s->cirrus_blt_dstpitch < 0) {
+ sx -= (s->cirrus_blt_width / depth) - 1;
+ dx -= (s->cirrus_blt_width / depth) - 1;
+ sy -= s->cirrus_blt_height - 1;
+ dy -= s->cirrus_blt_height - 1;
+ }
+
+ /* are we in the visible portion of memory? */
+ if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+ (sx + w) <= width && (sy + h) <= height &&
+ (dx + w) <= width && (dy + h) <= height) {
+ notify = 1;
+ }
+
+ /* make to sure only copy if it's a plain copy ROP */
+ if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src &&
+ *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src)
+ notify = 0;
+
+ /* we have to flush all pending changes so that the copy
+ is generated at the appropriate moment in time */
+ if (notify)
+ vga_hw_update();
+
+ (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
+ s->vram_ptr + s->cirrus_blt_srcaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ if (notify)
+ s->ds->dpy_copy(s->ds,
+ sx, sy, dx, dy,
+ s->cirrus_blt_width / depth,
+ s->cirrus_blt_height);
+
+ /* we don't have to notify the display that this portion has
+ changed since dpy_copy implies this */
+
+ if (!notify)
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+ if (s->ds->dpy_copy) {
+ cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr,
+ s->cirrus_blt_srcaddr - s->start_addr,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ } else {
+ (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
+ s->vram_ptr + s->cirrus_blt_srcaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ }
+
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (cpu-to-video)
+ *
+ ***************************************/
+
+static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
+{
+ int copy_count;
+ uint8_t *end_ptr;
+
+ if (s->cirrus_srccounter > 0) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
+ the_end:
+ s->cirrus_srccounter = 0;
+ cirrus_bitblt_reset(s);
+ } else {
+ /* at least one scan line */
+ do {
+ (*s->cirrus_rop)(s, s->vram_ptr + s->cirrus_blt_dstaddr,
+ s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
+ s->cirrus_blt_width, 1);
+ s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch;
+ s->cirrus_srccounter -= s->cirrus_blt_srcpitch;
+ if (s->cirrus_srccounter <= 0)
+ goto the_end;
+ /* more bytes than needed can be transfered because of
+ word alignment, so we keep them for the next line */
+ /* XXX: keep alignment to speed up transfer */
+ end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ copy_count = s->cirrus_srcptr_end - end_ptr;
+ memmove(s->cirrus_bltbuf, end_ptr, copy_count);
+ s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ } while (s->cirrus_srcptr >= s->cirrus_srcptr_end);
+ }
+ }
+}
+
+/***************************************
+ *
+ * bitblt wrapper
+ *
+ ***************************************/
+
+static void cirrus_bitblt_reset(CirrusVGAState * s)
+{
+ s->gr[0x31] &=
+ ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+ s->cirrus_srccounter = 0;
+ cirrus_update_memory_access(s);
+}
+
+static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+{
+ int w;
+
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ s->cirrus_blt_srcpitch = 8;
+ } else {
+ /* XXX: check for 24 bpp */
+ s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch;
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
+ s->cirrus_blt_srcpitch = ((w + 31) >> 5);
+ else
+ s->cirrus_blt_srcpitch = ((w + 7) >> 3);
+ } else {
+ /* always align input size to 32 bits */
+ s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
+ }
+ s->cirrus_srcptr = s->cirrus_bltbuf;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ cirrus_update_memory_access(s);
+ return 1;
+}
+
+static int cirrus_bitblt_videotocpu(CirrusVGAState * s)
+{
+ /* XXX */
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt (video to cpu) is not implemented yet\n");
+#endif
+ return 0;
+}
+
+static int cirrus_bitblt_videotovideo(CirrusVGAState * s)
+{
+ int ret;
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ ret = cirrus_bitblt_videotovideo_patterncopy(s);
+ } else {
+ ret = cirrus_bitblt_videotovideo_copy(s);
+ }
+ if (ret)
+ cirrus_bitblt_reset(s);
+ return ret;
+}
+
+static void cirrus_bitblt_start(CirrusVGAState * s)
+{
+ uint8_t blt_rop;
+
+ s->gr[0x31] |= CIRRUS_BLT_BUSY;
+
+ s->cirrus_blt_width = (s->gr[0x20] | (s->gr[0x21] << 8)) + 1;
+ s->cirrus_blt_height = (s->gr[0x22] | (s->gr[0x23] << 8)) + 1;
+ s->cirrus_blt_dstpitch = (s->gr[0x24] | (s->gr[0x25] << 8));
+ s->cirrus_blt_srcpitch = (s->gr[0x26] | (s->gr[0x27] << 8));
+ s->cirrus_blt_dstaddr =
+ (s->gr[0x28] | (s->gr[0x29] << 8) | (s->gr[0x2a] << 16));
+ s->cirrus_blt_srcaddr =
+ (s->gr[0x2c] | (s->gr[0x2d] << 8) | (s->gr[0x2e] << 16));
+ s->cirrus_blt_mode = s->gr[0x30];
+ s->cirrus_blt_modeext = s->gr[0x33];
+ blt_rop = s->gr[0x32];
+
+#ifdef DEBUG_BITBLT
+ printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
+ blt_rop,
+ s->cirrus_blt_mode,
+ s->cirrus_blt_modeext,
+ s->cirrus_blt_width,
+ s->cirrus_blt_height,
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_srcpitch,
+ s->cirrus_blt_dstaddr,
+ s->cirrus_blt_srcaddr,
+ s->gr[0x2f]);
+#endif
+
+ switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
+ case CIRRUS_BLTMODE_PIXELWIDTH8:
+ s->cirrus_blt_pixelwidth = 1;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH16:
+ s->cirrus_blt_pixelwidth = 2;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH24:
+ s->cirrus_blt_pixelwidth = 3;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH32:
+ s->cirrus_blt_pixelwidth = 4;
+ break;
+ default:
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - pixel width is unknown\n");
+#endif
+ goto bitblt_ignore;
+ }
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK;
+
+ if ((s->
+ cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC |
+ CIRRUS_BLTMODE_MEMSYSDEST))
+ == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) {
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - memory-to-memory copy is requested\n");
+#endif
+ goto bitblt_ignore;
+ }
+
+ if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
+ (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
+ CIRRUS_BLTMODE_TRANSPARENTCOMP |
+ CIRRUS_BLTMODE_PATTERNCOPY |
+ CIRRUS_BLTMODE_COLOREXPAND)) ==
+ (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_solidfill(s, blt_rop);
+ } else {
+ if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
+ CIRRUS_BLTMODE_PATTERNCOPY)) ==
+ CIRRUS_BLTMODE_COLOREXPAND) {
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+ s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+ s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+ s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+ } else {
+ s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+ }
+ }
+
+ // setup bitblt engine.
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
+ if (!cirrus_bitblt_cputovideo(s))
+ goto bitblt_ignore;
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) {
+ if (!cirrus_bitblt_videotocpu(s))
+ goto bitblt_ignore;
+ } else {
+ if (!cirrus_bitblt_videotovideo(s))
+ goto bitblt_ignore;
+ }
+ }
+ return;
+ bitblt_ignore:;
+ cirrus_bitblt_reset(s);
+}
+
+static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
+{
+ unsigned old_value;
+
+ old_value = s->gr[0x31];
+ s->gr[0x31] = reg_value;
+
+ if (((old_value & CIRRUS_BLT_RESET) != 0) &&
+ ((reg_value & CIRRUS_BLT_RESET) == 0)) {
+ cirrus_bitblt_reset(s);
+ } else if (((old_value & CIRRUS_BLT_START) == 0) &&
+ ((reg_value & CIRRUS_BLT_START) != 0)) {
+ cirrus_bitblt_start(s);
+ }
+}
+
+
+/***************************************
+ *
+ * basic parameters
+ *
+ ***************************************/
+
+static void cirrus_get_offsets(VGAState *s1,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr)
+{
+ CirrusVGAState * s = (CirrusVGAState *)s1;
+ uint32_t start_addr;
+ uint32_t line_offset;
+
+ line_offset = s->cr[0x13]
+ | ((s->cr[0x1b] & 0x10) << 4);
+ line_offset <<= 3;
+ *pline_offset = line_offset;
+
+ start_addr = (s->cr[0x0c] << 8)
+ | s->cr[0x0d]
+ | ((s->cr[0x1b] & 0x01) << 16)
+ | ((s->cr[0x1b] & 0x0c) << 15)
+ | ((s->cr[0x1d] & 0x80) << 12);
+ *pstart_addr = start_addr;
+}
+
+static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
+{
+ uint32_t ret = 16;
+
+ switch (s->cirrus_hidden_dac_data & 0xf) {
+ case 0:
+ ret = 15;
+ break; /* Sierra HiColor */
+ case 1:
+ ret = 16;
+ break; /* XGA HiColor */
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: invalid DAC value %x in 16bpp\n",
+ (s->cirrus_hidden_dac_data & 0xf));
+#endif
+ ret = 15; /* XXX */
+ break;
+ }
+ return ret;
+}
+
+static int cirrus_get_bpp(VGAState *s1)
+{
+ CirrusVGAState * s = (CirrusVGAState *)s1;
+ uint32_t ret = 8;
+
+ if ((s->sr[0x07] & 0x01) != 0) {
+ /* Cirrus SVGA */
+ switch (s->sr[0x07] & CIRRUS_SR7_BPP_MASK) {
+ case CIRRUS_SR7_BPP_8:
+ ret = 8;
+ break;
+ case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_24:
+ ret = 24;
+ break;
+ case CIRRUS_SR7_BPP_16:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_32:
+ ret = 32;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: unknown bpp - sr7=%x\n", s->sr[0x7]);
+#endif
+ ret = 8;
+ break;
+ }
+ } else {
+ /* VGA */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void cirrus_get_resolution(VGAState *s, int *pwidth, int *pheight)
+{
+ int width, height;
+
+ width = (s->cr[0x01] + 1) * 8;
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1);
+ /* interlace support */
+ if (s->cr[0x1a] & 0x01)
+ height = height * 2;
+ *pwidth = width;
+ *pheight = height;
+}
+
+/***************************************
+ *
+ * bank memory
+ *
+ ***************************************/
+
+static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
+{
+ unsigned offset;
+ unsigned limit;
+
+ if ((s->gr[0x0b] & 0x01) != 0) /* dual bank */
+ offset = s->gr[0x09 + bank_index];
+ else /* single bank */
+ offset = s->gr[0x09];
+
+ if ((s->gr[0x0b] & 0x20) != 0)
+ offset <<= 14;
+ else
+ offset <<= 12;
+
+ if (s->real_vram_size <= offset)
+ limit = 0;
+ else
+ limit = s->real_vram_size - offset;
+
+ if (((s->gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
+ if (limit > 0x8000) {
+ offset += 0x8000;
+ limit -= 0x8000;
+ } else {
+ limit = 0;
+ }
+ }
+
+ if (limit > 0) {
+ s->cirrus_bank_base[bank_index] = offset;
+ s->cirrus_bank_limit[bank_index] = limit;
+ } else {
+ s->cirrus_bank_base[bank_index] = 0;
+ s->cirrus_bank_limit[bank_index] = 0;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3c4-0x3c5
+ *
+ ***************************************/
+
+static int
+cirrus_hook_read_sr(CirrusVGAState * s, unsigned reg_index, int *reg_value)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x06: // Unlock Cirrus extensions
+ *reg_value = s->sr[reg_index];
+ break;
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ *reg_value = s->sr[0x10];
+ break;
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ *reg_value = s->sr[0x11];
+ break;
+ case 0x05: // ???
+ case 0x07: // Extended Sequencer Mode
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x17: // Configuration Readback and Extended Control
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signal Generator Result
+ case 0x1a: // Signal Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled inport sr_index %02x\n", reg_index);
+#endif
+ *reg_value = s->sr[reg_index];
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport sr_index %02x\n", reg_index);
+#endif
+ *reg_value = 0xff;
+ break;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+static int
+cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x06: // Unlock Cirrus extensions
+ reg_value &= 0x17;
+ if (reg_value == 0x12) {
+ s->sr[reg_index] = 0x12;
+ } else {
+ s->sr[reg_index] = 0x0f;
+ }
+ break;
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ s->sr[0x10] = reg_value;
+ s->hw_cursor_x = (reg_value << 3) | (reg_index >> 5);
+ break;
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ s->sr[0x11] = reg_value;
+ s->hw_cursor_y = (reg_value << 3) | (reg_index >> 5);
+ break;
+ case 0x07: // Extended Sequencer Mode
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signature Generator Result
+ case 0x1a: // Signature Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+ s->sr[reg_index] = reg_value;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
+ reg_index, reg_value);
+#endif
+ break;
+ case 0x17: // Configuration Readback and Extended Control
+ s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7);
+ cirrus_update_memory_access(s);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport sr_index %02x, sr_value %02x\n", reg_index,
+ reg_value);
+#endif
+ break;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c6
+ *
+ ***************************************/
+
+static void cirrus_read_hidden_dac(CirrusVGAState * s, int *reg_value)
+{
+ *reg_value = 0xff;
+ if (++s->cirrus_hidden_dac_lockindex == 5) {
+ *reg_value = s->cirrus_hidden_dac_data;
+ s->cirrus_hidden_dac_lockindex = 0;
+ }
+}
+
+static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value)
+{
+ if (s->cirrus_hidden_dac_lockindex == 4) {
+ s->cirrus_hidden_dac_data = reg_value;
+#if defined(DEBUG_CIRRUS)
+ printf("cirrus: outport hidden DAC, value %02x\n", reg_value);
+#endif
+ }
+ s->cirrus_hidden_dac_lockindex = 0;
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c9
+ *
+ ***************************************/
+
+static int cirrus_hook_read_palette(CirrusVGAState * s, int *reg_value)
+{
+ if (!(s->sr[0x12] & CIRRUS_CURSOR_HIDDENPEL))
+ return CIRRUS_HOOK_NOT_HANDLED;
+ *reg_value =
+ s->cirrus_hidden_palette[(s->dac_read_index & 0x0f) * 3 +
+ s->dac_sub_index];
+ if (++s->dac_sub_index == 3) {
+ s->dac_sub_index = 0;
+ s->dac_read_index++;
+ }
+ return CIRRUS_HOOK_HANDLED;
+}
+
+static int cirrus_hook_write_palette(CirrusVGAState * s, int reg_value)
+{
+ if (!(s->sr[0x12] & CIRRUS_CURSOR_HIDDENPEL))
+ return CIRRUS_HOOK_NOT_HANDLED;
+ s->dac_cache[s->dac_sub_index] = reg_value;
+ if (++s->dac_sub_index == 3) {
+ memcpy(&s->cirrus_hidden_palette[(s->dac_write_index & 0x0f) * 3],
+ s->dac_cache, 3);
+ /* XXX update cursor */
+ s->dac_sub_index = 0;
+ s->dac_write_index++;
+ }
+ return CIRRUS_HOOK_HANDLED;
+}
+
+/***************************************
+ *
+ * I/O access between 0x3ce-0x3cf
+ *
+ ***************************************/
+
+static int
+cirrus_hook_read_gr(CirrusVGAState * s, unsigned reg_index, int *reg_value)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ *reg_value = s->cirrus_shadow_gr0;
+ return CIRRUS_HOOK_HANDLED;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ *reg_value = s->cirrus_shadow_gr1;
+ return CIRRUS_HOOK_HANDLED;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x05: // Standard VGA, Cirrus extended mode
+ default:
+ break;
+ }
+
+ if (reg_index < 0x3a) {
+ *reg_value = s->gr[reg_index];
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport gr_index %02x\n", reg_index);
+#endif
+ *reg_value = 0xff;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+static int
+cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+#if defined(DEBUG_BITBLT) && 0
+ printf("gr%02x: %02x\n", reg_index, reg_value);
+#endif
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ s->cirrus_shadow_gr0 = reg_value;
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ s->cirrus_shadow_gr1 = reg_value;
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x05: // Standard VGA, Cirrus extended mode
+ s->gr[reg_index] = reg_value & 0x7f;
+ cirrus_update_memory_access(s);
+ break;
+ case 0x09: // bank offset #0
+ case 0x0A: // bank offset #1
+ s->gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ break;
+ case 0x0B:
+ s->gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
+ break;
+ case 0x10: // BGCOLOR 0x0000ff00
+ case 0x11: // FGCOLOR 0x0000ff00
+ case 0x12: // BGCOLOR 0x00ff0000
+ case 0x13: // FGCOLOR 0x00ff0000
+ case 0x14: // BGCOLOR 0xff000000
+ case 0x15: // FGCOLOR 0xff000000
+ case 0x20: // BLT WIDTH 0x0000ff
+ case 0x22: // BLT HEIGHT 0x0000ff
+ case 0x24: // BLT DEST PITCH 0x0000ff
+ case 0x26: // BLT SRC PITCH 0x0000ff
+ case 0x28: // BLT DEST ADDR 0x0000ff
+ case 0x29: // BLT DEST ADDR 0x00ff00
+ case 0x2c: // BLT SRC ADDR 0x0000ff
+ case 0x2d: // BLT SRC ADDR 0x00ff00
+ case 0x2f: // BLT WRITEMASK
+ case 0x30: // BLT MODE
+ case 0x32: // RASTER OP
+ case 0x33: // BLT MODEEXT
+ case 0x34: // BLT TRANSPARENT COLOR 0x00ff
+ case 0x35: // BLT TRANSPARENT COLOR 0xff00
+ case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff
+ case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00
+ s->gr[reg_index] = reg_value;
+ break;
+ case 0x21: // BLT WIDTH 0x001f00
+ case 0x23: // BLT HEIGHT 0x001f00
+ case 0x25: // BLT DEST PITCH 0x001f00
+ case 0x27: // BLT SRC PITCH 0x001f00
+ s->gr[reg_index] = reg_value & 0x1f;
+ break;
+ case 0x2a: // BLT DEST ADDR 0x3f0000
+ s->gr[reg_index] = reg_value & 0x3f;
+ /* if auto start mode, starts bit blt now */
+ if (s->gr[0x31] & CIRRUS_BLT_AUTOSTART) {
+ cirrus_bitblt_start(s);
+ }
+ break;
+ case 0x2e: // BLT SRC ADDR 0x3f0000
+ s->gr[reg_index] = reg_value & 0x3f;
+ break;
+ case 0x31: // BLT STATUS/START
+ cirrus_write_bitblt(s, reg_value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index,
+ reg_value);
+#endif
+ break;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+/***************************************
+ *
+ * I/O access between 0x3d4-0x3d5
+ *
+ ***************************************/
+
+static int
+cirrus_hook_read_cr(CirrusVGAState * s, unsigned reg_index, int *reg_value)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ case 0x25: // Part Status
+ case 0x27: // Part ID (R)
+ *reg_value = s->cr[reg_index];
+ break;
+ case 0x26: // Attribute Controller Index Readback (R)
+ *reg_value = s->ar_index & 0x3f;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport cr_index %02x\n", reg_index);
+ *reg_value = 0xff;
+#endif
+ break;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+static int
+cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ return CIRRUS_HOOK_NOT_HANDLED;
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ s->cr[reg_index] = reg_value;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
+ reg_index, reg_value);
+#endif
+ break;
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ case 0x26: // Attribute Controller Index Readback (R)
+ case 0x27: // Part ID (R)
+ break;
+ case 0x25: // Part Status
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport cr_index %02x, cr_value %02x\n", reg_index,
+ reg_value);
+#endif
+ break;
+ }
+
+ return CIRRUS_HOOK_HANDLED;
+}
+
+/***************************************
+ *
+ * memory-mapped I/O (bitblt)
+ *
+ ***************************************/
+
+static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
+{
+ int value = 0xff;
+
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ cirrus_hook_read_gr(s, 0x00, &value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ cirrus_hook_read_gr(s, 0x10, &value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ cirrus_hook_read_gr(s, 0x12, &value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ cirrus_hook_read_gr(s, 0x14, &value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ cirrus_hook_read_gr(s, 0x01, &value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ cirrus_hook_read_gr(s, 0x11, &value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ cirrus_hook_read_gr(s, 0x13, &value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ cirrus_hook_read_gr(s, 0x15, &value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ cirrus_hook_read_gr(s, 0x20, &value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ cirrus_hook_read_gr(s, 0x21, &value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ cirrus_hook_read_gr(s, 0x22, &value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ cirrus_hook_read_gr(s, 0x23, &value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ cirrus_hook_read_gr(s, 0x24, &value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ cirrus_hook_read_gr(s, 0x25, &value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ cirrus_hook_read_gr(s, 0x26, &value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ cirrus_hook_read_gr(s, 0x27, &value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ cirrus_hook_read_gr(s, 0x28, &value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ cirrus_hook_read_gr(s, 0x29, &value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ cirrus_hook_read_gr(s, 0x2a, &value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ cirrus_hook_read_gr(s, 0x2c, &value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ cirrus_hook_read_gr(s, 0x2d, &value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ cirrus_hook_read_gr(s, 0x2e, &value);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ cirrus_hook_read_gr(s, 0x2f, &value);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ cirrus_hook_read_gr(s, 0x30, &value);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ cirrus_hook_read_gr(s, 0x32, &value);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ cirrus_hook_read_gr(s, 0x33, &value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ cirrus_hook_read_gr(s, 0x34, &value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ cirrus_hook_read_gr(s, 0x35, &value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ cirrus_hook_read_gr(s, 0x38, &value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ cirrus_hook_read_gr(s, 0x39, &value);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ cirrus_hook_read_gr(s, 0x31, &value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio read - address 0x%04x\n", address);
+#endif
+ break;
+ }
+
+ return (uint8_t) value;
+}
+
+static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address,
+ uint8_t value)
+{
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ cirrus_hook_write_gr(s, 0x00, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ cirrus_hook_write_gr(s, 0x10, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ cirrus_hook_write_gr(s, 0x12, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ cirrus_hook_write_gr(s, 0x14, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ cirrus_hook_write_gr(s, 0x01, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ cirrus_hook_write_gr(s, 0x11, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ cirrus_hook_write_gr(s, 0x13, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ cirrus_hook_write_gr(s, 0x15, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ cirrus_hook_write_gr(s, 0x20, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ cirrus_hook_write_gr(s, 0x21, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ cirrus_hook_write_gr(s, 0x22, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ cirrus_hook_write_gr(s, 0x23, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ cirrus_hook_write_gr(s, 0x24, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ cirrus_hook_write_gr(s, 0x25, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ cirrus_hook_write_gr(s, 0x26, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ cirrus_hook_write_gr(s, 0x27, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ cirrus_hook_write_gr(s, 0x28, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ cirrus_hook_write_gr(s, 0x29, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ cirrus_hook_write_gr(s, 0x2a, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 3):
+ /* ignored */
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ cirrus_hook_write_gr(s, 0x2c, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ cirrus_hook_write_gr(s, 0x2d, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ cirrus_hook_write_gr(s, 0x2e, value);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ cirrus_hook_write_gr(s, 0x2f, value);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ cirrus_hook_write_gr(s, 0x30, value);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ cirrus_hook_write_gr(s, 0x32, value);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ cirrus_hook_write_gr(s, 0x33, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ cirrus_hook_write_gr(s, 0x34, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ cirrus_hook_write_gr(s, 0x35, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ cirrus_hook_write_gr(s, 0x38, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ cirrus_hook_write_gr(s, 0x39, value);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ cirrus_hook_write_gr(s, 0x31, value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n",
+ address, value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * write mode 4/5
+ *
+ * assume TARGET_PAGE_SIZE >= 16
+ *
+ ***************************************/
+
+static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vram_ptr + offset;
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ }
+ val <<= 1;
+ dst++;
+ }
+ cpu_physical_memory_set_dirty(s->vram_offset + offset);
+ cpu_physical_memory_set_dirty(s->vram_offset + offset + 7);
+}
+
+static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vram_ptr + offset;
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ *(dst + 1) = s->gr[0x11];
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ *(dst + 1) = s->gr[0x10];
+ }
+ val <<= 1;
+ dst += 2;
+ }
+ cpu_physical_memory_set_dirty(s->vram_offset + offset);
+ cpu_physical_memory_set_dirty(s->vram_offset + offset + 15);
+}
+
+/***************************************
+ *
+ * memory access between 0xa0000-0xbffff
+ *
+ ***************************************/
+
+static uint32_t cirrus_vga_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ uint32_t val;
+
+ if ((s->sr[0x07] & 0x01) == 0) {
+ return vga_mem_readb(s, addr);
+ }
+
+ addr &= 0x1ffff;
+
+ if (addr < 0x10000) {
+ /* XXX handle bitblt */
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ val = *(s->vram_ptr + bank_offset);
+ } else
+ val = 0xff;
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ val = 0xff;
+ if ((s->sr[0x17] & 0x44) == 0x04) {
+ val = cirrus_mmio_blt_read(s, addr & 0xff);
+ }
+ } else {
+ val = 0xff;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_readb %06x\n", addr);
+#endif
+ }
+ return val;
+}
+
+static uint32_t cirrus_vga_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_vga_mem_readb(opaque, addr) << 8;
+ v |= cirrus_vga_mem_readb(opaque, addr + 1);
+#else
+ v = cirrus_vga_mem_readb(opaque, addr);
+ v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_vga_mem_readb(opaque, addr) << 24;
+ v |= cirrus_vga_mem_readb(opaque, addr + 1) << 16;
+ v |= cirrus_vga_mem_readb(opaque, addr + 2) << 8;
+ v |= cirrus_vga_mem_readb(opaque, addr + 3);
+#else
+ v = cirrus_vga_mem_readb(opaque, addr);
+ v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8;
+ v |= cirrus_vga_mem_readb(opaque, addr + 2) << 16;
+ v |= cirrus_vga_mem_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ unsigned mode;
+
+ if ((s->sr[0x07] & 0x01) == 0) {
+ vga_mem_writeb(s, addr, mem_value);
+ return;
+ }
+
+ addr &= 0x1ffff;
+
+ if (addr < 0x10000) {
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) mem_value;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ mode = s->gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+ *(s->vram_ptr + bank_offset) = mem_value;
+ cpu_physical_memory_set_dirty(s->vram_offset +
+ bank_offset);
+ } else {
+ if ((s->gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode,
+ bank_offset,
+ mem_value);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode,
+ bank_offset,
+ mem_value);
+ }
+ }
+ }
+ }
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ if ((s->sr[0x17] & 0x44) == 0x04) {
+ cirrus_mmio_blt_write(s, addr & 0xff, mem_value);
+ }
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_writeb %06x value %02x\n", addr, mem_value);
+#endif
+ }
+}
+
+static void cirrus_vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, val & 0xff);
+#else
+ cirrus_vga_mem_writeb(opaque, addr, val & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void cirrus_vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 3, val & 0xff);
+#else
+ cirrus_vga_mem_writeb(opaque, addr, val & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+static CPUReadMemoryFunc *cirrus_vga_mem_read[3] = {
+ cirrus_vga_mem_readb,
+ cirrus_vga_mem_readw,
+ cirrus_vga_mem_readl,
+};
+
+static CPUWriteMemoryFunc *cirrus_vga_mem_write[3] = {
+ cirrus_vga_mem_writeb,
+ cirrus_vga_mem_writew,
+ cirrus_vga_mem_writel,
+};
+
+/***************************************
+ *
+ * hardware cursor
+ *
+ ***************************************/
+
+static inline void invalidate_cursor1(CirrusVGAState *s)
+{
+ if (s->last_hw_cursor_size) {
+ vga_invalidate_scanlines((VGAState *)s,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_start,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_end);
+ }
+}
+
+static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
+{
+ const uint8_t *src;
+ uint32_t content;
+ int y, y_min, y_max;
+
+ src = s->vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->sr[0x13] & 0x3c) * 256;
+ y_min = 64;
+ y_max = -1;
+ for(y = 0; y < 64; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 16;
+ }
+ } else {
+ src += (s->sr[0x13] & 0x3f) * 256;
+ y_min = 32;
+ y_max = -1;
+ for(y = 0; y < 32; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 4;
+ }
+ }
+ if (y_min > y_max) {
+ s->last_hw_cursor_y_start = 0;
+ s->last_hw_cursor_y_end = 0;
+ } else {
+ s->last_hw_cursor_y_start = y_min;
+ s->last_hw_cursor_y_end = y_max + 1;
+ }
+}
+
+/* NOTE: we do not currently handle the cursor bitmap change, so we
+ update the cursor only if it moves. */
+static void cirrus_cursor_invalidate(VGAState *s1)
+{
+ CirrusVGAState *s = (CirrusVGAState *)s1;
+ int size;
+
+ if (!s->sr[0x12] & CIRRUS_CURSOR_SHOW) {
+ size = 0;
+ } else {
+ if (s->sr[0x12] & CIRRUS_CURSOR_LARGE)
+ size = 64;
+ else
+ size = 32;
+ }
+ /* invalidate last cursor and new cursor if any change */
+ if (s->last_hw_cursor_size != size ||
+ s->last_hw_cursor_x != s->hw_cursor_x ||
+ s->last_hw_cursor_y != s->hw_cursor_y) {
+
+ invalidate_cursor1(s);
+
+ s->last_hw_cursor_size = size;
+ s->last_hw_cursor_x = s->hw_cursor_x;
+ s->last_hw_cursor_y = s->hw_cursor_y;
+ /* compute the real cursor min and max y */
+ cirrus_cursor_compute_yrange(s);
+ invalidate_cursor1(s);
+ }
+}
+
+static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y)
+{
+ CirrusVGAState *s = (CirrusVGAState *)s1;
+ int w, h, bpp, x1, x2, poffset;
+ unsigned int color0, color1;
+ const uint8_t *palette, *src;
+ uint32_t content;
+
+ if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW))
+ return;
+ /* fast test to see if the cursor intersects with the scan line */
+ if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ h = 64;
+ } else {
+ h = 32;
+ }
+ if (scr_y < s->hw_cursor_y ||
+ scr_y >= (s->hw_cursor_y + h))
+ return;
+
+ src = s->vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->sr[0x13] & 0x3c) * 256;
+ src += (scr_y - s->hw_cursor_y) * 16;
+ poffset = 8;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ } else {
+ src += (s->sr[0x13] & 0x3f) * 256;
+ src += (scr_y - s->hw_cursor_y) * 4;
+ poffset = 128;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ }
+ /* if nothing to draw, no need to continue */
+ if (!content)
+ return;
+ w = h;
+
+ x1 = s->hw_cursor_x;
+ if (x1 >= s->last_scr_width)
+ return;
+ x2 = s->hw_cursor_x + w;
+ if (x2 > s->last_scr_width)
+ x2 = s->last_scr_width;
+ w = x2 - x1;
+ palette = s->cirrus_hidden_palette;
+ color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
+ c6_to_8(palette[0x0 * 3 + 1]),
+ c6_to_8(palette[0x0 * 3 + 2]));
+ color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]),
+ c6_to_8(palette[0xf * 3 + 1]),
+ c6_to_8(palette[0xf * 3 + 2]));
+ bpp = ((s->ds->depth + 7) >> 3);
+ d1 += x1 * bpp;
+ switch(s->ds->depth) {
+ default:
+ break;
+ case 8:
+ vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff);
+ break;
+ case 15:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff);
+ break;
+ case 16:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff);
+ break;
+ case 32:
+ vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff);
+ break;
+ }
+}
+
+/***************************************
+ *
+ * LFB memory access
+ *
+ ***************************************/
+
+static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+ uint32_t ret;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ ret = cirrus_mmio_blt_read(s, addr & 0xff);
+ } else if (0) {
+ /* XXX handle bitblt */
+ ret = 0xff;
+ } else {
+ /* video memory */
+ if ((s->gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+ ret = *(s->vram_ptr + addr);
+ }
+
+ return ret;
+}
+
+static uint32_t cirrus_linear_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_linear_readb(opaque, addr) << 8;
+ v |= cirrus_linear_readb(opaque, addr + 1);
+#else
+ v = cirrus_linear_readb(opaque, addr);
+ v |= cirrus_linear_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t cirrus_linear_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_linear_readb(opaque, addr) << 24;
+ v |= cirrus_linear_readb(opaque, addr + 1) << 16;
+ v |= cirrus_linear_readb(opaque, addr + 2) << 8;
+ v |= cirrus_linear_readb(opaque, addr + 3);
+#else
+ v = cirrus_linear_readb(opaque, addr);
+ v |= cirrus_linear_readb(opaque, addr + 1) << 8;
+ v |= cirrus_linear_readb(opaque, addr + 2) << 16;
+ v |= cirrus_linear_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+static void cirrus_linear_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+ unsigned mode;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ cirrus_mmio_blt_write(s, addr & 0xff, val);
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ if ((s->gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+
+ mode = s->gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+ *(s->vram_ptr + addr) = (uint8_t) val;
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+ } else {
+ if ((s->gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val);
+ }
+ }
+ }
+}
+
+static void cirrus_linear_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_linear_writeb(opaque, addr, (val >> 8) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, val & 0xff);
+#else
+ cirrus_linear_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void cirrus_linear_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_linear_writeb(opaque, addr, (val >> 24) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 3, val & 0xff);
+#else
+ cirrus_linear_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+
+static CPUReadMemoryFunc *cirrus_linear_read[3] = {
+ cirrus_linear_readb,
+ cirrus_linear_readw,
+ cirrus_linear_readl,
+};
+
+static CPUWriteMemoryFunc *cirrus_linear_write[3] = {
+ cirrus_linear_writeb,
+ cirrus_linear_writew,
+ cirrus_linear_writel,
+};
+
+static void cirrus_linear_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ addr &= s->cirrus_addr_mask;
+ *(s->vram_ptr + addr) = val;
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
+static void cirrus_linear_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ addr &= s->cirrus_addr_mask;
+ cpu_to_le16w((uint16_t *)(s->vram_ptr + addr), val);
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
+static void cirrus_linear_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ addr &= s->cirrus_addr_mask;
+ cpu_to_le32w((uint32_t *)(s->vram_ptr + addr), val);
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
+/***************************************
+ *
+ * system to screen memory access
+ *
+ ***************************************/
+
+
+static uint32_t cirrus_linear_bitblt_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ /* XXX handle bitblt */
+ ret = 0xff;
+ return ret;
+}
+
+static uint32_t cirrus_linear_bitblt_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_linear_bitblt_readb(opaque, addr) << 8;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1);
+#else
+ v = cirrus_linear_bitblt_readb(opaque, addr);
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t cirrus_linear_bitblt_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_linear_bitblt_readb(opaque, addr) << 24;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 16;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 8;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 3);
+#else
+ v = cirrus_linear_bitblt_readb(opaque, addr);
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 16;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+static void cirrus_linear_bitblt_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ }
+}
+
+static void cirrus_linear_bitblt_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_linear_bitblt_writeb(opaque, addr, (val >> 8) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, val & 0xff);
+#else
+ cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void cirrus_linear_bitblt_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_linear_bitblt_writeb(opaque, addr, (val >> 24) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 3, val & 0xff);
+#else
+ cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+
+static CPUReadMemoryFunc *cirrus_linear_bitblt_read[3] = {
+ cirrus_linear_bitblt_readb,
+ cirrus_linear_bitblt_readw,
+ cirrus_linear_bitblt_readl,
+};
+
+static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = {
+ cirrus_linear_bitblt_writeb,
+ cirrus_linear_bitblt_writew,
+ cirrus_linear_bitblt_writel,
+};
+
+/* Compute the memory access functions */
+static void cirrus_update_memory_access(CirrusVGAState *s)
+{
+ unsigned mode;
+
+ if ((s->sr[0x17] & 0x44) == 0x44) {
+ goto generic_io;
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ goto generic_io;
+ } else {
+ if ((s->gr[0x0B] & 0x14) == 0x14) {
+ goto generic_io;
+ } else if (s->gr[0x0B] & 0x02) {
+ goto generic_io;
+ }
+
+ mode = s->gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+ s->cirrus_linear_write[0] = cirrus_linear_mem_writeb;
+ s->cirrus_linear_write[1] = cirrus_linear_mem_writew;
+ s->cirrus_linear_write[2] = cirrus_linear_mem_writel;
+ } else {
+ generic_io:
+ s->cirrus_linear_write[0] = cirrus_linear_writeb;
+ s->cirrus_linear_write[1] = cirrus_linear_writew;
+ s->cirrus_linear_write[2] = cirrus_linear_writel;
+ }
+ }
+}
+
+
+/* I/O ports */
+
+static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
+{
+ CirrusVGAState *s = opaque;
+ int val, index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION))
+ || (addr >= 0x3d0 && addr <= 0x3df
+ && !(s->msr & MSR_COLOR_EMULATION))) {
+ val = 0xff;
+ } else {
+ switch (addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ if (cirrus_hook_read_sr(s, s->sr_index, &val))
+ break;
+ val = s->sr[s->sr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c6:
+ cirrus_read_hidden_dac(s, &val);
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ s->cirrus_hidden_dac_lockindex = 0;
+ break;
+ case 0x3c9:
+ if (cirrus_hook_read_palette(s, &val))
+ break;
+ val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
+ if (++s->dac_sub_index == 3) {
+ s->dac_sub_index = 0;
+ s->dac_read_index++;
+ }
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ if (cirrus_hook_read_gr(s, s->gr_index, &val))
+ break;
+ val = s->gr[s->gr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ if (cirrus_hook_read_cr(s, s->cr_index, &val))
+ break;
+ val = s->cr[s->cr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* just toggle to fool polling */
+ s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
+ val = s->st01;
+ s->ar_flip_flop = 0;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ }
+#if defined(DEBUG_VGA)
+ printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ CirrusVGAState *s = opaque;
+ int index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION))
+ || (addr >= 0x3d0 && addr <= 0x3df
+ && !(s->msr & MSR_COLOR_EMULATION)))
+ return;
+
+#ifdef DEBUG_VGA
+ printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+ switch (addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch (index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ break;
+ case 0x3c4:
+ s->sr_index = val;
+ break;
+ case 0x3c5:
+ if (cirrus_hook_write_sr(s, s->sr_index, val))
+ break;
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ break;
+ case 0x3c6:
+ cirrus_write_hidden_dac(s, val);
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ if (cirrus_hook_write_palette(s, val))
+ break;
+ s->dac_cache[s->dac_sub_index] = val;
+ if (++s->dac_sub_index == 3) {
+ memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
+ s->dac_sub_index = 0;
+ s->dac_write_index++;
+ }
+ break;
+ case 0x3ce:
+ s->gr_index = val;
+ break;
+ case 0x3cf:
+ if (cirrus_hook_write_gr(s, s->gr_index, val))
+ break;
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ if (cirrus_hook_write_cr(s, s->cr_index, val))
+ break;
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ /* handle CR0-7 protection */
+ if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->cr_index == 7)
+ s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
+ return;
+ }
+ switch (s->cr_index) {
+ case 0x01: /* horizontal display end */
+ case 0x07:
+ case 0x09:
+ case 0x0c:
+ case 0x0d:
+ case 0x12: /* veritcal display end */
+ s->cr[s->cr_index] = val;
+ break;
+
+ default:
+ s->cr[s->cr_index] = val;
+ break;
+ }
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+/***************************************
+ *
+ * memory-mapped I/O access
+ *
+ ***************************************/
+
+static uint32_t cirrus_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ addr &= CIRRUS_PNPMMIO_SIZE - 1;
+
+ if (addr >= 0x100) {
+ return cirrus_mmio_blt_read(s, addr - 0x100);
+ } else {
+ return vga_ioport_read(s, addr + 0x3c0);
+ }
+}
+
+static uint32_t cirrus_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_mmio_readb(opaque, addr) << 8;
+ v |= cirrus_mmio_readb(opaque, addr + 1);
+#else
+ v = cirrus_mmio_readb(opaque, addr);
+ v |= cirrus_mmio_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t cirrus_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = cirrus_mmio_readb(opaque, addr) << 24;
+ v |= cirrus_mmio_readb(opaque, addr + 1) << 16;
+ v |= cirrus_mmio_readb(opaque, addr + 2) << 8;
+ v |= cirrus_mmio_readb(opaque, addr + 3);
+#else
+ v = cirrus_mmio_readb(opaque, addr);
+ v |= cirrus_mmio_readb(opaque, addr + 1) << 8;
+ v |= cirrus_mmio_readb(opaque, addr + 2) << 16;
+ v |= cirrus_mmio_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+static void cirrus_mmio_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+ addr &= CIRRUS_PNPMMIO_SIZE - 1;
+
+ if (addr >= 0x100) {
+ cirrus_mmio_blt_write(s, addr - 0x100, val);
+ } else {
+ vga_ioport_write(s, addr + 0x3c0, val);
+ }
+}
+
+static void cirrus_mmio_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_mmio_writeb(opaque, addr, (val >> 8) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, val & 0xff);
+#else
+ cirrus_mmio_writeb(opaque, addr, val & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void cirrus_mmio_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ cirrus_mmio_writeb(opaque, addr, (val >> 24) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 3, val & 0xff);
+#else
+ cirrus_mmio_writeb(opaque, addr, val & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+
+static CPUReadMemoryFunc *cirrus_mmio_read[3] = {
+ cirrus_mmio_readb,
+ cirrus_mmio_readw,
+ cirrus_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *cirrus_mmio_write[3] = {
+ cirrus_mmio_writeb,
+ cirrus_mmio_writew,
+ cirrus_mmio_writel,
+};
+
+/* load/save state */
+
+static void cirrus_vga_save(QEMUFile *f, void *opaque)
+{
+ CirrusVGAState *s = opaque;
+
+ qemu_put_be32s(f, &s->latch);
+ qemu_put_8s(f, &s->sr_index);
+ qemu_put_buffer(f, s->sr, 256);
+ qemu_put_8s(f, &s->gr_index);
+ qemu_put_8s(f, &s->cirrus_shadow_gr0);
+ qemu_put_8s(f, &s->cirrus_shadow_gr1);
+ qemu_put_buffer(f, s->gr + 2, 254);
+ qemu_put_8s(f, &s->ar_index);
+ qemu_put_buffer(f, s->ar, 21);
+ qemu_put_be32s(f, &s->ar_flip_flop);
+ qemu_put_8s(f, &s->cr_index);
+ qemu_put_buffer(f, s->cr, 256);
+ qemu_put_8s(f, &s->msr);
+ qemu_put_8s(f, &s->fcr);
+ qemu_put_8s(f, &s->st00);
+ qemu_put_8s(f, &s->st01);
+
+ qemu_put_8s(f, &s->dac_state);
+ qemu_put_8s(f, &s->dac_sub_index);
+ qemu_put_8s(f, &s->dac_read_index);
+ qemu_put_8s(f, &s->dac_write_index);
+ qemu_put_buffer(f, s->dac_cache, 3);
+ qemu_put_buffer(f, s->palette, 768);
+
+ qemu_put_be32s(f, &s->bank_offset);
+
+ qemu_put_8s(f, &s->cirrus_hidden_dac_lockindex);
+ qemu_put_8s(f, &s->cirrus_hidden_dac_data);
+
+ qemu_put_be32s(f, &s->hw_cursor_x);
+ qemu_put_be32s(f, &s->hw_cursor_y);
+ /* XXX: we do not save the bitblt state - we assume we do not save
+ the state when the blitter is active */
+}
+
+static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CirrusVGAState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->latch);
+ qemu_get_8s(f, &s->sr_index);
+ qemu_get_buffer(f, s->sr, 256);
+ qemu_get_8s(f, &s->gr_index);
+ qemu_get_8s(f, &s->cirrus_shadow_gr0);
+ qemu_get_8s(f, &s->cirrus_shadow_gr1);
+ s->gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
+ s->gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
+ qemu_get_buffer(f, s->gr + 2, 254);
+ qemu_get_8s(f, &s->ar_index);
+ qemu_get_buffer(f, s->ar, 21);
+ qemu_get_be32s(f, &s->ar_flip_flop);
+ qemu_get_8s(f, &s->cr_index);
+ qemu_get_buffer(f, s->cr, 256);
+ qemu_get_8s(f, &s->msr);
+ qemu_get_8s(f, &s->fcr);
+ qemu_get_8s(f, &s->st00);
+ qemu_get_8s(f, &s->st01);
+
+ qemu_get_8s(f, &s->dac_state);
+ qemu_get_8s(f, &s->dac_sub_index);
+ qemu_get_8s(f, &s->dac_read_index);
+ qemu_get_8s(f, &s->dac_write_index);
+ qemu_get_buffer(f, s->dac_cache, 3);
+ qemu_get_buffer(f, s->palette, 768);
+
+ qemu_get_be32s(f, &s->bank_offset);
+
+ qemu_get_8s(f, &s->cirrus_hidden_dac_lockindex);
+ qemu_get_8s(f, &s->cirrus_hidden_dac_data);
+
+ qemu_get_be32s(f, &s->hw_cursor_x);
+ qemu_get_be32s(f, &s->hw_cursor_y);
+
+ /* force refresh */
+ s->graphic_mode = -1;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ return 0;
+}
+
+/***************************************
+ *
+ * initialize
+ *
+ ***************************************/
+
+static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
+{
+ int vga_io_memory, i;
+ static int inited;
+
+ if (!inited) {
+ inited = 1;
+ for(i = 0;i < 256; i++)
+ rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
+ rop_to_index[CIRRUS_ROP_0] = 0;
+ rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
+ rop_to_index[CIRRUS_ROP_NOP] = 2;
+ rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
+ rop_to_index[CIRRUS_ROP_NOTDST] = 4;
+ rop_to_index[CIRRUS_ROP_SRC] = 5;
+ rop_to_index[CIRRUS_ROP_1] = 6;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
+ rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
+ rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
+ rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
+ rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
+ rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
+ }
+
+ register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
+
+ register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
+ register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
+
+ register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
+
+ register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
+ register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
+
+ vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read,
+ cirrus_vga_mem_write, s);
+ cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+ vga_io_memory);
+
+ s->sr[0x06] = 0x0f;
+ if (device_id == CIRRUS_ID_CLGD5446) {
+ /* 4MB 64 bit memory config, always PCI */
+ s->sr[0x1F] = 0x2d; // MemClock
+ s->gr[0x18] = 0x0f; // fastest memory configuration
+#if 1
+ s->sr[0x0f] = 0x98;
+ s->sr[0x17] = 0x20;
+ s->sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
+ s->real_vram_size = 4096 * 1024;
+#else
+ s->sr[0x0f] = 0x18;
+ s->sr[0x17] = 0x20;
+ s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+ s->real_vram_size = 2048 * 1024;
+#endif
+ } else {
+ s->sr[0x1F] = 0x22; // MemClock
+ s->sr[0x0F] = CIRRUS_MEMSIZE_2M;
+ if (is_pci)
+ s->sr[0x17] = CIRRUS_BUSTYPE_PCI;
+ else
+ s->sr[0x17] = CIRRUS_BUSTYPE_ISA;
+ s->real_vram_size = 2048 * 1024;
+ s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+ }
+ s->cr[0x27] = device_id;
+
+ /* Win2K seems to assume that the pattern buffer is at 0xff
+ initially ! */
+ memset(s->vram_ptr, 0xff, s->real_vram_size);
+
+ s->cirrus_hidden_dac_lockindex = 5;
+ s->cirrus_hidden_dac_data = 0;
+
+ /* I/O handler for LFB */
+ s->cirrus_linear_io_addr =
+ cpu_register_io_memory(0, cirrus_linear_read, cirrus_linear_write,
+ s);
+ s->cirrus_linear_write = cpu_get_io_memory_write(s->cirrus_linear_io_addr);
+
+ /* I/O handler for LFB */
+ s->cirrus_linear_bitblt_io_addr =
+ cpu_register_io_memory(0, cirrus_linear_bitblt_read, cirrus_linear_bitblt_write,
+ s);
+
+ /* I/O handler for memory-mapped I/O */
+ s->cirrus_mmio_io_addr =
+ cpu_register_io_memory(0, cirrus_mmio_read, cirrus_mmio_write, s);
+
+ /* XXX: s->vram_size must be a power of two */
+ s->cirrus_addr_mask = s->real_vram_size - 1;
+ s->linear_mmio_mask = s->real_vram_size - 256;
+
+ s->get_bpp = cirrus_get_bpp;
+ s->get_offsets = cirrus_get_offsets;
+ s->get_resolution = cirrus_get_resolution;
+ s->cursor_invalidate = cirrus_cursor_invalidate;
+ s->cursor_draw_line = cirrus_cursor_draw_line;
+
+ register_savevm("cirrus_vga", 0, 1, cirrus_vga_save, cirrus_vga_load, s);
+}
+
+/***************************************
+ *
+ * ISA bus support
+ *
+ ***************************************/
+
+void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size)
+{
+ CirrusVGAState *s;
+
+ s = qemu_mallocz(sizeof(CirrusVGAState));
+
+ vga_common_init((VGAState *)s,
+ ds, vga_ram_base, vga_ram_offset, vga_ram_size);
+ cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0);
+ /* XXX ISA-LFB support */
+}
+
+/***************************************
+ *
+ * PCI bus support
+ *
+ ***************************************/
+
+static void cirrus_pci_lfb_map(PCIDevice *d, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga;
+
+ /* XXX: add byte swapping apertures */
+ cpu_register_physical_memory(addr, s->vram_size,
+ s->cirrus_linear_io_addr);
+ cpu_register_physical_memory(addr + 0x1000000, 0x400000,
+ s->cirrus_linear_bitblt_io_addr);
+}
+
+static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga;
+
+ cpu_register_physical_memory(addr, CIRRUS_PNPMMIO_SIZE,
+ s->cirrus_mmio_io_addr);
+}
+
+void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size)
+{
+ PCICirrusVGAState *d;
+ uint8_t *pci_conf;
+ CirrusVGAState *s;
+ int device_id;
+
+ device_id = CIRRUS_ID_CLGD5446;
+
+ /* setup PCI configuration registers */
+ d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA",
+ sizeof(PCICirrusVGAState),
+ -1, NULL, NULL);
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = (uint8_t) (PCI_VENDOR_CIRRUS & 0xff);
+ pci_conf[0x01] = (uint8_t) (PCI_VENDOR_CIRRUS >> 8);
+ pci_conf[0x02] = (uint8_t) (device_id & 0xff);
+ pci_conf[0x03] = (uint8_t) (device_id >> 8);
+ pci_conf[0x04] = PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS;
+ pci_conf[0x0a] = PCI_CLASS_SUB_VGA;
+ pci_conf[0x0b] = PCI_CLASS_BASE_DISPLAY;
+ pci_conf[0x0e] = PCI_CLASS_HEADERTYPE_00h;
+
+ /* setup VGA */
+ s = &d->cirrus_vga;
+ vga_common_init((VGAState *)s,
+ ds, vga_ram_base, vga_ram_offset, vga_ram_size);
+ cirrus_init_common(s, device_id, 1);
+
+ /* setup memory space */
+ /* memory #0 LFB */
+ /* memory #1 memory-mapped I/O */
+ /* XXX: s->vram_size must be a power of two */
+ pci_register_io_region((PCIDevice *)d, 0, 0x2000000,
+ PCI_ADDRESS_SPACE_MEM_PREFETCH, cirrus_pci_lfb_map);
+ if (device_id == CIRRUS_ID_CLGD5446) {
+ pci_register_io_region((PCIDevice *)d, 1, CIRRUS_PNPMMIO_SIZE,
+ PCI_ADDRESS_SPACE_MEM, cirrus_pci_mmio_map);
+ }
+ /* XXX: ROM BIOS */
+}
diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h
new file mode 100644
index 0000000..c54f125
--- /dev/null
+++ b/hw/cirrus_vga_rop.h
@@ -0,0 +1,78 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static void
+glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(*dst, *src);
+ dst++;
+ src++;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(*dst, *src);
+ dst--;
+ src--;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+#define DEPTH 8
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 16
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 24
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 32
+#include "cirrus_vga_rop2.h"
+
+#undef ROP_NAME
+#undef ROP_OP
diff --git a/hw/cirrus_vga_rop2.h b/hw/cirrus_vga_rop2.h
new file mode 100644
index 0000000..da11d0f
--- /dev/null
+++ b/hw/cirrus_vga_rop2.h
@@ -0,0 +1,281 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if DEPTH == 8
+#define PUTPIXEL() ROP_OP(d[0], col)
+#elif DEPTH == 16
+#define PUTPIXEL() ROP_OP(((uint16_t *)d)[0], col);
+#elif DEPTH == 24
+#define PUTPIXEL() ROP_OP(d[0], col); \
+ ROP_OP(d[1], (col >> 8)); \
+ ROP_OP(d[2], (col >> 16))
+#elif DEPTH == 32
+#define PUTPIXEL() ROP_OP(((uint32_t *)d)[0], col)
+#else
+#error unsupported DEPTH
+#endif
+
+static void
+glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, pattern_y, pattern_pitch, pattern_x;
+ unsigned int col;
+ const uint8_t *src1;
+#if DEPTH == 24
+ int skipleft = s->gr[0x2f] & 0x1f;
+#else
+ int skipleft = (s->gr[0x2f] & 0x07) * (DEPTH / 8);
+#endif
+
+#if DEPTH == 8
+ pattern_pitch = 8;
+#elif DEPTH == 16
+ pattern_pitch = 16;
+#else
+ pattern_pitch = 32;
+#endif
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+ for(y = 0; y < bltheight; y++) {
+ pattern_x = skipleft;
+ d = dst + skipleft;
+ src1 = src + pattern_y * pattern_pitch;
+ for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) {
+#if DEPTH == 8
+ col = src1[pattern_x];
+ pattern_x = (pattern_x + 1) & 7;
+#elif DEPTH == 16
+ col = ((uint16_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 2) & 15;
+#elif DEPTH == 24
+ {
+ const uint8_t *src2 = src1 + pattern_x * 3;
+ col = src2[0] | (src2[1] << 8) | (src2[2] << 16);
+ pattern_x = (pattern_x + 1) & 7;
+ }
+#else
+ col = ((uint32_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 4) & 31;
+#endif
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+/* NOTE: srcpitch is ignored */
+static void
+glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y;
+ unsigned bits, bits_xor;
+ unsigned int col;
+ unsigned bitmask;
+ unsigned index;
+#if DEPTH == 24
+ int dstskipleft = s->gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++ ^ bits_xor;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++ ^ bits_xor;
+ }
+ index = (bits & bitmask);
+ if (index) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y;
+ unsigned bits;
+ unsigned int col;
+ unsigned bitmask;
+ int srcskipleft = s->gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++;
+ }
+ col = colors[!!(bits & bitmask)];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits, bits_xor;
+ unsigned int col;
+#if DEPTH == 24
+ int dstskipleft = s->gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y] ^ bits_xor;
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bits >> bitpos) & 1) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits;
+ unsigned int col;
+ int srcskipleft = s->gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y];
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ col = colors[(bits >> bitpos) & 1];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch,
+ int width, int height)
+{
+ uint8_t *d, *d1;
+ uint32_t col;
+ int x, y;
+
+ col = s->cirrus_blt_fgcol;
+
+ d1 = dst;
+ for(y = 0; y < height; y++) {
+ d = d1;
+ for(x = 0; x < width; x += (DEPTH / 8)) {
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ d1 += dst_pitch;
+ }
+}
+
+#undef DEPTH
+#undef PUTPIXEL
diff --git a/hw/cuda.c b/hw/cuda.c
new file mode 100644
index 0000000..f3c2b56
--- /dev/null
+++ b/hw/cuda.c
@@ -0,0 +1,656 @@
+/*
+ * QEMU CUDA support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* XXX: implement all timer modes */
+
+//#define DEBUG_CUDA
+//#define DEBUG_CUDA_PACKET
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define T1_INT 0x40 /* Timer 1 interrupt */
+#define T2_INT 0x20 /* Timer 2 interrupt */
+
+/* Bits in ACR */
+#define T1MODE 0xc0 /* Timer 1 mode */
+#define T1MODE_CONT 0x40 /* continuous interrupts */
+
+/* commands (1st byte) */
+#define ADB_PACKET 0
+#define CUDA_PACKET 1
+#define ERROR_PACKET 2
+#define TIMER_PACKET 3
+#define POWER_PACKET 4
+#define MACIIC_PACKET 5
+#define PMU_PACKET 6
+
+
+/* CUDA commands (2nd byte) */
+#define CUDA_WARM_START 0x0
+#define CUDA_AUTOPOLL 0x1
+#define CUDA_GET_6805_ADDR 0x2
+#define CUDA_GET_TIME 0x3
+#define CUDA_GET_PRAM 0x7
+#define CUDA_SET_6805_ADDR 0x8
+#define CUDA_SET_TIME 0x9
+#define CUDA_POWERDOWN 0xa
+#define CUDA_POWERUP_TIME 0xb
+#define CUDA_SET_PRAM 0xc
+#define CUDA_MS_RESET 0xd
+#define CUDA_SEND_DFAC 0xe
+#define CUDA_BATTERY_SWAP_SENSE 0x10
+#define CUDA_RESET_SYSTEM 0x11
+#define CUDA_SET_IPL 0x12
+#define CUDA_FILE_SERVER_FLAG 0x13
+#define CUDA_SET_AUTO_RATE 0x14
+#define CUDA_GET_AUTO_RATE 0x16
+#define CUDA_SET_DEVICE_LIST 0x19
+#define CUDA_GET_DEVICE_LIST 0x1a
+#define CUDA_SET_ONE_SECOND_MODE 0x1b
+#define CUDA_SET_POWER_MESSAGES 0x21
+#define CUDA_GET_SET_IIC 0x22
+#define CUDA_WAKEUP 0x23
+#define CUDA_TIMER_TICKLE 0x24
+#define CUDA_COMBINED_FORMAT_IIC 0x25
+
+#define CUDA_TIMER_FREQ (4700000 / 6)
+#define CUDA_ADB_POLL_FREQ 50
+
+/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
+#define RTC_OFFSET 2082844800
+
+typedef struct CUDATimer {
+ int index;
+ uint16_t latch;
+ uint16_t counter_value; /* counter value at load time */
+ int64_t load_time;
+ int64_t next_irq_time;
+ QEMUTimer *timer;
+} CUDATimer;
+
+typedef struct CUDAState {
+ /* cuda registers */
+ uint8_t b; /* B-side data */
+ uint8_t a; /* A-side data */
+ uint8_t dirb; /* B-side direction (1=output) */
+ uint8_t dira; /* A-side direction (1=output) */
+ uint8_t sr; /* Shift register */
+ uint8_t acr; /* Auxiliary control register */
+ uint8_t pcr; /* Peripheral control register */
+ uint8_t ifr; /* Interrupt flag register */
+ uint8_t ier; /* Interrupt enable register */
+ uint8_t anh; /* A-side data, no handshake */
+
+ CUDATimer timers[2];
+
+ uint8_t last_b; /* last value of B register */
+ uint8_t last_acr; /* last value of B register */
+
+ int data_in_size;
+ int data_in_index;
+ int data_out_index;
+
+ SetIRQFunc *set_irq;
+ int irq;
+ void *irq_opaque;
+ uint8_t autopoll;
+ uint8_t data_in[128];
+ uint8_t data_out[16];
+ QEMUTimer *adb_poll_timer;
+} CUDAState;
+
+static CUDAState cuda_state;
+ADBBusState adb_bus;
+
+static void cuda_update(CUDAState *s);
+static void cuda_receive_packet_from_host(CUDAState *s,
+ const uint8_t *data, int len);
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time);
+
+static void cuda_update_irq(CUDAState *s)
+{
+ if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ s->set_irq(s->irq_opaque, s->irq, 1);
+ } else {
+ s->set_irq(s->irq_opaque, s->irq, 0);
+ }
+}
+
+static unsigned int get_counter(CUDATimer *s)
+{
+ int64_t d;
+ unsigned int counter;
+
+ d = muldiv64(qemu_get_clock(vm_clock) - s->load_time,
+ CUDA_TIMER_FREQ, ticks_per_sec);
+ if (s->index == 0) {
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
+ } else {
+ counter = (s->counter_value - d) & 0xffff;
+ }
+ return counter;
+}
+
+static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
+{
+#ifdef DEBUG_CUDA
+ printf("cuda: T%d.counter=%d\n",
+ 1 + (ti->timer == NULL), val);
+#endif
+ ti->load_time = qemu_get_clock(vm_clock);
+ ti->counter_value = val;
+ cuda_timer_update(s, ti, ti->load_time);
+}
+
+static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
+{
+ int64_t d, next_time;
+ unsigned int counter;
+
+ /* current counter value */
+ d = muldiv64(current_time - s->load_time,
+ CUDA_TIMER_FREQ, ticks_per_sec);
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
+
+ /* Note: we consider the irq is raised on 0 */
+ if (counter == 0xffff) {
+ next_time = d + s->latch + 1;
+ } else if (counter == 0) {
+ next_time = d + s->latch + 2;
+ } else {
+ next_time = d + counter;
+ }
+#if 0
+#ifdef DEBUG_CUDA
+ printf("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
+ s->latch, d, next_time - d);
+#endif
+#endif
+ next_time = muldiv64(next_time, ticks_per_sec, CUDA_TIMER_FREQ) +
+ s->load_time;
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time)
+{
+ if (!ti->timer)
+ return;
+ if ((s->acr & T1MODE) != T1MODE_CONT) {
+ qemu_del_timer(ti->timer);
+ } else {
+ ti->next_irq_time = get_next_irq_time(ti, current_time);
+ qemu_mod_timer(ti->timer, ti->next_irq_time);
+ }
+}
+
+static void cuda_timer1(void *opaque)
+{
+ CUDAState *s = opaque;
+ CUDATimer *ti = &s->timers[0];
+
+ cuda_timer_update(s, ti, ti->next_irq_time);
+ s->ifr |= T1_INT;
+ cuda_update_irq(s);
+}
+
+static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr)
+{
+ CUDAState *s = opaque;
+ uint32_t val;
+
+ addr = (addr >> 9) & 0xf;
+ switch(addr) {
+ case 0:
+ val = s->b;
+ break;
+ case 1:
+ val = s->a;
+ break;
+ case 2:
+ val = s->dirb;
+ break;
+ case 3:
+ val = s->dira;
+ break;
+ case 4:
+ val = get_counter(&s->timers[0]) & 0xff;
+ s->ifr &= ~T1_INT;
+ cuda_update_irq(s);
+ break;
+ case 5:
+ val = get_counter(&s->timers[0]) >> 8;
+ cuda_update_irq(s);
+ break;
+ case 6:
+ val = s->timers[0].latch & 0xff;
+ break;
+ case 7:
+ /* XXX: check this */
+ val = (s->timers[0].latch >> 8) & 0xff;
+ break;
+ case 8:
+ val = get_counter(&s->timers[1]) & 0xff;
+ s->ifr &= ~T2_INT;
+ break;
+ case 9:
+ val = get_counter(&s->timers[1]) >> 8;
+ break;
+ case 10:
+ val = s->sr;
+ s->ifr &= ~SR_INT;
+ cuda_update_irq(s);
+ break;
+ case 11:
+ val = s->acr;
+ break;
+ case 12:
+ val = s->pcr;
+ break;
+ case 13:
+ val = s->ifr;
+ if (s->ifr & s->ier)
+ val |= 0x80;
+ break;
+ case 14:
+ val = s->ier | 0x80;
+ break;
+ default:
+ case 15:
+ val = s->anh;
+ break;
+ }
+#ifdef DEBUG_CUDA
+ if (addr != 13 || val != 0)
+ printf("cuda: read: reg=0x%x val=%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ CUDAState *s = opaque;
+
+ addr = (addr >> 9) & 0xf;
+#ifdef DEBUG_CUDA
+ printf("cuda: write: reg=0x%x val=%02x\n", addr, val);
+#endif
+
+ switch(addr) {
+ case 0:
+ s->b = val;
+ cuda_update(s);
+ break;
+ case 1:
+ s->a = val;
+ break;
+ case 2:
+ s->dirb = val;
+ break;
+ case 3:
+ s->dira = val;
+ break;
+ case 4:
+ s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 5:
+ s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
+ set_counter(s, &s->timers[0], s->timers[0].latch);
+ break;
+ case 6:
+ s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 7:
+ s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 8:
+ s->timers[1].latch = val;
+ set_counter(s, &s->timers[1], val);
+ break;
+ case 9:
+ set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+ break;
+ case 10:
+ s->sr = val;
+ break;
+ case 11:
+ s->acr = val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ cuda_update(s);
+ break;
+ case 12:
+ s->pcr = val;
+ break;
+ case 13:
+ /* reset bits */
+ s->ifr &= ~val;
+ cuda_update_irq(s);
+ break;
+ case 14:
+ if (val & IER_SET) {
+ /* set bits */
+ s->ier |= val & 0x7f;
+ } else {
+ /* reset bits */
+ s->ier &= ~val;
+ }
+ cuda_update_irq(s);
+ break;
+ default:
+ case 15:
+ s->anh = val;
+ break;
+ }
+}
+
+/* NOTE: TIP and TREQ are negated */
+static void cuda_update(CUDAState *s)
+{
+ int packet_received, len;
+
+ packet_received = 0;
+ if (!(s->b & TIP)) {
+ /* transfer requested from host */
+
+ if (s->acr & SR_OUT) {
+ /* data output */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ if (s->data_out_index < sizeof(s->data_out)) {
+#ifdef DEBUG_CUDA
+ printf("cuda: send: %02x\n", s->sr);
+#endif
+ s->data_out[s->data_out_index++] = s->sr;
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ }
+ } else {
+ if (s->data_in_index < s->data_in_size) {
+ /* data input */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ s->sr = s->data_in[s->data_in_index++];
+#ifdef DEBUG_CUDA
+ printf("cuda: recv: %02x\n", s->sr);
+#endif
+ /* indicate end of transfer */
+ if (s->data_in_index >= s->data_in_size) {
+ s->b = (s->b | TREQ);
+ }
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ }
+ }
+ } else {
+ /* no transfer requested: handle sync case */
+ if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
+ /* update TREQ state each time TACK change state */
+ if (s->b & TACK)
+ s->b = (s->b | TREQ);
+ else
+ s->b = (s->b & ~TREQ);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ } else {
+ if (!(s->last_b & TIP)) {
+ /* handle end of host to cuda transfert */
+ packet_received = (s->data_out_index > 0);
+ /* always an IRQ at the end of transfert */
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ /* signal if there is data to read */
+ if (s->data_in_index < s->data_in_size) {
+ s->b = (s->b & ~TREQ);
+ }
+ }
+ }
+
+ s->last_acr = s->acr;
+ s->last_b = s->b;
+
+ /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
+ recursively */
+ if (packet_received) {
+ len = s->data_out_index;
+ s->data_out_index = 0;
+ cuda_receive_packet_from_host(s, s->data_out, len);
+ }
+}
+
+static void cuda_send_packet_to_host(CUDAState *s,
+ const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_send_packet_to_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
+ memcpy(s->data_in, data, len);
+ s->data_in_size = len;
+ s->data_in_index = 0;
+ cuda_update(s);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_adb_poll(void *opaque)
+{
+ CUDAState *s = opaque;
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+
+ olen = adb_poll(&adb_bus, obuf + 2);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x40; /* polled data */
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (ticks_per_sec / CUDA_ADB_POLL_FREQ));
+}
+
+static void cuda_receive_packet(CUDAState *s,
+ const uint8_t *data, int len)
+{
+ uint8_t obuf[16];
+ int ti, autopoll;
+
+ switch(data[0]) {
+ case CUDA_AUTOPOLL:
+ autopoll = (data[1] != 0);
+ if (autopoll != s->autopoll) {
+ s->autopoll = autopoll;
+ if (autopoll) {
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (ticks_per_sec / CUDA_ADB_POLL_FREQ));
+ } else {
+ qemu_del_timer(s->adb_poll_timer);
+ }
+ }
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = data[1];
+ cuda_send_packet_to_host(s, obuf, 2);
+ break;
+ case CUDA_GET_TIME:
+ case CUDA_SET_TIME:
+ /* XXX: add time support ? */
+ ti = time(NULL) + RTC_OFFSET;
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ obuf[2] = 0;
+ obuf[3] = ti >> 24;
+ obuf[4] = ti >> 16;
+ obuf[5] = ti >> 8;
+ obuf[6] = ti;
+ cuda_send_packet_to_host(s, obuf, 7);
+ break;
+ case CUDA_FILE_SERVER_FLAG:
+ case CUDA_SET_DEVICE_LIST:
+ case CUDA_SET_AUTO_RATE:
+ case CUDA_SET_POWER_MESSAGES:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ break;
+ case CUDA_POWERDOWN:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ qemu_system_shutdown_request();
+ break;
+ default:
+ break;
+ }
+}
+
+static void cuda_receive_packet_from_host(CUDAState *s,
+ const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_receive_packet_from_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
+ switch(data[0]) {
+ case ADB_PACKET:
+ {
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+ olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x00;
+ } else {
+ /* error */
+ obuf[0] = ADB_PACKET;
+ obuf[1] = -olen;
+ olen = 0;
+ }
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
+ break;
+ case CUDA_PACKET:
+ cuda_receive_packet(s, data + 1, len - 1);
+ break;
+ }
+}
+
+static void cuda_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static void cuda_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static uint32_t cuda_readw (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static CPUWriteMemoryFunc *cuda_write[] = {
+ &cuda_writeb,
+ &cuda_writew,
+ &cuda_writel,
+};
+
+static CPUReadMemoryFunc *cuda_read[] = {
+ &cuda_readb,
+ &cuda_readw,
+ &cuda_readl,
+};
+
+int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq)
+{
+ CUDAState *s = &cuda_state;
+ int cuda_mem_index;
+
+ s->set_irq = set_irq;
+ s->irq_opaque = irq_opaque;
+ s->irq = irq;
+
+ s->timers[0].index = 0;
+ s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
+ s->timers[0].latch = 0xffff;
+ set_counter(s, &s->timers[0], 0xffff);
+
+ s->timers[1].index = 1;
+ s->timers[1].latch = 0;
+ // s->ier = T1_INT | SR_INT;
+ s->ier = 0;
+ set_counter(s, &s->timers[1], 0xffff);
+
+ s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s);
+ cuda_mem_index = cpu_register_io_memory(0, cuda_read, cuda_write, s);
+ return cuda_mem_index;
+}
diff --git a/hw/dma.c b/hw/dma.c
new file mode 100644
index 0000000..ea13eae
--- /dev/null
+++ b/hw/dma.c
@@ -0,0 +1,537 @@
+/*
+ * QEMU DMA emulation
+ *
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* #define DEBUG_DMA */
+
+#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#ifdef DEBUG_DMA
+#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#else
+#define lwarn(...)
+#define linfo(...)
+#define ldebug(...)
+#endif
+
+#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
+
+struct dma_regs {
+ int now[2];
+ uint16_t base[2];
+ uint8_t mode;
+ uint8_t page;
+ uint8_t pageh;
+ uint8_t dack;
+ uint8_t eop;
+ DMA_transfer_handler transfer_handler;
+ void *opaque;
+};
+
+#define ADDR 0
+#define COUNT 1
+
+static struct dma_cont {
+ uint8_t status;
+ uint8_t command;
+ uint8_t mask;
+ uint8_t flip_flop;
+ int dshift;
+ struct dma_regs regs[4];
+} dma_controllers[2];
+
+enum {
+ CMD_MEMORY_TO_MEMORY = 0x01,
+ CMD_FIXED_ADDRESS = 0x02,
+ CMD_BLOCK_CONTROLLER = 0x04,
+ CMD_COMPRESSED_TIME = 0x08,
+ CMD_CYCLIC_PRIORITY = 0x10,
+ CMD_EXTENDED_WRITE = 0x20,
+ CMD_LOW_DREQ = 0x40,
+ CMD_LOW_DACK = 0x80,
+ CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
+ | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
+ | CMD_LOW_DREQ | CMD_LOW_DACK
+
+};
+
+static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+
+static void write_page (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].page = data;
+}
+
+static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].pageh = data;
+}
+
+static uint32_t read_page (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].page;
+}
+
+static uint32_t read_pageh (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].pageh;
+}
+
+static inline void init_chan (struct dma_cont *d, int ichan)
+{
+ struct dma_regs *r;
+
+ r = d->regs + ichan;
+ r->now[ADDR] = r->base[ADDR] << d->dshift;
+ r->now[COUNT] = 0;
+}
+
+static inline int getff (struct dma_cont *d)
+{
+ int ff;
+
+ ff = d->flip_flop;
+ d->flip_flop = !ff;
+ return ff;
+}
+
+static uint32_t read_chan (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan, nreg, iport, ff, val, dir;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+
+ dir = ((r->mode >> 5) & 1) ? -1 : 1;
+ ff = getff (d);
+ if (nreg)
+ val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
+ else
+ val = r->now[ADDR] + r->now[COUNT] * dir;
+
+ ldebug ("read_chan %#x -> %d\n", iport, val);
+ return (val >> (d->dshift + (ff << 3))) & 0xff;
+}
+
+static void write_chan (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan, nreg;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+ if (getff (d)) {
+ r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
+ init_chan (d, ichan);
+ } else {
+ r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
+ }
+}
+
+static void write_cont (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan = 0;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* command */
+ if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
+ dolog ("command %#x not supported\n", data);
+ return;
+ }
+ d->command = data;
+ break;
+
+ case 0x09:
+ ichan = data & 3;
+ if (data & 4) {
+ d->status |= 1 << (ichan + 4);
+ }
+ else {
+ d->status &= ~(1 << (ichan + 4));
+ }
+ d->status &= ~(1 << ichan);
+ break;
+
+ case 0x0a: /* single mask */
+ if (data & 4)
+ d->mask |= 1 << (data & 3);
+ else
+ d->mask &= ~(1 << (data & 3));
+ break;
+
+ case 0x0b: /* mode */
+ {
+ ichan = data & 3;
+#ifdef DEBUG_DMA
+ {
+ int op, ai, dir, opmode;
+ op = (data >> 2) & 3;
+ ai = (data >> 4) & 1;
+ dir = (data >> 5) & 1;
+ opmode = (data >> 6) & 3;
+
+ linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
+ ichan, op, ai, dir, opmode);
+ }
+#endif
+ d->regs[ichan].mode = data;
+ break;
+ }
+
+ case 0x0c: /* clear flip flop */
+ d->flip_flop = 0;
+ break;
+
+ case 0x0d: /* reset */
+ d->flip_flop = 0;
+ d->mask = ~0;
+ d->status = 0;
+ d->command = 0;
+ break;
+
+ case 0x0e: /* clear mask for all channels */
+ d->mask = 0;
+ break;
+
+ case 0x0f: /* write mask for all channels */
+ d->mask = data;
+ break;
+
+ default:
+ dolog ("unknown iport %#x\n", iport);
+ break;
+ }
+
+#ifdef DEBUG_DMA
+ if (0xc != iport) {
+ linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
+ nport, ichan, data);
+ }
+#endif
+}
+
+static uint32_t read_cont (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int iport, val;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* status */
+ val = d->status;
+ d->status &= 0xf0;
+ break;
+ case 0x0f: /* mask */
+ val = d->mask;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
+ return val;
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+ return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+}
+
+void DMA_hold_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("held cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status |= 1 << (ichan + 4);
+}
+
+void DMA_release_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("released cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status &= ~(1 << (ichan + 4));
+}
+
+static void channel_run (int ncont, int ichan)
+{
+ int n;
+ struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+#ifdef DEBUG_DMA
+ int dir, opmode;
+
+ dir = (r->mode >> 5) & 1;
+ opmode = (r->mode >> 6) & 3;
+
+ if (dir) {
+ dolog ("DMA in address decrement mode\n");
+ }
+ if (opmode != 1) {
+ dolog ("DMA not in single mode select %#x\n", opmode);
+ }
+#endif
+
+ r = dma_controllers[ncont].regs + ichan;
+ n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
+ r->now[COUNT], (r->base[COUNT] + 1) << ncont);
+ r->now[COUNT] = n;
+ ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+}
+
+void DMA_run (void)
+{
+ struct dma_cont *d;
+ int icont, ichan;
+
+ d = dma_controllers;
+
+ for (icont = 0; icont < 2; icont++, d++) {
+ for (ichan = 0; ichan < 4; ichan++) {
+ int mask;
+
+ mask = 1 << ichan;
+
+ if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4))))
+ channel_run (icont, ichan);
+ }
+ }
+}
+
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+ struct dma_regs *r;
+ int ichan, ncont;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+
+ r = dma_controllers[ncont].regs + ichan;
+ r->transfer_handler = transfer_handler;
+ r->opaque = opaque;
+}
+
+int DMA_read_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_read (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len >> 1; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_read (addr + pos, buf, len);
+
+ return len;
+}
+
+int DMA_write_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_write (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_write (addr + pos, buf, len);
+
+ return len;
+}
+
+/* request the emulator to transfer a new DMA memory block ASAP */
+void DMA_schedule(int nchan)
+{
+ CPUState *env = cpu_single_env;
+ if (env)
+ cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+}
+
+static void dma_reset(void *opaque)
+{
+ struct dma_cont *d = opaque;
+ write_cont (d, (0x0d << d->dshift), 0);
+}
+
+/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
+static void dma_init2(struct dma_cont *d, int base, int dshift,
+ int page_base, int pageh_base)
+{
+ const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
+ int i;
+
+ d->dshift = dshift;
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
+ register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
+ }
+ for (i = 0; i < LENOFA (page_port_list); i++) {
+ register_ioport_write (page_base + page_port_list[i], 1, 1,
+ write_page, d);
+ register_ioport_read (page_base + page_port_list[i], 1, 1,
+ read_page, d);
+ if (pageh_base >= 0) {
+ register_ioport_write (pageh_base + page_port_list[i], 1, 1,
+ write_pageh, d);
+ register_ioport_read (pageh_base + page_port_list[i], 1, 1,
+ read_pageh, d);
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + ((i + 8) << dshift), 1, 1,
+ write_cont, d);
+ register_ioport_read (base + ((i + 8) << dshift), 1, 1,
+ read_cont, d);
+ }
+ qemu_register_reset(dma_reset, d);
+ dma_reset(d);
+}
+
+static void dma_save (QEMUFile *f, void *opaque)
+{
+ struct dma_cont *d = opaque;
+ int i;
+
+ /* qemu_put_8s (f, &d->status); */
+ qemu_put_8s (f, &d->command);
+ qemu_put_8s (f, &d->mask);
+ qemu_put_8s (f, &d->flip_flop);
+ qemu_put_be32s (f, &d->dshift);
+
+ for (i = 0; i < 4; ++i) {
+ struct dma_regs *r = &d->regs[i];
+ qemu_put_be32s (f, &r->now[0]);
+ qemu_put_be32s (f, &r->now[1]);
+ qemu_put_be16s (f, &r->base[0]);
+ qemu_put_be16s (f, &r->base[1]);
+ qemu_put_8s (f, &r->mode);
+ qemu_put_8s (f, &r->page);
+ qemu_put_8s (f, &r->pageh);
+ qemu_put_8s (f, &r->dack);
+ qemu_put_8s (f, &r->eop);
+ }
+}
+
+static int dma_load (QEMUFile *f, void *opaque, int version_id)
+{
+ struct dma_cont *d = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ /* qemu_get_8s (f, &d->status); */
+ qemu_get_8s (f, &d->command);
+ qemu_get_8s (f, &d->mask);
+ qemu_get_8s (f, &d->flip_flop);
+ qemu_get_be32s (f, &d->dshift);
+
+ for (i = 0; i < 4; ++i) {
+ struct dma_regs *r = &d->regs[i];
+ qemu_get_be32s (f, &r->now[0]);
+ qemu_get_be32s (f, &r->now[1]);
+ qemu_get_be16s (f, &r->base[0]);
+ qemu_get_be16s (f, &r->base[1]);
+ qemu_get_8s (f, &r->mode);
+ qemu_get_8s (f, &r->page);
+ qemu_get_8s (f, &r->pageh);
+ qemu_get_8s (f, &r->dack);
+ qemu_get_8s (f, &r->eop);
+ }
+ return 0;
+}
+
+void DMA_init (int high_page_enable)
+{
+ dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
+ high_page_enable ? 0x480 : -1);
+ dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
+ high_page_enable ? 0x488 : -1);
+ register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
+ register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
+}
diff --git a/hw/es1370.c b/hw/es1370.c
new file mode 100644
index 0000000..0d2d861
--- /dev/null
+++ b/hw/es1370.c
@@ -0,0 +1,1062 @@
+/*
+ * QEMU ES1370 emulation
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* #define DEBUG_ES1370 */
+/* #define VERBOSE_ES1370 */
+#define SILENT_ES1370
+
+#include "vl.h"
+
+/* Missing stuff:
+ SCTRL_P[12](END|ST)INC
+ SCTRL_P1SCTRLD
+ SCTRL_P2DACSEN
+ CTRL_DAC_SYNC
+ MIDI
+ non looped mode
+ surely more
+*/
+
+/*
+ Following macros and samplerate array were copied verbatim from
+ Linux kernel 2.4.30: drivers/sound/es1370.c
+
+ Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
+*/
+
+/* Start blatant GPL violation */
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* electret mic bias */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define USTAT_RXINT 0x80 /* UART rx int pending */
+#define USTAT_TXINT 0x04 /* UART tx int pending */
+#define USTAT_TXRDY 0x02 /* UART tx ready */
+#define USTAT_RXRDY 0x01 /* UART rx ready */
+
+#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */
+#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20 /* enable TX int */
+#define UCTRL_CNTRL 0x03 /* control field */
+#define UCTRL_CNTRL_SWR 0x03 /* software reset command */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+/* End blatant GPL violation */
+
+#define NB_CHANNELS 3
+#define DAC1_CHANNEL 0
+#define DAC2_CHANNEL 1
+#define ADC_CHANNEL 2
+
+#define IO_READ_PROTO(n) \
+static uint32_t n (void *opaque, uint32_t addr)
+#define IO_WRITE_PROTO(n) \
+static void n (void *opaque, uint32_t addr, uint32_t val)
+
+static void es1370_dac1_callback (void *opaque, int free);
+static void es1370_dac2_callback (void *opaque, int free);
+static void es1370_adc_callback (void *opaque, int avail);
+
+#ifdef DEBUG_ES1370
+
+#define ldebug(...) AUD_log ("es1370", __VA_ARGS__)
+
+static void print_ctl (uint32_t val)
+{
+ char buf[1024];
+
+ buf[0] = '\0';
+#define a(n) if (val & CTRL_##n) strcat (buf, " "#n)
+ a (ADC_STOP);
+ a (XCTL1);
+ a (OPEN);
+ a (MSFMTSEL);
+ a (M_SBB);
+ a (DAC_SYNC);
+ a (CCB_INTRM);
+ a (M_CB);
+ a (XCTL0);
+ a (BREQ);
+ a (DAC1_EN);
+ a (DAC2_EN);
+ a (ADC_EN);
+ a (UART_EN);
+ a (JYSTK_EN);
+ a (CDC_EN);
+ a (SERR_DIS);
+#undef a
+ AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n",
+ (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV,
+ DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+ dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+ buf);
+}
+
+static void print_sctl (uint32_t val)
+{
+ static const char *fmt_names[] = {"8M", "8S", "16M", "16S"};
+ char buf[1024];
+
+ buf[0] = '\0';
+
+#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n)
+#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n)
+ b (R1LOOPSEL);
+ b (P2LOOPSEL);
+ b (P1LOOPSEL);
+ a (P2PAUSE);
+ a (P1PAUSE);
+ a (R1INTEN);
+ a (P2INTEN);
+ a (P1INTEN);
+ a (P1SCTRLD);
+ a (P2DACSEN);
+ if (buf[0]) {
+ strcat (buf, "\n ");
+ }
+ else {
+ buf[0] = ' ';
+ buf[1] = '\0';
+ }
+#undef b
+#undef a
+ AUD_log ("es1370",
+ "%s"
+ "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n",
+ buf,
+ (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC,
+ (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC,
+ fmt_names [(val >> SCTRL_SH_R1FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P2FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P1FMT) & 3]
+ );
+}
+#else
+#define ldebug(...)
+#define print_ctl(...)
+#define print_sctl(...)
+#endif
+
+#ifdef VERBOSE_ES1370
+#define dolog(...) AUD_log ("es1370", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#ifndef SILENT_ES1370
+#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__)
+#else
+#define lwarn(...)
+#endif
+
+struct chan {
+ uint32_t shift;
+ uint32_t leftover;
+ uint32_t scount;
+ uint32_t frame_addr;
+ uint32_t frame_cnt;
+};
+
+typedef struct ES1370State {
+ PCIDevice *pci_dev;
+
+ QEMUSoundCard card;
+ struct chan chan[NB_CHANNELS];
+ SWVoiceOut *dac_voice[2];
+ SWVoiceIn *adc_voice;
+
+ uint32_t ctl;
+ uint32_t status;
+ uint32_t mempage;
+ uint32_t codec;
+ uint32_t sctl;
+} ES1370State;
+
+typedef struct PCIES1370State {
+ PCIDevice dev;
+ ES1370State es1370;
+} PCIES1370State;
+
+struct chan_bits {
+ uint32_t ctl_en;
+ uint32_t stat_int;
+ uint32_t sctl_pause;
+ uint32_t sctl_inten;
+ uint32_t sctl_fmt;
+ uint32_t sctl_sh_fmt;
+ uint32_t sctl_loopsel;
+ void (*calc_freq) (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+};
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq);
+
+static const struct chan_bits es1370_chan_bits[] = {
+ {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN,
+ SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL,
+ es1370_dac1_calc_freq},
+
+ {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN,
+ SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL,
+ es1370_dac2_and_adc_calc_freq},
+
+ {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN,
+ SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL,
+ es1370_dac2_and_adc_calc_freq}
+};
+
+static void es1370_update_status (ES1370State *s, uint32_t new_status)
+{
+ uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
+
+ if (level) {
+ s->status = new_status | STAT_INTR;
+ }
+ else {
+ s->status = new_status & ~STAT_INTR;
+ }
+ pci_set_irq (s->pci_dev, 0, !!level);
+}
+
+static void es1370_reset (ES1370State *s)
+{
+ size_t i;
+
+ s->ctl = 1;
+ s->status = 0x60;
+ s->mempage = 0;
+ s->codec = 0;
+ s->sctl = 0;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ d->scount = 0;
+ d->leftover = 0;
+ if (i == ADC_CHANNEL) {
+ AUD_close_in (&s->card, s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ else {
+ AUD_close_out (&s->card, s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ pci_set_irq (s->pci_dev, 0, 0);
+}
+
+static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl)
+{
+ uint32_t new_status = s->status;
+
+ if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) {
+ new_status &= ~STAT_DAC1;
+ }
+
+ if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) {
+ new_status &= ~STAT_DAC2;
+ }
+
+ if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) {
+ new_status &= ~STAT_ADC;
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq)
+
+{
+ *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+ *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+}
+
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq)
+
+{
+ uint32_t old_pclkdiv, new_pclkdiv;
+
+ new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ *new_freq = DAC2_DIVTOSR (new_pclkdiv);
+ *old_freq = DAC2_DIVTOSR (old_pclkdiv);
+}
+
+static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
+{
+ size_t i;
+ uint32_t old_freq, new_freq, old_fmt, new_fmt;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ const struct chan_bits *b = &es1370_chan_bits[i];
+
+ new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+ old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+
+ b->calc_freq (s, ctl, &old_freq, &new_freq);
+
+ if ((old_fmt != new_fmt) || (old_freq != new_freq)) {
+ d->shift = (new_fmt & 1) + (new_fmt >> 1);
+ ldebug ("channel %d, freq = %d, nchannels %d, fmt %d, shift %d\n",
+ i,
+ new_freq,
+ 1 << (new_fmt & 1),
+ (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+ d->shift);
+ if (new_freq) {
+ audsettings_t as;
+
+ as.freq = new_freq;
+ as.nchannels = 1 << (new_fmt & 1);
+ as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.endianness = 0;
+
+ if (i == ADC_CHANNEL) {
+ s->adc_voice =
+ AUD_open_in (
+ &s->card,
+ s->adc_voice,
+ "es1370.adc",
+ s,
+ es1370_adc_callback,
+ &as
+ );
+ }
+ else {
+ s->dac_voice[i] =
+ AUD_open_out (
+ &s->card,
+ s->dac_voice[i],
+ i ? "es1370.dac2" : "es1370.dac1",
+ s,
+ i ? es1370_dac2_callback : es1370_dac1_callback,
+ &as
+ );
+ }
+ }
+ }
+
+ if (((ctl ^ s->ctl) & b->ctl_en)
+ || ((sctl ^ s->sctl) & b->sctl_pause)) {
+ int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause);
+
+ if (i == ADC_CHANNEL) {
+ AUD_set_active_in (s->adc_voice, on);
+ }
+ else {
+ AUD_set_active_out (s->dac_voice[i], on);
+ }
+ }
+ }
+
+ s->ctl = ctl;
+ s->sctl = sctl;
+}
+
+static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
+{
+ addr &= 0xff;
+ if (addr >= 0x30 && addr <= 0x3f)
+ addr |= s->mempage << 8;
+ return addr;
+}
+
+IO_WRITE_PROTO (es1370_writeb)
+{
+ ES1370State *s = opaque;
+ uint32_t shift, mask;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ shift = (addr - ES1370_REG_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ case ES1370_REG_SERIAL_CONTROL + 1:
+ case ES1370_REG_SERIAL_CONTROL + 2:
+ case ES1370_REG_SERIAL_CONTROL + 3:
+ shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->sctl & ~mask) | ((val & 0xff) << shift);
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+ default:
+ lwarn ("writeb %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writew)
+{
+ ES1370State *s = opaque;
+ addr = es1370_fixup (s, addr);
+ uint32_t shift, mask;
+ struct chan *d = &s->chan[0];
+
+ switch (addr) {
+ case ES1370_REG_CODEC:
+ dolog ("ignored codec write address %#x, data %#x\n",
+ (val >> 8) & 0xff, val & 0xff);
+ s->codec = val;
+ break;
+
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 2:
+ shift = (addr != ES1370_REG_CONTROL) << 4;
+ mask = 0xffff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (d->scount & ~0xffff) | (val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writew %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writel)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val & 0xf;
+ break;
+
+ case ES1370_REG_SERIAL_CONTROL:
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (val & 0xffff) | (d->scount & ~0xffff);
+ ldebug ("chan %d CURR_SAMP_CT %d, SAMP_CT %d\n",
+ d - &s->chan[0], val >> 16, (val & 0xffff));
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ d->frame_addr = val;
+ ldebug ("chan %d frame address %#x\n", d - &s->chan[0], val);
+ break;
+
+ case ES1370_REG_PHANTOM_FRAMECNT:
+ lwarn ("writing to phantom frame count %#x\n", val);
+ break;
+ case ES1370_REG_PHANTOM_FRAMEADR:
+ lwarn ("writing to phantom frame address %#x\n", val);
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ d->frame_cnt = val;
+ d->leftover = 0;
+ ldebug ("chan %d frame count %d, buffer size %d\n",
+ d - &s->chan[0], val >> 16, val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writel %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_READ_PROTO (es1370_readb)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case 0x1b: /* Legacy */
+ lwarn ("Attempt to read from legacy register\n");
+ val = 5;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CONTROL + 0:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
+ break;
+ case ES1370_REG_STATUS + 0:
+ case ES1370_REG_STATUS + 1:
+ case ES1370_REG_STATUS + 2:
+ case ES1370_REG_STATUS + 3:
+ val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
+ break;
+ default:
+ val = ~0;
+ lwarn ("readb %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+IO_READ_PROTO (es1370_readw)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_ADC_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT + 2:
+ val = d->scount >> 16;
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ val = d->frame_cnt & 0xffff;
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT + 2:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT + 2:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT + 2:
+ val = d->frame_cnt >> 16;
+ break;
+
+ default:
+ val = ~0;
+ lwarn ("readw %#x -> %#x\n", addr, val);
+ break;
+ }
+
+ return val;
+}
+
+IO_READ_PROTO (es1370_readl)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ val = s->ctl;
+ break;
+ case ES1370_REG_STATUS:
+ val = s->status;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CODEC:
+ val = s->codec;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ val = s->sctl;
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ val = d->scount;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t curr_count = d->scount >> 16;
+ uint32_t count = d->scount & 0xffff;
+
+ curr_count <<= d->shift;
+ count <<= d->shift;
+ dolog ("read scount curr %d, total %d\n", curr_count, count);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ val = d->frame_cnt;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2;
+ uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2;
+ if (curr > size)
+ dolog ("read framecnt curr %d, size %d %d\n", curr, size,
+ curr > size);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ val = d->frame_addr;
+ break;
+
+ case ES1370_REG_PHANTOM_FRAMECNT:
+ val = ~0U;
+ lwarn ("reading from phantom frame count\n");
+ break;
+ case ES1370_REG_PHANTOM_FRAMEADR:
+ val = ~0U;
+ lwarn ("reading from phantom frame address\n");
+ break;
+
+ default:
+ val = ~0U;
+ lwarn ("readl %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+
+static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
+ int max, int *irq)
+{
+ uint8_t tmpbuf[4096];
+ uint32_t addr = d->frame_addr;
+ int sc = d->scount & 0xffff;
+ int csc = d->scount >> 16;
+ int csc_bytes = (csc + 1) << d->shift;
+ int cnt = d->frame_cnt >> 16;
+ int size = d->frame_cnt & 0xffff;
+ int left = ((size - cnt + 1) << 2) + d->leftover;
+ int transfered = 0;
+ int temp = audio_MIN (max, audio_MIN (left, csc_bytes));
+ int index = d - &s->chan[0];
+
+ addr += (cnt << 2) + d->leftover;
+
+ if (index == ADC_CHANNEL) {
+ while (temp) {
+ int acquired, to_copy;
+
+ to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+ acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
+ if (!acquired)
+ break;
+
+ cpu_physical_memory_write (addr, tmpbuf, acquired);
+
+ temp -= acquired;
+ addr += acquired;
+ transfered += acquired;
+ }
+ }
+ else {
+ SWVoiceOut *voice = s->dac_voice[index];
+
+ while (temp) {
+ int copied, to_copy;
+
+ to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+ cpu_physical_memory_read (addr, tmpbuf, to_copy);
+ copied = AUD_write (voice, tmpbuf, to_copy);
+ if (!copied)
+ break;
+ temp -= copied;
+ addr += copied;
+ transfered += copied;
+ }
+ }
+
+ if (csc_bytes == transfered) {
+ *irq = 1;
+ d->scount = sc | (sc << 16);
+ ldebug ("sc = %d, rate = %f\n",
+ (sc + 1) << d->shift,
+ (sc + 1) / (double) 44100);
+ }
+ else {
+ *irq = 0;
+ d->scount = sc | (((csc_bytes - transfered - 1) >> d->shift) << 16);
+ }
+
+ cnt += (transfered + d->leftover) >> 2;
+
+ if (s->sctl & loop_sel) {
+ /* Bah, how stupid is that having a 0 represent true value?
+ i just spent few hours on this shit */
+ AUD_log ("es1370: warning", "non looping mode\n");
+ }
+ else {
+ d->frame_cnt = size;
+
+ if ((uint32_t) cnt <= d->frame_cnt)
+ d->frame_cnt |= cnt << 16;
+ }
+
+ d->leftover = (transfered + d->leftover) & 3;
+}
+
+static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail)
+{
+ uint32_t new_status = s->status;
+ int max_bytes, irq;
+ struct chan *d = &s->chan[chan];
+ const struct chan_bits *b = &es1370_chan_bits[chan];
+
+ if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) {
+ return;
+ }
+
+ max_bytes = free_or_avail;
+ max_bytes &= ~((1 << d->shift) - 1);
+ if (!max_bytes) {
+ return;
+ }
+
+ es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq);
+
+ if (irq) {
+ if (s->sctl & b->sctl_inten) {
+ new_status |= b->stat_int;
+ }
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC1_CHANNEL, free);
+}
+
+static void es1370_dac2_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC2_CHANNEL, free);
+}
+
+static void es1370_adc_callback (void *opaque, int avail)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, ADC_CHANNEL, avail);
+}
+
+static void es1370_map (PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIES1370State *d = (PCIES1370State *) pci_dev;
+ ES1370State *s = &d->es1370;
+
+ (void) region_num;
+ (void) size;
+ (void) type;
+
+ register_ioport_write (addr, 0x40 * 4, 1, es1370_writeb, s);
+ register_ioport_write (addr, 0x40 * 2, 2, es1370_writew, s);
+ register_ioport_write (addr, 0x40, 4, es1370_writel, s);
+
+ register_ioport_read (addr, 0x40 * 4, 1, es1370_readb, s);
+ register_ioport_read (addr, 0x40 * 2, 2, es1370_readw, s);
+ register_ioport_read (addr, 0x40, 4, es1370_readl, s);
+}
+
+static void es1370_save (QEMUFile *f, void *opaque)
+{
+ ES1370State *s = opaque;
+ size_t i;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ qemu_put_be32s (f, &d->shift);
+ qemu_put_be32s (f, &d->leftover);
+ qemu_put_be32s (f, &d->scount);
+ qemu_put_be32s (f, &d->frame_addr);
+ qemu_put_be32s (f, &d->frame_cnt);
+ }
+ qemu_put_be32s (f, &s->ctl);
+ qemu_put_be32s (f, &s->status);
+ qemu_put_be32s (f, &s->mempage);
+ qemu_put_be32s (f, &s->codec);
+ qemu_put_be32s (f, &s->sctl);
+}
+
+static int es1370_load (QEMUFile *f, void *opaque, int version_id)
+{
+ uint32_t ctl, sctl;
+ ES1370State *s = opaque;
+ size_t i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ qemu_get_be32s (f, &d->shift);
+ qemu_get_be32s (f, &d->leftover);
+ qemu_get_be32s (f, &d->scount);
+ qemu_get_be32s (f, &d->frame_addr);
+ qemu_get_be32s (f, &d->frame_cnt);
+ if (i == ADC_CHANNEL) {
+ if (s->adc_voice) {
+ AUD_close_in (&s->card, s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ }
+ else {
+ if (s->dac_voice[i]) {
+ AUD_close_out (&s->card, s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ }
+
+ qemu_get_be32s (f, &ctl);
+ qemu_get_be32s (f, &s->status);
+ qemu_get_be32s (f, &s->mempage);
+ qemu_get_be32s (f, &s->codec);
+ qemu_get_be32s (f, &sctl);
+
+ s->ctl = 0;
+ s->sctl = 0;
+ es1370_update_voices (s, ctl, sctl);
+ return 0;
+}
+
+static void es1370_on_reset (void *opaque)
+{
+ ES1370State *s = opaque;
+ es1370_reset (s);
+}
+
+int es1370_init (PCIBus *bus, AudioState *audio)
+{
+ PCIES1370State *d;
+ ES1370State *s;
+ uint8_t *c;
+
+ if (!bus) {
+ dolog ("No PCI bus\n");
+ return -1;
+ }
+
+ if (!audio) {
+ dolog ("No audio state\n");
+ return -1;
+ }
+
+ d = (PCIES1370State *) pci_register_device (bus, "ES1370",
+ sizeof (PCIES1370State),
+ -1, NULL, NULL);
+
+ if (!d) {
+ AUD_log (NULL, "Failed to register PCI device for ES1370\n");
+ return -1;
+ }
+
+ c = d->dev.config;
+ c[0x00] = 0x74;
+ c[0x01] = 0x12;
+ c[0x02] = 0x00;
+ c[0x03] = 0x50;
+ c[0x07] = 2 << 1;
+ c[0x0a] = 0x01;
+ c[0x0b] = 0x04;
+
+#if 1
+ c[0x2c] = 0x42;
+ c[0x2d] = 0x49;
+ c[0x2e] = 0x4c;
+ c[0x2f] = 0x4c;
+#else
+ c[0x2c] = 0x74;
+ c[0x2d] = 0x12;
+ c[0x2e] = 0x71;
+ c[0x2f] = 0x13;
+ c[0x34] = 0xdc;
+ c[0x3c] = 10;
+ c[0xdc] = 0x00;
+#endif
+
+ c[0x3d] = 1;
+ c[0x3e] = 0x0c;
+ c[0x3f] = 0x80;
+
+ s = &d->es1370;
+ s->pci_dev = &d->dev;
+
+ pci_register_io_region (&d->dev, 0, 256, PCI_ADDRESS_SPACE_IO, es1370_map);
+ register_savevm ("es1370", 0, 1, es1370_save, es1370_load, s);
+ qemu_register_reset (es1370_on_reset, s);
+
+ AUD_register_card (audio, "es1370", &s->card);
+ es1370_reset (s);
+ return 0;
+}
diff --git a/hw/esp.c b/hw/esp.c
new file mode 100644
index 0000000..cdd062f
--- /dev/null
+++ b/hw/esp.c
@@ -0,0 +1,571 @@
+/*
+ * QEMU ESP emulation
+ *
+ * Copyright (c) 2005-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug ESP card */
+//#define DEBUG_ESP
+
+#ifdef DEBUG_ESP
+#define DPRINTF(fmt, args...) \
+do { printf("ESP: " fmt , ##args); } while (0)
+#define pic_set_irq(irq, level) \
+do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+#define ESPDMA_REGS 4
+#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
+#define ESP_MAXREG 0x3f
+#define TI_BUFSZ 32
+#define DMA_VER 0xa0000000
+#define DMA_INTR 1
+#define DMA_INTREN 0x10
+#define DMA_WRITE_MEM 0x100
+#define DMA_LOADED 0x04000000
+typedef struct ESPState ESPState;
+
+struct ESPState {
+ BlockDriverState **bd;
+ uint8_t rregs[ESP_MAXREG];
+ uint8_t wregs[ESP_MAXREG];
+ int irq;
+ uint32_t espdmaregs[ESPDMA_REGS];
+ uint32_t ti_size;
+ uint32_t ti_rptr, ti_wptr;
+ uint8_t ti_buf[TI_BUFSZ];
+ int sense;
+ int dma;
+ SCSIDevice *scsi_dev[MAX_DISKS];
+ SCSIDevice *current_dev;
+ uint8_t cmdbuf[TI_BUFSZ];
+ int cmdlen;
+ int do_cmd;
+};
+
+#define STAT_DO 0x00
+#define STAT_DI 0x01
+#define STAT_CD 0x02
+#define STAT_ST 0x03
+#define STAT_MI 0x06
+#define STAT_MO 0x07
+
+#define STAT_TC 0x10
+#define STAT_IN 0x80
+
+#define INTR_FC 0x08
+#define INTR_BS 0x10
+#define INTR_DC 0x20
+#define INTR_RST 0x80
+
+#define SEQ_0 0x0
+#define SEQ_CD 0x4
+
+static int get_cmd(ESPState *s, uint8_t *buf)
+{
+ uint32_t dmaptr, dmalen;
+ int target;
+
+ dmalen = s->wregs[0] | (s->wregs[1] << 8);
+ target = s->wregs[4] & 7;
+ DPRINTF("get_cmd: len %d target %d\n", dmalen, target);
+ if (s->dma) {
+ dmaptr = iommu_translate(s->espdmaregs[1]);
+ DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
+ s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
+ cpu_physical_memory_read(dmaptr, buf, dmalen);
+ } else {
+ buf[0] = 0;
+ memcpy(&buf[1], s->ti_buf, dmalen);
+ dmalen++;
+ }
+
+ s->ti_size = 0;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+
+ if (target >= 4 || !s->scsi_dev[target]) {
+ // No such drive
+ s->rregs[4] = STAT_IN;
+ s->rregs[5] = INTR_DC;
+ s->rregs[6] = SEQ_0;
+ s->espdmaregs[0] |= DMA_INTR;
+ pic_set_irq(s->irq, 1);
+ return 0;
+ }
+ s->current_dev = s->scsi_dev[target];
+ return dmalen;
+}
+
+static void do_cmd(ESPState *s, uint8_t *buf)
+{
+ int32_t datalen;
+ int lun;
+
+ DPRINTF("do_cmd: busid 0x%x\n", buf[0]);
+ lun = buf[0] & 7;
+ datalen = scsi_send_command(s->current_dev, 0, &buf[1], lun);
+ if (datalen == 0) {
+ s->ti_size = 0;
+ } else {
+ s->rregs[4] = STAT_IN | STAT_TC;
+ if (datalen > 0) {
+ s->rregs[4] |= STAT_DI;
+ s->ti_size = datalen;
+ } else {
+ s->rregs[4] |= STAT_DO;
+ s->ti_size = -datalen;
+ }
+ }
+ s->rregs[5] = INTR_BS | INTR_FC;
+ s->rregs[6] = SEQ_CD;
+ s->espdmaregs[0] |= DMA_INTR;
+ pic_set_irq(s->irq, 1);
+}
+
+static void handle_satn(ESPState *s)
+{
+ uint8_t buf[32];
+ int len;
+
+ len = get_cmd(s, buf);
+ if (len)
+ do_cmd(s, buf);
+}
+
+static void handle_satn_stop(ESPState *s)
+{
+ s->cmdlen = get_cmd(s, s->cmdbuf);
+ if (s->cmdlen) {
+ DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
+ s->do_cmd = 1;
+ s->espdmaregs[1] += s->cmdlen;
+ s->rregs[4] = STAT_IN | STAT_TC | STAT_CD;
+ s->rregs[5] = INTR_BS | INTR_FC;
+ s->rregs[6] = SEQ_CD;
+ s->espdmaregs[0] |= DMA_INTR;
+ pic_set_irq(s->irq, 1);
+ }
+}
+
+static void write_response(ESPState *s)
+{
+ uint32_t dmaptr;
+
+ DPRINTF("Transfer status (sense=%d)\n", s->sense);
+ s->ti_buf[0] = s->sense;
+ s->ti_buf[1] = 0;
+ if (s->dma) {
+ dmaptr = iommu_translate(s->espdmaregs[1]);
+ DPRINTF("DMA Direction: %c\n",
+ s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
+ cpu_physical_memory_write(dmaptr, s->ti_buf, 2);
+ s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
+ s->rregs[5] = INTR_BS | INTR_FC;
+ s->rregs[6] = SEQ_CD;
+ } else {
+ s->ti_size = 2;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ s->rregs[7] = 2;
+ }
+ s->espdmaregs[0] |= DMA_INTR;
+ pic_set_irq(s->irq, 1);
+
+}
+
+static void esp_command_complete(void *opaque, uint32_t tag, int sense)
+{
+ ESPState *s = (ESPState *)opaque;
+
+ DPRINTF("SCSI Command complete\n");
+ if (s->ti_size != 0)
+ DPRINTF("SCSI command completed unexpectedly\n");
+ s->ti_size = 0;
+ if (sense)
+ DPRINTF("Command failed\n");
+ s->sense = sense;
+ s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
+}
+
+static void handle_ti(ESPState *s)
+{
+ uint32_t dmaptr, dmalen, minlen, len, from, to;
+ unsigned int i;
+ int to_device;
+ uint8_t buf[TARGET_PAGE_SIZE];
+
+ dmalen = s->wregs[0] | (s->wregs[1] << 8);
+ if (dmalen==0) {
+ dmalen=0x10000;
+ }
+
+ if (s->do_cmd)
+ minlen = (dmalen < 32) ? dmalen : 32;
+ else
+ minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
+ DPRINTF("Transfer Information len %d\n", minlen);
+ if (s->dma) {
+ dmaptr = iommu_translate(s->espdmaregs[1]);
+ /* Check if the transfer writes to to reads from the device. */
+ to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
+ DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
+ to_device ? 'r': 'w', dmaptr, s->ti_size);
+ from = s->espdmaregs[1];
+ to = from + minlen;
+ for (i = 0; i < minlen; i += len, from += len) {
+ dmaptr = iommu_translate(s->espdmaregs[1] + i);
+ if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
+ len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
+ } else {
+ len = to - from;
+ }
+ DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
+ s->ti_size -= len;
+ if (s->do_cmd) {
+ DPRINTF("command len %d + %d\n", s->cmdlen, len);
+ cpu_physical_memory_read(dmaptr, &s->cmdbuf[s->cmdlen], len);
+ s->ti_size = 0;
+ s->cmdlen = 0;
+ s->do_cmd = 0;
+ do_cmd(s, s->cmdbuf);
+ return;
+ } else {
+ if (to_device) {
+ cpu_physical_memory_read(dmaptr, buf, len);
+ scsi_write_data(s->current_dev, buf, len);
+ } else {
+ scsi_read_data(s->current_dev, buf, len);
+ cpu_physical_memory_write(dmaptr, buf, len);
+ }
+ }
+ }
+ if (s->ti_size) {
+ s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
+ }
+ s->rregs[5] = INTR_BS;
+ s->rregs[6] = 0;
+ s->rregs[7] = 0;
+ s->espdmaregs[0] |= DMA_INTR;
+ } else if (s->do_cmd) {
+ DPRINTF("command len %d\n", s->cmdlen);
+ s->ti_size = 0;
+ s->cmdlen = 0;
+ s->do_cmd = 0;
+ do_cmd(s, s->cmdbuf);
+ return;
+ }
+ pic_set_irq(s->irq, 1);
+}
+
+static void esp_reset(void *opaque)
+{
+ ESPState *s = opaque;
+ memset(s->rregs, 0, ESP_MAXREG);
+ memset(s->wregs, 0, ESP_MAXREG);
+ s->rregs[0x0e] = 0x4; // Indicate fas100a
+ memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
+ s->ti_size = 0;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ s->dma = 0;
+ s->do_cmd = 0;
+}
+
+static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ ESPState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & ESP_MAXREG) >> 2;
+ DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
+ switch (saddr) {
+ case 2:
+ // FIFO
+ if (s->ti_size > 0) {
+ s->ti_size--;
+ if ((s->rregs[4] & 6) == 0) {
+ /* Data in/out. */
+ scsi_read_data(s->current_dev, &s->rregs[2], 0);
+ } else {
+ s->rregs[2] = s->ti_buf[s->ti_rptr++];
+ }
+ pic_set_irq(s->irq, 1);
+ }
+ if (s->ti_size == 0) {
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ }
+ break;
+ case 5:
+ // interrupt
+ // Clear status bits except TC
+ s->rregs[4] &= STAT_TC;
+ pic_set_irq(s->irq, 0);
+ s->espdmaregs[0] &= ~DMA_INTR;
+ break;
+ default:
+ break;
+ }
+ return s->rregs[saddr];
+}
+
+static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ESPState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & ESP_MAXREG) >> 2;
+ DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
+ switch (saddr) {
+ case 0:
+ case 1:
+ s->rregs[saddr] = val;
+ break;
+ case 2:
+ // FIFO
+ if (s->do_cmd) {
+ s->cmdbuf[s->cmdlen++] = val & 0xff;
+ } else if ((s->rregs[4] & 6) == 0) {
+ uint8_t buf;
+ buf = val & 0xff;
+ s->ti_size--;
+ scsi_write_data(s->current_dev, &buf, 0);
+ } else {
+ s->ti_size++;
+ s->ti_buf[s->ti_wptr++] = val & 0xff;
+ }
+ break;
+ case 3:
+ s->rregs[saddr] = val;
+ // Command
+ if (val & 0x80) {
+ s->dma = 1;
+ } else {
+ s->dma = 0;
+ }
+ switch(val & 0x7f) {
+ case 0:
+ DPRINTF("NOP (%2.2x)\n", val);
+ break;
+ case 1:
+ DPRINTF("Flush FIFO (%2.2x)\n", val);
+ //s->ti_size = 0;
+ s->rregs[5] = INTR_FC;
+ s->rregs[6] = 0;
+ break;
+ case 2:
+ DPRINTF("Chip reset (%2.2x)\n", val);
+ esp_reset(s);
+ break;
+ case 3:
+ DPRINTF("Bus reset (%2.2x)\n", val);
+ s->rregs[5] = INTR_RST;
+ if (!(s->wregs[8] & 0x40)) {
+ s->espdmaregs[0] |= DMA_INTR;
+ pic_set_irq(s->irq, 1);
+ }
+ break;
+ case 0x10:
+ handle_ti(s);
+ break;
+ case 0x11:
+ DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
+ write_response(s);
+ break;
+ case 0x12:
+ DPRINTF("Message Accepted (%2.2x)\n", val);
+ write_response(s);
+ s->rregs[5] = INTR_DC;
+ s->rregs[6] = 0;
+ break;
+ case 0x1a:
+ DPRINTF("Set ATN (%2.2x)\n", val);
+ break;
+ case 0x42:
+ DPRINTF("Set ATN (%2.2x)\n", val);
+ handle_satn(s);
+ break;
+ case 0x43:
+ DPRINTF("Set ATN & stop (%2.2x)\n", val);
+ handle_satn_stop(s);
+ break;
+ default:
+ DPRINTF("Unhandled ESP command (%2.2x)\n", val);
+ break;
+ }
+ break;
+ case 4 ... 7:
+ break;
+ case 8:
+ s->rregs[saddr] = val;
+ break;
+ case 9 ... 10:
+ break;
+ case 11:
+ s->rregs[saddr] = val & 0x15;
+ break;
+ case 12 ... 15:
+ s->rregs[saddr] = val;
+ break;
+ default:
+ break;
+ }
+ s->wregs[saddr] = val;
+}
+
+static CPUReadMemoryFunc *esp_mem_read[3] = {
+ esp_mem_readb,
+ esp_mem_readb,
+ esp_mem_readb,
+};
+
+static CPUWriteMemoryFunc *esp_mem_write[3] = {
+ esp_mem_writeb,
+ esp_mem_writeb,
+ esp_mem_writeb,
+};
+
+static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ ESPState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & ESPDMA_MAXADDR) >> 2;
+ DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]);
+
+ return s->espdmaregs[saddr];
+}
+
+static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ESPState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & ESPDMA_MAXADDR) >> 2;
+ DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
+ switch (saddr) {
+ case 0:
+ if (!(val & DMA_INTREN))
+ pic_set_irq(s->irq, 0);
+ if (val & 0x80) {
+ esp_reset(s);
+ } else if (val & 0x40) {
+ val &= ~0x40;
+ } else if (val == 0)
+ val = 0x40;
+ val &= 0x0fffffff;
+ val |= DMA_VER;
+ break;
+ case 1:
+ s->espdmaregs[0] |= DMA_LOADED;
+ break;
+ default:
+ break;
+ }
+ s->espdmaregs[saddr] = val;
+}
+
+static CPUReadMemoryFunc *espdma_mem_read[3] = {
+ espdma_mem_readl,
+ espdma_mem_readl,
+ espdma_mem_readl,
+};
+
+static CPUWriteMemoryFunc *espdma_mem_write[3] = {
+ espdma_mem_writel,
+ espdma_mem_writel,
+ espdma_mem_writel,
+};
+
+static void esp_save(QEMUFile *f, void *opaque)
+{
+ ESPState *s = opaque;
+ unsigned int i;
+
+ qemu_put_buffer(f, s->rregs, ESP_MAXREG);
+ qemu_put_buffer(f, s->wregs, ESP_MAXREG);
+ qemu_put_be32s(f, &s->irq);
+ for (i = 0; i < ESPDMA_REGS; i++)
+ qemu_put_be32s(f, &s->espdmaregs[i]);
+ qemu_put_be32s(f, &s->ti_size);
+ qemu_put_be32s(f, &s->ti_rptr);
+ qemu_put_be32s(f, &s->ti_wptr);
+ qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
+ qemu_put_be32s(f, &s->dma);
+}
+
+static int esp_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ESPState *s = opaque;
+ unsigned int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_buffer(f, s->rregs, ESP_MAXREG);
+ qemu_get_buffer(f, s->wregs, ESP_MAXREG);
+ qemu_get_be32s(f, &s->irq);
+ for (i = 0; i < ESPDMA_REGS; i++)
+ qemu_get_be32s(f, &s->espdmaregs[i]);
+ qemu_get_be32s(f, &s->ti_size);
+ qemu_get_be32s(f, &s->ti_rptr);
+ qemu_get_be32s(f, &s->ti_wptr);
+ qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
+ qemu_get_be32s(f, &s->dma);
+
+ return 0;
+}
+
+void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
+{
+ ESPState *s;
+ int esp_io_memory, espdma_io_memory;
+ int i;
+
+ s = qemu_mallocz(sizeof(ESPState));
+ if (!s)
+ return;
+
+ s->bd = bd;
+ s->irq = irq;
+
+ esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
+ cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
+
+ espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
+ cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
+
+ esp_reset(s);
+
+ register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
+ qemu_register_reset(esp_reset, s);
+ for (i = 0; i < MAX_DISKS; i++) {
+ if (bs_table[i]) {
+ s->scsi_dev[i] =
+ scsi_disk_init(bs_table[i], esp_command_complete, s);
+ }
+ }
+}
+
diff --git a/hw/fdc.c b/hw/fdc.c
new file mode 100644
index 0000000..3890ace
--- /dev/null
+++ b/hw/fdc.c
@@ -0,0 +1,1757 @@
+/*
+ * QEMU Floppy disk emulator (Intel 82078)
+ *
+ * Copyright (c) 2003 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * The controller is used in Sun4m systems in a slightly different
+ * way. There are changes in DOR register and DMA is not available.
+ */
+#include "vl.h"
+
+/********************************************************/
+/* debug Floppy devices */
+//#define DEBUG_FLOPPY
+
+#ifdef DEBUG_FLOPPY
+#define FLOPPY_DPRINTF(fmt, args...) \
+do { printf("FLOPPY: " fmt , ##args); } while (0)
+#else
+#define FLOPPY_DPRINTF(fmt, args...)
+#endif
+
+#define FLOPPY_ERROR(fmt, args...) \
+do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)
+
+/********************************************************/
+/* Floppy drive emulation */
+
+/* Will always be a fixed parameter for us */
+#define FD_SECTOR_LEN 512
+#define FD_SECTOR_SC 2 /* Sector size code */
+
+/* Floppy disk drive emulation */
+typedef enum fdisk_type_t {
+ FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */
+ FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */
+ FDRIVE_DISK_720 = 0x03, /* 720 kB disk */
+ FDRIVE_DISK_USER = 0x04, /* User defined geometry */
+ FDRIVE_DISK_NONE = 0x05, /* No disk */
+} fdisk_type_t;
+
+typedef enum fdrive_type_t {
+ FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
+ FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
+ FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
+ FDRIVE_DRV_NONE = 0x03, /* No drive connected */
+} fdrive_type_t;
+
+typedef enum fdrive_flags_t {
+ FDRIVE_MOTOR_ON = 0x01, /* motor on/off */
+ FDRIVE_REVALIDATE = 0x02, /* Revalidated */
+} fdrive_flags_t;
+
+typedef enum fdisk_flags_t {
+ FDISK_DBL_SIDES = 0x01,
+} fdisk_flags_t;
+
+typedef struct fdrive_t {
+ BlockDriverState *bs;
+ /* Drive status */
+ fdrive_type_t drive;
+ fdrive_flags_t drflags;
+ uint8_t perpendicular; /* 2.88 MB access mode */
+ /* Position */
+ uint8_t head;
+ uint8_t track;
+ uint8_t sect;
+ /* Last operation status */
+ uint8_t dir; /* Direction */
+ uint8_t rw; /* Read/write */
+ /* Media */
+ fdisk_flags_t flags;
+ uint8_t last_sect; /* Nb sector per track */
+ uint8_t max_track; /* Nb of tracks */
+ uint16_t bps; /* Bytes per sector */
+ uint8_t ro; /* Is read-only */
+} fdrive_t;
+
+static void fd_init (fdrive_t *drv, BlockDriverState *bs)
+{
+ /* Drive */
+ drv->bs = bs;
+ drv->drive = FDRIVE_DRV_NONE;
+ drv->drflags = 0;
+ drv->perpendicular = 0;
+ /* Disk */
+ drv->last_sect = 0;
+ drv->max_track = 0;
+}
+
+static int _fd_sector (uint8_t head, uint8_t track,
+ uint8_t sect, uint8_t last_sect)
+{
+ return (((track * 2) + head) * last_sect) + sect - 1;
+}
+
+/* Returns current position, in sectors, for given drive */
+static int fd_sector (fdrive_t *drv)
+{
+ return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);
+}
+
+static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
+ int enable_seek)
+{
+ uint32_t sector;
+ int ret;
+
+ if (track > drv->max_track ||
+ (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
+ FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+ head, track, sect, 1,
+ (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+ drv->max_track, drv->last_sect);
+ return 2;
+ }
+ if (sect > drv->last_sect) {
+ FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+ head, track, sect, 1,
+ (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+ drv->max_track, drv->last_sect);
+ return 3;
+ }
+ sector = _fd_sector(head, track, sect, drv->last_sect);
+ ret = 0;
+ if (sector != fd_sector(drv)) {
+#if 0
+ if (!enable_seek) {
+ FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
+ head, track, sect, 1, drv->max_track, drv->last_sect);
+ return 4;
+ }
+#endif
+ drv->head = head;
+ if (drv->track != track)
+ ret = 1;
+ drv->track = track;
+ drv->sect = sect;
+ }
+
+ return ret;
+}
+
+/* Set drive back to track 0 */
+static void fd_recalibrate (fdrive_t *drv)
+{
+ FLOPPY_DPRINTF("recalibrate\n");
+ drv->head = 0;
+ drv->track = 0;
+ drv->sect = 1;
+ drv->dir = 1;
+ drv->rw = 0;
+}
+
+/* Recognize floppy formats */
+typedef struct fd_format_t {
+ fdrive_type_t drive;
+ fdisk_type_t disk;
+ uint8_t last_sect;
+ uint8_t max_track;
+ uint8_t max_head;
+ const unsigned char *str;
+} fd_format_t;
+
+static fd_format_t fd_formats[] = {
+ /* First entry is default format */
+ /* 1.44 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
+ /* 2.88 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
+ /* 720 kB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
+ /* 1.2 MB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", },
+ /* 720 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", },
+ /* 360 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", },
+ /* 320 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", },
+ /* 360 kB must match 5"1/4 better than 3"1/2... */
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", },
+ /* end */
+ { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
+};
+
+/* Revalidate a disk drive after a disk change */
+static void fd_revalidate (fdrive_t *drv)
+{
+ fd_format_t *parse;
+ int64_t nb_sectors, size;
+ int i, first_match, match;
+ int nb_heads, max_track, last_sect, ro;
+
+ FLOPPY_DPRINTF("revalidate\n");
+ drv->drflags &= ~FDRIVE_REVALIDATE;
+ if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
+ ro = bdrv_is_read_only(drv->bs);
+ bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
+ if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
+ FLOPPY_DPRINTF("User defined disk (%d %d %d)",
+ nb_heads - 1, max_track, last_sect);
+ } else {
+ bdrv_get_geometry(drv->bs, &nb_sectors);
+ match = -1;
+ first_match = -1;
+ for (i = 0;; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FDRIVE_DRV_NONE)
+ break;
+ if (drv->drive == parse->drive ||
+ drv->drive == FDRIVE_DRV_NONE) {
+ size = (parse->max_head + 1) * parse->max_track *
+ parse->last_sect;
+ if (nb_sectors == size) {
+ match = i;
+ break;
+ }
+ if (first_match == -1)
+ first_match = i;
+ }
+ }
+ if (match == -1) {
+ if (first_match == -1)
+ match = 1;
+ else
+ match = first_match;
+ parse = &fd_formats[match];
+ }
+ nb_heads = parse->max_head + 1;
+ max_track = parse->max_track;
+ last_sect = parse->last_sect;
+ drv->drive = parse->drive;
+ FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
+ nb_heads, max_track, last_sect, ro ? "ro" : "rw");
+ }
+ if (nb_heads == 1) {
+ drv->flags &= ~FDISK_DBL_SIDES;
+ } else {
+ drv->flags |= FDISK_DBL_SIDES;
+ }
+ drv->max_track = max_track;
+ drv->last_sect = last_sect;
+ drv->ro = ro;
+ } else {
+ FLOPPY_DPRINTF("No disk in drive\n");
+ drv->last_sect = 0;
+ drv->max_track = 0;
+ drv->flags &= ~FDISK_DBL_SIDES;
+ }
+ drv->drflags |= FDRIVE_REVALIDATE;
+}
+
+/* Motor control */
+static void fd_start (fdrive_t *drv)
+{
+ drv->drflags |= FDRIVE_MOTOR_ON;
+}
+
+static void fd_stop (fdrive_t *drv)
+{
+ drv->drflags &= ~FDRIVE_MOTOR_ON;
+}
+
+/* Re-initialise a drives (motor off, repositioned) */
+static void fd_reset (fdrive_t *drv)
+{
+ fd_stop(drv);
+ fd_recalibrate(drv);
+}
+
+/********************************************************/
+/* Intel 82078 floppy disk controller emulation */
+
+static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
+static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+ int dma_pos, int dma_len);
+static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
+static void fdctrl_result_timer(void *opaque);
+
+static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl);
+static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl);
+static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl);
+static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl);
+static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_data (fdctrl_t *fdctrl);
+static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl);
+
+enum {
+ FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */
+ FD_CTRL_RESET = 0x02,
+ FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */
+ FD_CTRL_BUSY = 0x08, /* dma transfer in progress */
+ FD_CTRL_INTR = 0x10,
+};
+
+enum {
+ FD_DIR_WRITE = 0,
+ FD_DIR_READ = 1,
+ FD_DIR_SCANE = 2,
+ FD_DIR_SCANL = 3,
+ FD_DIR_SCANH = 4,
+};
+
+enum {
+ FD_STATE_CMD = 0x00,
+ FD_STATE_STATUS = 0x01,
+ FD_STATE_DATA = 0x02,
+ FD_STATE_STATE = 0x03,
+ FD_STATE_MULTI = 0x10,
+ FD_STATE_SEEK = 0x20,
+ FD_STATE_FORMAT = 0x40,
+};
+
+#define FD_STATE(state) ((state) & FD_STATE_STATE)
+#define FD_SET_STATE(state, new_state) \
+do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)
+#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
+#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
+#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
+
+struct fdctrl_t {
+ fdctrl_t *fdctrl;
+ /* Controller's identification */
+ uint8_t version;
+ /* HW */
+ int irq_lvl;
+ int dma_chann;
+ uint32_t io_base;
+ /* Controller state */
+ QEMUTimer *result_timer;
+ uint8_t state;
+ uint8_t dma_en;
+ uint8_t cur_drv;
+ uint8_t bootsel;
+ /* Command FIFO */
+ uint8_t fifo[FD_SECTOR_LEN];
+ uint32_t data_pos;
+ uint32_t data_len;
+ uint8_t data_state;
+ uint8_t data_dir;
+ uint8_t int_status;
+ uint8_t eot; /* last wanted sector */
+ /* States kept only to be returned back */
+ /* Timers state */
+ uint8_t timer0;
+ uint8_t timer1;
+ /* precompensation */
+ uint8_t precomp_trk;
+ uint8_t config;
+ uint8_t lock;
+ /* Power down config (also with status regB access mode */
+ uint8_t pwrd;
+ /* Floppy drives */
+ fdrive_t drives[2];
+};
+
+static uint32_t fdctrl_read (void *opaque, uint32_t reg)
+{
+ fdctrl_t *fdctrl = opaque;
+ uint32_t retval;
+
+ switch (reg & 0x07) {
+#ifdef TARGET_SPARC
+ case 0x00:
+ // Identify to Linux as S82078B
+ retval = fdctrl_read_statusB(fdctrl);
+ break;
+#endif
+ case 0x01:
+ retval = fdctrl_read_statusB(fdctrl);
+ break;
+ case 0x02:
+ retval = fdctrl_read_dor(fdctrl);
+ break;
+ case 0x03:
+ retval = fdctrl_read_tape(fdctrl);
+ break;
+ case 0x04:
+ retval = fdctrl_read_main_status(fdctrl);
+ break;
+ case 0x05:
+ retval = fdctrl_read_data(fdctrl);
+ break;
+ case 0x07:
+ retval = fdctrl_read_dir(fdctrl);
+ break;
+ default:
+ retval = (uint32_t)(-1);
+ break;
+ }
+ FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
+
+ return retval;
+}
+
+static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
+{
+ fdctrl_t *fdctrl = opaque;
+
+ FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
+
+ switch (reg & 0x07) {
+ case 0x02:
+ fdctrl_write_dor(fdctrl, value);
+ break;
+ case 0x03:
+ fdctrl_write_tape(fdctrl, value);
+ break;
+ case 0x04:
+ fdctrl_write_rate(fdctrl, value);
+ break;
+ case 0x05:
+ fdctrl_write_data(fdctrl, value);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
+{
+ return fdctrl_read(opaque, reg);
+}
+
+static void fdctrl_write_mem (void *opaque,
+ target_phys_addr_t reg, uint32_t value)
+{
+ fdctrl_write(opaque, reg, value);
+}
+
+static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
+ fdctrl_read_mem,
+ fdctrl_read_mem,
+ fdctrl_read_mem,
+};
+
+static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
+ fdctrl_write_mem,
+ fdctrl_write_mem,
+ fdctrl_write_mem,
+};
+
+static void fd_change_cb (void *opaque)
+{
+ fdrive_t *drv = opaque;
+
+ FLOPPY_DPRINTF("disk change\n");
+ fd_revalidate(drv);
+#if 0
+ fd_recalibrate(drv);
+ fdctrl_reset_fifo(drv->fdctrl);
+ fdctrl_raise_irq(drv->fdctrl, 0x20);
+#endif
+}
+
+fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
+ uint32_t io_base,
+ BlockDriverState **fds)
+{
+ fdctrl_t *fdctrl;
+ int io_mem;
+ int i;
+
+ FLOPPY_DPRINTF("init controller\n");
+ fdctrl = qemu_mallocz(sizeof(fdctrl_t));
+ if (!fdctrl)
+ return NULL;
+ fdctrl->result_timer = qemu_new_timer(vm_clock,
+ fdctrl_result_timer, fdctrl);
+
+ fdctrl->version = 0x90; /* Intel 82078 controller */
+ fdctrl->irq_lvl = irq_lvl;
+ fdctrl->dma_chann = dma_chann;
+ fdctrl->io_base = io_base;
+ fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
+ if (fdctrl->dma_chann != -1) {
+ fdctrl->dma_en = 1;
+ DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
+ } else {
+ fdctrl->dma_en = 0;
+ }
+ for (i = 0; i < 2; i++) {
+ fd_init(&fdctrl->drives[i], fds[i]);
+ if (fds[i]) {
+ bdrv_set_change_cb(fds[i],
+ &fd_change_cb, &fdctrl->drives[i]);
+ }
+ }
+ fdctrl_reset(fdctrl, 0);
+ fdctrl->state = FD_CTRL_ACTIVE;
+ if (mem_mapped) {
+ io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl);
+ cpu_register_physical_memory(io_base, 0x08, io_mem);
+ } else {
+ register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl);
+ register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl);
+ register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl);
+ register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl);
+ }
+ for (i = 0; i < 2; i++) {
+ fd_revalidate(&fdctrl->drives[i]);
+ }
+
+ return fdctrl;
+}
+
+/* XXX: may change if moved to bdrv */
+int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
+{
+ return fdctrl->drives[drive_num].drive;
+}
+
+/* Change IRQ state */
+static void fdctrl_reset_irq (fdctrl_t *fdctrl)
+{
+ FLOPPY_DPRINTF("Reset interrupt\n");
+ pic_set_irq(fdctrl->irq_lvl, 0);
+ fdctrl->state &= ~FD_CTRL_INTR;
+}
+
+static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status)
+{
+#ifdef TARGET_SPARC
+ // Sparc mutation
+ if (!fdctrl->dma_en) {
+ fdctrl->state &= ~FD_CTRL_BUSY;
+ fdctrl->int_status = status;
+ return;
+ }
+#endif
+ if (~(fdctrl->state & FD_CTRL_INTR)) {
+ pic_set_irq(fdctrl->irq_lvl, 1);
+ fdctrl->state |= FD_CTRL_INTR;
+ }
+ FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
+ fdctrl->int_status = status;
+}
+
+/* Reset controller */
+static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
+{
+ int i;
+
+ FLOPPY_DPRINTF("reset controller\n");
+ fdctrl_reset_irq(fdctrl);
+ /* Initialise controller */
+ fdctrl->cur_drv = 0;
+ /* FIFO state */
+ fdctrl->data_pos = 0;
+ fdctrl->data_len = 0;
+ fdctrl->data_state = FD_STATE_CMD;
+ fdctrl->data_dir = FD_DIR_WRITE;
+ for (i = 0; i < MAX_FD; i++)
+ fd_reset(&fdctrl->drives[i]);
+ fdctrl_reset_fifo(fdctrl);
+ if (do_irq)
+ fdctrl_raise_irq(fdctrl, 0xc0);
+}
+
+static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
+{
+ return &fdctrl->drives[fdctrl->bootsel];
+}
+
+static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
+{
+ return &fdctrl->drives[1 - fdctrl->bootsel];
+}
+
+static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
+{
+ return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl);
+}
+
+/* Status B register : 0x01 (read-only) */
+static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
+{
+ FLOPPY_DPRINTF("status register: 0x00\n");
+ return 0;
+}
+
+/* Digital output register : 0x02 */
+static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
+{
+ uint32_t retval = 0;
+
+ /* Drive motors state indicators */
+ if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
+ retval |= 1 << 5;
+ if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
+ retval |= 1 << 4;
+ /* DMA enable */
+ retval |= fdctrl->dma_en << 3;
+ /* Reset indicator */
+ retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
+ /* Selected drive */
+ retval |= fdctrl->cur_drv;
+ FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (fdctrl->state & FD_CTRL_RESET) {
+ if (!(value & 0x04)) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ }
+ FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
+ /* Drive motors state indicators */
+ if (value & 0x20)
+ fd_start(drv1(fdctrl));
+ else
+ fd_stop(drv1(fdctrl));
+ if (value & 0x10)
+ fd_start(drv0(fdctrl));
+ else
+ fd_stop(drv0(fdctrl));
+ /* DMA enable */
+#if 0
+ if (fdctrl->dma_chann != -1)
+ fdctrl->dma_en = 1 - ((value >> 3) & 1);
+#endif
+ /* Reset */
+ if (!(value & 0x04)) {
+ if (!(fdctrl->state & FD_CTRL_RESET)) {
+ FLOPPY_DPRINTF("controller enter RESET state\n");
+ fdctrl->state |= FD_CTRL_RESET;
+ }
+ } else {
+ if (fdctrl->state & FD_CTRL_RESET) {
+ FLOPPY_DPRINTF("controller out of RESET state\n");
+ fdctrl_reset(fdctrl, 1);
+ fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
+ }
+ }
+ /* Selected drive */
+ fdctrl->cur_drv = value & 1;
+}
+
+/* Tape drive register : 0x03 */
+static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
+{
+ uint32_t retval = 0;
+
+ /* Disk boot selection indicator */
+ retval |= fdctrl->bootsel << 2;
+ /* Tape indicators: never allowed */
+ FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (fdctrl->state & FD_CTRL_RESET) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
+ /* Disk boot selection indicator */
+ fdctrl->bootsel = (value >> 2) & 1;
+ /* Tape indicators: never allow */
+}
+
+/* Main status register : 0x04 (read) */
+static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
+{
+ uint32_t retval = 0;
+
+ fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
+ if (!(fdctrl->state & FD_CTRL_BUSY)) {
+ /* Data transfer allowed */
+ retval |= 0x80;
+ /* Data transfer direction indicator */
+ if (fdctrl->data_dir == FD_DIR_READ)
+ retval |= 0x40;
+ }
+ /* Should handle 0x20 for SPECIFY command */
+ /* Command busy indicator */
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
+ FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
+ retval |= 0x10;
+ FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+/* Data select rate register : 0x04 (write) */
+static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (fdctrl->state & FD_CTRL_RESET) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
+ /* Reset: autoclear */
+ if (value & 0x80) {
+ fdctrl->state |= FD_CTRL_RESET;
+ fdctrl_reset(fdctrl, 1);
+ fdctrl->state &= ~FD_CTRL_RESET;
+ }
+ if (value & 0x40) {
+ fdctrl->state |= FD_CTRL_SLEEP;
+ fdctrl_reset(fdctrl, 1);
+ }
+// fdctrl.precomp = (value >> 2) & 0x07;
+}
+
+/* Digital input register : 0x07 (read-only) */
+static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
+{
+ uint32_t retval = 0;
+
+ if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
+ drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
+ retval |= 0x80;
+ if (retval != 0)
+ FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
+ drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
+ drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
+
+ return retval;
+}
+
+/* FIFO state control */
+static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
+{
+ fdctrl->data_dir = FD_DIR_WRITE;
+ fdctrl->data_pos = 0;
+ FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD);
+}
+
+/* Set FIFO status for the host to read */
+static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
+{
+ fdctrl->data_dir = FD_DIR_READ;
+ fdctrl->data_len = fifo_len;
+ fdctrl->data_pos = 0;
+ FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS);
+ if (do_irq)
+ fdctrl_raise_irq(fdctrl, 0x00);
+}
+
+/* Set an error: unimplemented/unknown command */
+static void fdctrl_unimplemented (fdctrl_t *fdctrl)
+{
+#if 0
+ fdrive_t *cur_drv;
+
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv;
+ fdctrl->fifo[1] = 0x00;
+ fdctrl->fifo[2] = 0x00;
+ fdctrl_set_fifo(fdctrl, 3, 1);
+#else
+ // fdctrl_reset_fifo(fdctrl);
+ fdctrl->fifo[0] = 0x80;
+ fdctrl_set_fifo(fdctrl, 1, 0);
+#endif
+}
+
+/* Callback for transfer end (stop or abort) */
+static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
+ uint8_t status1, uint8_t status2)
+{
+ fdrive_t *cur_drv;
+
+ cur_drv = get_cur_drv(fdctrl);
+ FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
+ status0, status1, status2,
+ status0 | (cur_drv->head << 2) | fdctrl->cur_drv);
+ fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv;
+ fdctrl->fifo[1] = status1;
+ fdctrl->fifo[2] = status2;
+ fdctrl->fifo[3] = cur_drv->track;
+ fdctrl->fifo[4] = cur_drv->head;
+ fdctrl->fifo[5] = cur_drv->sect;
+ fdctrl->fifo[6] = FD_SECTOR_SC;
+ fdctrl->data_dir = FD_DIR_READ;
+ if (fdctrl->state & FD_CTRL_BUSY) {
+ DMA_release_DREQ(fdctrl->dma_chann);
+ fdctrl->state &= ~FD_CTRL_BUSY;
+ }
+ fdctrl_set_fifo(fdctrl, 7, 1);
+}
+
+/* Prepare a data transfer (either DMA or FIFO) */
+static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
+{
+ fdrive_t *cur_drv;
+ uint8_t kh, kt, ks;
+ int did_seek;
+
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ kt = fdctrl->fifo[2];
+ kh = fdctrl->fifo[3];
+ ks = fdctrl->fifo[4];
+ FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
+ fdctrl->cur_drv, kh, kt, ks,
+ _fd_sector(kh, kt, ks, cur_drv->last_sect));
+ did_seek = 0;
+ switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
+ case 2:
+ /* sect too big */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 3:
+ /* track too big */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 4:
+ /* No seek enabled */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 1:
+ did_seek = 1;
+ break;
+ default:
+ break;
+ }
+ /* Set the FIFO state */
+ fdctrl->data_dir = direction;
+ fdctrl->data_pos = 0;
+ FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */
+ if (fdctrl->fifo[0] & 0x80)
+ fdctrl->data_state |= FD_STATE_MULTI;
+ else
+ fdctrl->data_state &= ~FD_STATE_MULTI;
+ if (did_seek)
+ fdctrl->data_state |= FD_STATE_SEEK;
+ else
+ fdctrl->data_state &= ~FD_STATE_SEEK;
+ if (fdctrl->fifo[5] == 00) {
+ fdctrl->data_len = fdctrl->fifo[8];
+ } else {
+ int tmp;
+ fdctrl->data_len = 128 << fdctrl->fifo[5];
+ tmp = (cur_drv->last_sect - ks + 1);
+ if (fdctrl->fifo[0] & 0x80)
+ tmp += cur_drv->last_sect;
+ fdctrl->data_len *= tmp;
+ }
+ fdctrl->eot = fdctrl->fifo[6];
+ if (fdctrl->dma_en) {
+ int dma_mode;
+ /* DMA transfer are enabled. Check if DMA channel is well programmed */
+ dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
+ dma_mode = (dma_mode >> 2) & 3;
+ FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
+ dma_mode, direction,
+ (128 << fdctrl->fifo[5]) *
+ (cur_drv->last_sect - ks + 1), fdctrl->data_len);
+ if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
+ direction == FD_DIR_SCANH) && dma_mode == 0) ||
+ (direction == FD_DIR_WRITE && dma_mode == 2) ||
+ (direction == FD_DIR_READ && dma_mode == 1)) {
+ /* No access is allowed until DMA transfer has completed */
+ fdctrl->state |= FD_CTRL_BUSY;
+ /* Now, we just have to wait for the DMA controller to
+ * recall us...
+ */
+ DMA_hold_DREQ(fdctrl->dma_chann);
+ DMA_schedule(fdctrl->dma_chann);
+ return;
+ } else {
+ FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
+ }
+ }
+ FLOPPY_DPRINTF("start non-DMA transfer\n");
+ /* IO based transfer: calculate len */
+ fdctrl_raise_irq(fdctrl, 0x00);
+
+ return;
+}
+
+/* Prepare a transfer of deleted data */
+static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
+{
+ /* We don't handle deleted data,
+ * so we don't return *ANYTHING*
+ */
+ fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
+}
+
+/* handlers for DMA transfers */
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+ int dma_pos, int dma_len)
+{
+ fdctrl_t *fdctrl;
+ fdrive_t *cur_drv;
+ int len, start_pos, rel_pos;
+ uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
+
+ fdctrl = opaque;
+ if (!(fdctrl->state & FD_CTRL_BUSY)) {
+ FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
+ return 0;
+ }
+ cur_drv = get_cur_drv(fdctrl);
+ if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
+ fdctrl->data_dir == FD_DIR_SCANH)
+ status2 = 0x04;
+ if (dma_len > fdctrl->data_len)
+ dma_len = fdctrl->data_len;
+ if (cur_drv->bs == NULL) {
+ if (fdctrl->data_dir == FD_DIR_WRITE)
+ fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
+ else
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
+ len = 0;
+ goto transfer_error;
+ }
+ rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
+ len = dma_len - fdctrl->data_pos;
+ if (len + rel_pos > FD_SECTOR_LEN)
+ len = FD_SECTOR_LEN - rel_pos;
+ FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
+ "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
+ fdctrl->data_len, fdctrl->cur_drv, cur_drv->head,
+ cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
+ fd_sector(cur_drv) * 512);
+ if (fdctrl->data_dir != FD_DIR_WRITE ||
+ len < FD_SECTOR_LEN || rel_pos != 0) {
+ /* READ & SCAN commands and realign to a sector for WRITE */
+ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, 1) < 0) {
+ FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
+ fd_sector(cur_drv));
+ /* Sure, image size is too small... */
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ }
+ }
+ switch (fdctrl->data_dir) {
+ case FD_DIR_READ:
+ /* READ commands */
+ DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
+/* cpu_physical_memory_write(addr + fdctrl->data_pos, */
+/* fdctrl->fifo + rel_pos, len); */
+ break;
+ case FD_DIR_WRITE:
+ /* WRITE commands */
+ DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
+/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
+/* fdctrl->fifo + rel_pos, len); */
+ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, 1) < 0) {
+ FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
+ fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
+ goto transfer_error;
+ }
+ break;
+ default:
+ /* SCAN commands */
+ {
+ uint8_t tmpbuf[FD_SECTOR_LEN];
+ int ret;
+ DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
+/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
+/* tmpbuf, len); */
+ ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
+ if (ret == 0) {
+ status2 = 0x08;
+ goto end_transfer;
+ }
+ if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
+ (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
+ status2 = 0x00;
+ goto end_transfer;
+ }
+ }
+ break;
+ }
+ fdctrl->data_pos += len;
+ rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ if (rel_pos == 0) {
+ /* Seek to next sector */
+ FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
+ cur_drv->head, cur_drv->track, cur_drv->sect,
+ fd_sector(cur_drv),
+ fdctrl->data_pos - len);
+ /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
+ error in fact */
+ if (cur_drv->sect >= cur_drv->last_sect ||
+ cur_drv->sect == fdctrl->eot) {
+ cur_drv->sect = 1;
+ if (FD_MULTI_TRACK(fdctrl->data_state)) {
+ if (cur_drv->head == 0 &&
+ (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
+ cur_drv->head = 1;
+ } else {
+ cur_drv->head = 0;
+ cur_drv->track++;
+ if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
+ break;
+ }
+ } else {
+ cur_drv->track++;
+ break;
+ }
+ FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
+ cur_drv->head, cur_drv->track,
+ cur_drv->sect, fd_sector(cur_drv));
+ } else {
+ cur_drv->sect++;
+ }
+ }
+ }
+end_transfer:
+ len = fdctrl->data_pos - start_pos;
+ FLOPPY_DPRINTF("end transfer %d %d %d\n",
+ fdctrl->data_pos, len, fdctrl->data_len);
+ if (fdctrl->data_dir == FD_DIR_SCANE ||
+ fdctrl->data_dir == FD_DIR_SCANL ||
+ fdctrl->data_dir == FD_DIR_SCANH)
+ status2 = 0x08;
+ if (FD_DID_SEEK(fdctrl->data_state))
+ status0 |= 0x20;
+ fdctrl->data_len -= len;
+ // if (fdctrl->data_len == 0)
+ fdctrl_stop_transfer(fdctrl, status0, status1, status2);
+transfer_error:
+
+ return len;
+}
+
+/* Data register : 0x05 */
+static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
+{
+ fdrive_t *cur_drv;
+ uint32_t retval = 0;
+ int pos, len;
+
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl->state &= ~FD_CTRL_SLEEP;
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) {
+ FLOPPY_ERROR("can't read data in CMD state\n");
+ return 0;
+ }
+ pos = fdctrl->data_pos;
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
+ pos %= FD_SECTOR_LEN;
+ if (pos == 0) {
+ len = fdctrl->data_len - fdctrl->data_pos;
+ if (len > FD_SECTOR_LEN)
+ len = FD_SECTOR_LEN;
+ bdrv_read(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, len);
+ }
+ }
+ retval = fdctrl->fifo[pos];
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->data_pos = 0;
+ /* Switch from transfer mode to status mode
+ * then from status mode to command mode
+ */
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
+ fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
+ } else {
+ fdctrl_reset_fifo(fdctrl);
+ fdctrl_reset_irq(fdctrl);
+ }
+ }
+ FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_format_sector (fdctrl_t *fdctrl)
+{
+ fdrive_t *cur_drv;
+ uint8_t kh, kt, ks;
+ int did_seek;
+
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ kt = fdctrl->fifo[6];
+ kh = fdctrl->fifo[7];
+ ks = fdctrl->fifo[8];
+ FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
+ fdctrl->cur_drv, kh, kt, ks,
+ _fd_sector(kh, kt, ks, cur_drv->last_sect));
+ did_seek = 0;
+ switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
+ case 2:
+ /* sect too big */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 3:
+ /* track too big */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 4:
+ /* No seek enabled */
+ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 1:
+ did_seek = 1;
+ fdctrl->data_state |= FD_STATE_SEEK;
+ break;
+ default:
+ break;
+ }
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ if (cur_drv->bs == NULL ||
+ bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv));
+ fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
+ } else {
+ if (cur_drv->sect == cur_drv->last_sect) {
+ fdctrl->data_state &= ~FD_STATE_FORMAT;
+ /* Last sector done */
+ if (FD_DID_SEEK(fdctrl->data_state))
+ fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
+ else
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ } else {
+ /* More to do */
+ fdctrl->data_pos = 0;
+ fdctrl->data_len = 4;
+ }
+ }
+}
+
+static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
+{
+ fdrive_t *cur_drv;
+
+ cur_drv = get_cur_drv(fdctrl);
+ /* Reset mode */
+ if (fdctrl->state & FD_CTRL_RESET) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ fdctrl->state &= ~FD_CTRL_SLEEP;
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) {
+ FLOPPY_ERROR("can't write data in status mode\n");
+ return;
+ }
+ /* Is it write command time ? */
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
+ /* FIFO data write */
+ fdctrl->fifo[fdctrl->data_pos++] = value;
+ if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
+ fdctrl->data_pos == fdctrl->data_len) {
+ bdrv_write(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, FD_SECTOR_LEN);
+ }
+ /* Switch from transfer mode to status mode
+ * then from status mode to command mode
+ */
+ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA)
+ fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
+ return;
+ }
+ if (fdctrl->data_pos == 0) {
+ /* Command */
+ switch (value & 0x5F) {
+ case 0x46:
+ /* READ variants */
+ FLOPPY_DPRINTF("READ command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x4C:
+ /* READ_DELETED variants */
+ FLOPPY_DPRINTF("READ_DELETED command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x50:
+ /* SCAN_EQUAL variants */
+ FLOPPY_DPRINTF("SCAN_EQUAL command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x56:
+ /* VERIFY variants */
+ FLOPPY_DPRINTF("VERIFY command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x59:
+ /* SCAN_LOW_OR_EQUAL variants */
+ FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x5D:
+ /* SCAN_HIGH_OR_EQUAL variants */
+ FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ default:
+ break;
+ }
+ switch (value & 0x7F) {
+ case 0x45:
+ /* WRITE variants */
+ FLOPPY_DPRINTF("WRITE command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x49:
+ /* WRITE_DELETED variants */
+ FLOPPY_DPRINTF("WRITE_DELETED command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ default:
+ break;
+ }
+ switch (value) {
+ case 0x03:
+ /* SPECIFY */
+ FLOPPY_DPRINTF("SPECIFY command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 3;
+ goto enqueue;
+ case 0x04:
+ /* SENSE_DRIVE_STATUS */
+ FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 2;
+ goto enqueue;
+ case 0x07:
+ /* RECALIBRATE */
+ FLOPPY_DPRINTF("RECALIBRATE command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 2;
+ goto enqueue;
+ case 0x08:
+ /* SENSE_INTERRUPT_STATUS */
+ FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n",
+ fdctrl->int_status);
+ /* No parameters cmd: returns status if no interrupt */
+#if 0
+ fdctrl->fifo[0] =
+ fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv;
+#else
+ /* XXX: int_status handling is broken for read/write
+ commands, so we do this hack. It should be suppressed
+ ASAP */
+ fdctrl->fifo[0] =
+ 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv;
+#endif
+ fdctrl->fifo[1] = cur_drv->track;
+ fdctrl_set_fifo(fdctrl, 2, 0);
+ fdctrl_reset_irq(fdctrl);
+ fdctrl->int_status = 0xC0;
+ return;
+ case 0x0E:
+ /* DUMPREG */
+ FLOPPY_DPRINTF("DUMPREG command\n");
+ /* Drives position */
+ fdctrl->fifo[0] = drv0(fdctrl)->track;
+ fdctrl->fifo[1] = drv1(fdctrl)->track;
+ fdctrl->fifo[2] = 0;
+ fdctrl->fifo[3] = 0;
+ /* timers */
+ fdctrl->fifo[4] = fdctrl->timer0;
+ fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en;
+ fdctrl->fifo[6] = cur_drv->last_sect;
+ fdctrl->fifo[7] = (fdctrl->lock << 7) |
+ (cur_drv->perpendicular << 2);
+ fdctrl->fifo[8] = fdctrl->config;
+ fdctrl->fifo[9] = fdctrl->precomp_trk;
+ fdctrl_set_fifo(fdctrl, 10, 0);
+ return;
+ case 0x0F:
+ /* SEEK */
+ FLOPPY_DPRINTF("SEEK command\n");
+ /* 2 parameters cmd */
+ fdctrl->data_len = 3;
+ goto enqueue;
+ case 0x10:
+ /* VERSION */
+ FLOPPY_DPRINTF("VERSION command\n");
+ /* No parameters cmd */
+ /* Controller's version */
+ fdctrl->fifo[0] = fdctrl->version;
+ fdctrl_set_fifo(fdctrl, 1, 1);
+ return;
+ case 0x12:
+ /* PERPENDICULAR_MODE */
+ FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 2;
+ goto enqueue;
+ case 0x13:
+ /* CONFIGURE */
+ FLOPPY_DPRINTF("CONFIGURE command\n");
+ /* 3 parameters cmd */
+ fdctrl->data_len = 4;
+ goto enqueue;
+ case 0x14:
+ /* UNLOCK */
+ FLOPPY_DPRINTF("UNLOCK command\n");
+ /* No parameters cmd */
+ fdctrl->lock = 0;
+ fdctrl->fifo[0] = 0;
+ fdctrl_set_fifo(fdctrl, 1, 0);
+ return;
+ case 0x17:
+ /* POWERDOWN_MODE */
+ FLOPPY_DPRINTF("POWERDOWN_MODE command\n");
+ /* 2 parameters cmd */
+ fdctrl->data_len = 3;
+ goto enqueue;
+ case 0x18:
+ /* PART_ID */
+ FLOPPY_DPRINTF("PART_ID command\n");
+ /* No parameters cmd */
+ fdctrl->fifo[0] = 0x41; /* Stepping 1 */
+ fdctrl_set_fifo(fdctrl, 1, 0);
+ return;
+ case 0x2C:
+ /* SAVE */
+ FLOPPY_DPRINTF("SAVE command\n");
+ /* No parameters cmd */
+ fdctrl->fifo[0] = 0;
+ fdctrl->fifo[1] = 0;
+ /* Drives position */
+ fdctrl->fifo[2] = drv0(fdctrl)->track;
+ fdctrl->fifo[3] = drv1(fdctrl)->track;
+ fdctrl->fifo[4] = 0;
+ fdctrl->fifo[5] = 0;
+ /* timers */
+ fdctrl->fifo[6] = fdctrl->timer0;
+ fdctrl->fifo[7] = fdctrl->timer1;
+ fdctrl->fifo[8] = cur_drv->last_sect;
+ fdctrl->fifo[9] = (fdctrl->lock << 7) |
+ (cur_drv->perpendicular << 2);
+ fdctrl->fifo[10] = fdctrl->config;
+ fdctrl->fifo[11] = fdctrl->precomp_trk;
+ fdctrl->fifo[12] = fdctrl->pwrd;
+ fdctrl->fifo[13] = 0;
+ fdctrl->fifo[14] = 0;
+ fdctrl_set_fifo(fdctrl, 15, 1);
+ return;
+ case 0x33:
+ /* OPTION */
+ FLOPPY_DPRINTF("OPTION command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 2;
+ goto enqueue;
+ case 0x42:
+ /* READ_TRACK */
+ FLOPPY_DPRINTF("READ_TRACK command\n");
+ /* 8 parameters cmd */
+ fdctrl->data_len = 9;
+ goto enqueue;
+ case 0x4A:
+ /* READ_ID */
+ FLOPPY_DPRINTF("READ_ID command\n");
+ /* 1 parameter cmd */
+ fdctrl->data_len = 2;
+ goto enqueue;
+ case 0x4C:
+ /* RESTORE */
+ FLOPPY_DPRINTF("RESTORE command\n");
+ /* 17 parameters cmd */
+ fdctrl->data_len = 18;
+ goto enqueue;
+ case 0x4D:
+ /* FORMAT_TRACK */
+ FLOPPY_DPRINTF("FORMAT_TRACK command\n");
+ /* 5 parameters cmd */
+ fdctrl->data_len = 6;
+ goto enqueue;
+ case 0x8E:
+ /* DRIVE_SPECIFICATION_COMMAND */
+ FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n");
+ /* 5 parameters cmd */
+ fdctrl->data_len = 6;
+ goto enqueue;
+ case 0x8F:
+ /* RELATIVE_SEEK_OUT */
+ FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n");
+ /* 2 parameters cmd */
+ fdctrl->data_len = 3;
+ goto enqueue;
+ case 0x94:
+ /* LOCK */
+ FLOPPY_DPRINTF("LOCK command\n");
+ /* No parameters cmd */
+ fdctrl->lock = 1;
+ fdctrl->fifo[0] = 0x10;
+ fdctrl_set_fifo(fdctrl, 1, 1);
+ return;
+ case 0xCD:
+ /* FORMAT_AND_WRITE */
+ FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n");
+ /* 10 parameters cmd */
+ fdctrl->data_len = 11;
+ goto enqueue;
+ case 0xCF:
+ /* RELATIVE_SEEK_IN */
+ FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n");
+ /* 2 parameters cmd */
+ fdctrl->data_len = 3;
+ goto enqueue;
+ default:
+ /* Unknown command */
+ FLOPPY_ERROR("unknown command: 0x%02x\n", value);
+ fdctrl_unimplemented(fdctrl);
+ return;
+ }
+ }
+enqueue:
+ FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
+ fdctrl->fifo[fdctrl->data_pos] = value;
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ /* We now have all parameters
+ * and will be able to treat the command
+ */
+ if (fdctrl->data_state & FD_STATE_FORMAT) {
+ fdctrl_format_sector(fdctrl);
+ return;
+ }
+ switch (fdctrl->fifo[0] & 0x1F) {
+ case 0x06:
+ {
+ /* READ variants */
+ FLOPPY_DPRINTF("treat READ command\n");
+ fdctrl_start_transfer(fdctrl, FD_DIR_READ);
+ return;
+ }
+ case 0x0C:
+ /* READ_DELETED variants */
+// FLOPPY_DPRINTF("treat READ_DELETED command\n");
+ FLOPPY_ERROR("treat READ_DELETED command\n");
+ fdctrl_start_transfer_del(fdctrl, FD_DIR_READ);
+ return;
+ case 0x16:
+ /* VERIFY variants */
+// FLOPPY_DPRINTF("treat VERIFY command\n");
+ FLOPPY_ERROR("treat VERIFY command\n");
+ fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
+ return;
+ case 0x10:
+ /* SCAN_EQUAL variants */
+// FLOPPY_DPRINTF("treat SCAN_EQUAL command\n");
+ FLOPPY_ERROR("treat SCAN_EQUAL command\n");
+ fdctrl_start_transfer(fdctrl, FD_DIR_SCANE);
+ return;
+ case 0x19:
+ /* SCAN_LOW_OR_EQUAL variants */
+// FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n");
+ FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n");
+ fdctrl_start_transfer(fdctrl, FD_DIR_SCANL);
+ return;
+ case 0x1D:
+ /* SCAN_HIGH_OR_EQUAL variants */
+// FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n");
+ FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n");
+ fdctrl_start_transfer(fdctrl, FD_DIR_SCANH);
+ return;
+ default:
+ break;
+ }
+ switch (fdctrl->fifo[0] & 0x3F) {
+ case 0x05:
+ /* WRITE variants */
+ FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]);
+ fdctrl_start_transfer(fdctrl, FD_DIR_WRITE);
+ return;
+ case 0x09:
+ /* WRITE_DELETED variants */
+// FLOPPY_DPRINTF("treat WRITE_DELETED command\n");
+ FLOPPY_ERROR("treat WRITE_DELETED command\n");
+ fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE);
+ return;
+ default:
+ break;
+ }
+ switch (fdctrl->fifo[0]) {
+ case 0x03:
+ /* SPECIFY */
+ FLOPPY_DPRINTF("treat SPECIFY command\n");
+ fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
+ fdctrl->timer1 = fdctrl->fifo[2] >> 1;
+ fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ;
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+ break;
+ case 0x04:
+ /* SENSE_DRIVE_STATUS */
+ FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+ /* 1 Byte status back */
+ fdctrl->fifo[0] = (cur_drv->ro << 6) |
+ (cur_drv->track == 0 ? 0x10 : 0x00) |
+ (cur_drv->head << 2) |
+ fdctrl->cur_drv |
+ 0x28;
+ fdctrl_set_fifo(fdctrl, 1, 0);
+ break;
+ case 0x07:
+ /* RECALIBRATE */
+ FLOPPY_DPRINTF("treat RECALIBRATE command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ fd_recalibrate(cur_drv);
+ fdctrl_reset_fifo(fdctrl);
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, 0x20);
+ break;
+ case 0x0F:
+ /* SEEK */
+ FLOPPY_DPRINTF("treat SEEK command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ fd_start(cur_drv);
+ if (fdctrl->fifo[2] <= cur_drv->track)
+ cur_drv->dir = 1;
+ else
+ cur_drv->dir = 0;
+ fdctrl_reset_fifo(fdctrl);
+ if (fdctrl->fifo[2] > cur_drv->max_track) {
+ fdctrl_raise_irq(fdctrl, 0x60);
+ } else {
+ cur_drv->track = fdctrl->fifo[2];
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, 0x20);
+ }
+ break;
+ case 0x12:
+ /* PERPENDICULAR_MODE */
+ FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n");
+ if (fdctrl->fifo[1] & 0x80)
+ cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+ break;
+ case 0x13:
+ /* CONFIGURE */
+ FLOPPY_DPRINTF("treat CONFIGURE command\n");
+ fdctrl->config = fdctrl->fifo[2];
+ fdctrl->precomp_trk = fdctrl->fifo[3];
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+ break;
+ case 0x17:
+ /* POWERDOWN_MODE */
+ FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n");
+ fdctrl->pwrd = fdctrl->fifo[1];
+ fdctrl->fifo[0] = fdctrl->fifo[1];
+ fdctrl_set_fifo(fdctrl, 1, 1);
+ break;
+ case 0x33:
+ /* OPTION */
+ FLOPPY_DPRINTF("treat OPTION command\n");
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+ break;
+ case 0x42:
+ /* READ_TRACK */
+// FLOPPY_DPRINTF("treat READ_TRACK command\n");
+ FLOPPY_ERROR("treat READ_TRACK command\n");
+ fdctrl_start_transfer(fdctrl, FD_DIR_READ);
+ break;
+ case 0x4A:
+ /* READ_ID */
+ FLOPPY_DPRINTF("treat READ_ID command\n");
+ /* XXX: should set main status register to busy */
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+ qemu_mod_timer(fdctrl->result_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 50));
+ break;
+ case 0x4C:
+ /* RESTORE */
+ FLOPPY_DPRINTF("treat RESTORE command\n");
+ /* Drives position */
+ drv0(fdctrl)->track = fdctrl->fifo[3];
+ drv1(fdctrl)->track = fdctrl->fifo[4];
+ /* timers */
+ fdctrl->timer0 = fdctrl->fifo[7];
+ fdctrl->timer1 = fdctrl->fifo[8];
+ cur_drv->last_sect = fdctrl->fifo[9];
+ fdctrl->lock = fdctrl->fifo[10] >> 7;
+ cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
+ fdctrl->config = fdctrl->fifo[11];
+ fdctrl->precomp_trk = fdctrl->fifo[12];
+ fdctrl->pwrd = fdctrl->fifo[13];
+ fdctrl_reset_fifo(fdctrl);
+ break;
+ case 0x4D:
+ /* FORMAT_TRACK */
+ FLOPPY_DPRINTF("treat FORMAT_TRACK command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl->data_state |= FD_STATE_FORMAT;
+ if (fdctrl->fifo[0] & 0x80)
+ fdctrl->data_state |= FD_STATE_MULTI;
+ else
+ fdctrl->data_state &= ~FD_STATE_MULTI;
+ fdctrl->data_state &= ~FD_STATE_SEEK;
+ cur_drv->bps =
+ fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
+#if 0
+ cur_drv->last_sect =
+ cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
+ fdctrl->fifo[3] / 2;
+#else
+ cur_drv->last_sect = fdctrl->fifo[3];
+#endif
+ /* Bochs BIOS is buggy and don't send format informations
+ * for each sector. So, pretend all's done right now...
+ */
+ fdctrl->data_state &= ~FD_STATE_FORMAT;
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ break;
+ case 0x8E:
+ /* DRIVE_SPECIFICATION_COMMAND */
+ FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n");
+ if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
+ /* Command parameters done */
+ if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
+ fdctrl->fifo[0] = fdctrl->fifo[1];
+ fdctrl->fifo[2] = 0;
+ fdctrl->fifo[3] = 0;
+ fdctrl_set_fifo(fdctrl, 4, 1);
+ } else {
+ fdctrl_reset_fifo(fdctrl);
+ }
+ } else if (fdctrl->data_len > 7) {
+ /* ERROR */
+ fdctrl->fifo[0] = 0x80 |
+ (cur_drv->head << 2) | fdctrl->cur_drv;
+ fdctrl_set_fifo(fdctrl, 1, 1);
+ }
+ break;
+ case 0x8F:
+ /* RELATIVE_SEEK_OUT */
+ FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ fd_start(cur_drv);
+ cur_drv->dir = 0;
+ if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
+ cur_drv->track = cur_drv->max_track - 1;
+ } else {
+ cur_drv->track += fdctrl->fifo[2];
+ }
+ fdctrl_reset_fifo(fdctrl);
+ fdctrl_raise_irq(fdctrl, 0x20);
+ break;
+ case 0xCD:
+ /* FORMAT_AND_WRITE */
+// FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n");
+ FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n");
+ fdctrl_unimplemented(fdctrl);
+ break;
+ case 0xCF:
+ /* RELATIVE_SEEK_IN */
+ FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n");
+ fdctrl->cur_drv = fdctrl->fifo[1] & 1;
+ cur_drv = get_cur_drv(fdctrl);
+ fd_start(cur_drv);
+ cur_drv->dir = 1;
+ if (fdctrl->fifo[2] > cur_drv->track) {
+ cur_drv->track = 0;
+ } else {
+ cur_drv->track -= fdctrl->fifo[2];
+ }
+ fdctrl_reset_fifo(fdctrl);
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, 0x20);
+ break;
+ }
+ }
+}
+
+static void fdctrl_result_timer(void *opaque)
+{
+ fdctrl_t *fdctrl = opaque;
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+}
diff --git a/hw/fmopl.c b/hw/fmopl.c
new file mode 100644
index 0000000..2b0e82b
--- /dev/null
+++ b/hw/fmopl.c
@@ -0,0 +1,1390 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.37a
+**
+*/
+
+/*
+ preliminary :
+ Problem :
+ note:
+*/
+
+/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define INLINE __inline
+#define HAS_YM3812 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+//#include "driver.h" /* use M.A.M.E. */
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- for debug --------------------- */
+/* #define OPL_OUTPUT_LOG */
+#ifdef OPL_OUTPUT_LOG
+static FILE *opl_dbg_fp = NULL;
+static FM_OPL *opl_dbg_opl[16];
+static int opl_dbg_maxchip,opl_dbg_chip;
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT 4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+
+#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */
+#define EG_DED EG_OFF
+#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */
+#define EG_AED EG_DST
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML 2
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32 *ams_table;
+INT32 *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2; /* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+
+//#define LOG_LEVEL LOG_INF
+#define LOG_LEVEL LOG_ERR
+
+//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#define LOG(n,x)
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+ if( SLOT->evm > ENV_MOD_RR)
+ {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc&EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+ /* calcrate envelope generator */
+ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+ {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ)
+ {
+ SLOT->evs = 0;
+ }
+ else
+ {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF+1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+ INT32 *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = MUL_TABLE[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_typ = (v&0x20)>>5;
+ SLOT->vib = (v&0x40);
+ SLOT->ams = (v&0x80);
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+ if( !(OPL->mode&0x80) )
+ { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ar = v>>4;
+ int dr = v&0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int sl = v>>4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+ UINT32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB)
+ {
+ int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ *CH->connect1 += OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+ UINT32 env_tam,env_sd,env_top,env_hh;
+ int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+ INT32 tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB)
+ {
+ int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ feedback2 = OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam=OPL_CALC_SLOT(SLOT8_1);
+ env_top=OPL_CALC_SLOT(SLOT8_2);
+ env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+ else SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+ if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+ else SLOT7_2->Cnt += (CH[7].fc*8);
+ if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+ else SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+ else SLOT8_2->Cnt += (CH[8].fc*48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if( env_sd < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+ /* TAM */
+ if( env_tam < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+ /* TOP-CY */
+ if( env_top < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+ /* HH */
+ if( env_hh < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4;i <= 60;i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT<<ENV_BITS);
+ OPL->AR_TABLE[i] = rate / ARRATE;
+ OPL->DR_TABLE[i] = rate / DRRATE;
+ }
+ for (i = 60;i < 76;i++)
+ {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+#if 0
+ for (i = 0;i < 64 ;i++){ /* make for overflow area */
+ LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i,
+ ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+ ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+ }
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+ return 0;
+ if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+ {
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0;t < EG_ENT-1 ;t++){
+ rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+ }
+ /* fill volume off area */
+ for ( t = EG_ENT-1; t < TL_MAX ;t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1];
+ for (s = 1;s <= SIN_ENT/4;s++){
+ pom = sin(2*PI*s/SIN_ENT); /* sin */
+ pom = 20*log10(1/pom); /* decibel */
+ j = pom / EG_STEP; /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j];
+/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+ }
+ for (s = 0;s < SIN_ENT;s++)
+ {
+ SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+ SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i<EG_ENT; i++)
+ {
+ /* ATTACK curve */
+ pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+ /* make LFO ams table */
+ for (i=0; i<AMS_ENT; i++)
+ {
+ pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+ AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */
+ AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i<VIB_ENT; i++)
+ {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */
+ VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+ /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+ }
+ return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+ /* make fnumber -> increment counter table */
+ for( fn=0 ; fn < 1024 ; fn++ )
+ {
+ OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+ OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:controll */
+ switch(r&0x1f)
+ {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ if(!OPL->wavesel)
+ {
+ /* preset compatible mode */
+ int c;
+ for(c=0;c<OPL->max_ch;c++)
+ {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f);
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL,v&0x78);
+ OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+ }
+ }
+ return;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+ }
+ return;
+ case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+ case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+ v&=0x1f; /* for DELTA-T unit */
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* EG-CTRL */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+#if 0
+ case 0x15: /* DAC data */
+ case 0x16:
+ case 0x17: /* SHIFT */
+ return;
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ return;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ return;
+ case 0x1a: /* PCM data */
+ return;
+#endif
+#endif
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r)
+ {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ UINT8 rkey = OPL->rythm^v;
+ OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+ OPL->rythm = v&0x3f;
+ if(OPL->rythm&0x20)
+ {
+#if 0
+ usrintf_showmessage("OPL Rythm mode select");
+#endif
+ /* BD key on/off */
+ if(rkey&0x10)
+ {
+ if(v&0x10)
+ {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey&0x08)
+ {
+ if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey&0x04)
+ {
+ if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey&0x02)
+ {
+ if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey&0x01)
+ {
+ if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ int keyon = (v>>5)&1;
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+ if(CH->keyon != keyon)
+ {
+ if( (CH->keyon=keyon) )
+ {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ int blockRv = 7-(block_fnum>>10);
+ int fnum = block_fnum&0x3ff;
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = KSL_TABLE[block_fnum>>6];
+ CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+ CH->kcode = CH->block_fnum>>9;
+ if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v>>1)&7;
+ CH->FB = feedback ? (8+1) - feedback : 0;
+ CH->CON = v&1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ CH = &OPL->P_CH[slot/2];
+ if(OPL->wavesel)
+ {
+ /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !OPLOpenTable() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
+ }
+#endif
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ /* setup DELTA-T unit */
+ YM_DELTAT_DECODE_PRESET(DELTAT);
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* deltaT ADPCM */
+ if( DELTAT->portstate )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+ /* deltaT START flag */
+ if( !DELTAT->portstate )
+ OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+ /* reset OPerator paramater */
+ for( c = 0 ; c < OPL->max_ch ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF+1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = outd;
+ DELTAT->portshift = 5;
+ DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0);
+ }
+#endif
+}
+
+/* ---------- Create one of vietual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+ int max_ch = 9; /* normaly 9 channels */
+
+ if( OPL_LockTable() ==-1) return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+ /* allocate memory block */
+ ptr = malloc(state_size);
+ if(ptr==NULL) return NULL;
+ /* clear */
+ memset(ptr,0,state_size);
+ OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+ /* init grobal tables */
+ OPL_initalize(OPL);
+ /* reset chip */
+ OPLResetChip(OPL);
+#ifdef OPL_OUTPUT_LOG
+ if(!opl_dbg_fp)
+ {
+ opl_dbg_fp = fopen("opllog.opl","wb");
+ opl_dbg_maxchip = 0;
+ }
+ if(opl_dbg_fp)
+ {
+ opl_dbg_opl[opl_dbg_maxchip] = OPL;
+ fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
+ type,
+ clock&0xff,
+ (clock/0x100)&0xff,
+ (clock/0x10000)&0xff,
+ (clock/0x1000000)&0xff);
+ opl_dbg_maxchip++;
+ }
+#endif
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL)
+{
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ fclose(opl_dbg_fp);
+ opl_dbg_fp = NULL;
+ }
+#endif
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
+ }
+#endif
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ { /* status port */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+ }
+ return 0;
+#if 0
+ case 0x0f: /* ADPCM-DATA */
+ return 0;
+#endif
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0;ch<9;ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+ return OPL->status>>7;
+}
diff --git a/hw/fmopl.h b/hw/fmopl.h
new file mode 100644
index 0000000..a01ff90
--- /dev/null
+++ b/hw/fmopl.h
@@ -0,0 +1,174 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+//#define BUILD_YM3526 (HAS_YM3526)
+//#define BUILD_Y8950 (HAS_Y8950)
+
+/* --- system optimize --- */
+/* select bit size of output : 8 or 16 */
+#define OPL_OUTPUT_BIT 16
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+#if (OPL_OUTPUT_BIT==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_OUTPUT_BIT==8)
+typedef unsigned char OPLSAMPLE;
+#endif
+
+
+#if BUILD_Y8950
+#include "ymdeltat.h"
+#endif
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ INT32 TL; /* total level :TL << 8 */
+ INT32 TLL; /* adjusted now TL */
+ UINT8 KSR; /* key scale rate :(shift down bit) */
+ INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
+ INT32 SL; /* sustin level :SL_TALBE[SL] */
+ INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
+ UINT8 ksl; /* keyscale level :(shift down bits) */
+ UINT8 ksr; /* key scale rate :kcode>>KSR */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+ UINT32 Cnt; /* frequency count : */
+ UINT32 Incr; /* frequency step : */
+ /* envelope generator state */
+ UINT8 eg_typ; /* envelope type flag */
+ UINT8 evm; /* envelope phase */
+ INT32 evc; /* envelope counter */
+ INT32 eve; /* envelope counter end point */
+ INT32 evs; /* envelope counter step */
+ INT32 evsa; /* envelope step for AR :AR[ksr] */
+ INT32 evsd; /* envelope step for DR :DR[ksr] */
+ INT32 evsr; /* envelope step for RR :RR[ksr] */
+ /* LFO */
+ UINT8 ams; /* ams flag */
+ UINT8 vib; /* vibrate flag */
+ /* wave selector */
+ INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ UINT8 CON; /* connection type */
+ UINT8 FB; /* feed back :(shift down bit) */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 *connect2; /* slot2 output pointer */
+ INT32 op1_out[2]; /* slot1 output for selfeedback */
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum : */
+ UINT8 kcode; /* key code : KeyScaleCode */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
+ /* Timer */
+ int T[2]; /* timer counter */
+ UINT8 st[2]; /* timer enable */
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+ /* Rythm sention */
+ UINT8 rythm; /* Rythm mode , key flag */
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+ YM_DELTAT *deltat; /* DELTA-T ADPCM */
+#endif
+ /* Keyboard / I/O interface unit (Y8950) */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ int port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ int keyboard_param;
+ /* time tables */
+ INT32 AR_TABLE[75]; /* atttack rate tables */
+ INT32 DR_TABLE[75]; /* decay rate tables */
+ UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
+ /* LFO */
+ INT32 *ams_table;
+ INT32 *vib_table;
+ INT32 amsCnt;
+ INT32 amsIncr;
+ INT32 vibCnt;
+ INT32 vibIncr;
+ /* wave selector enable flag */
+ UINT8 wavesel;
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+/* Y8950 port handlers */
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
new file mode 100644
index 0000000..e3cbceb
--- /dev/null
+++ b/hw/grackle_pci.c
@@ -0,0 +1,156 @@
+/*
+ * QEMU Grackle (heathrow PPC) PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+typedef target_phys_addr_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState GrackleState;
+
+static void pci_grackle_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ GrackleState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ s->config_reg = val;
+}
+
+static uint32_t pci_grackle_config_readl (void *opaque, target_phys_addr_t addr)
+{
+ GrackleState *s = opaque;
+ uint32_t val;
+
+ val = s->config_reg;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_grackle_config_write[] = {
+ &pci_grackle_config_writel,
+ &pci_grackle_config_writel,
+ &pci_grackle_config_writel,
+};
+
+static CPUReadMemoryFunc *pci_grackle_config_read[] = {
+ &pci_grackle_config_readl,
+ &pci_grackle_config_readl,
+ &pci_grackle_config_readl,
+};
+
+static CPUWriteMemoryFunc *pci_grackle_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static CPUReadMemoryFunc *pci_grackle_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+/* XXX: we do not simulate the hardware - we rely on the BIOS to
+ set correctly for irq line field */
+static void pci_grackle_set_irq(PCIDevice *d, void *pic, int irq_num, int level)
+{
+ heathrow_pic_set_irq(pic, d->config[PCI_INTERRUPT_LINE], level);
+}
+
+PCIBus *pci_grackle_init(uint32_t base, void *pic)
+{
+ GrackleState *s;
+ PCIDevice *d;
+ int pci_mem_config, pci_mem_data;
+
+ s = qemu_mallocz(sizeof(GrackleState));
+ s->bus = pci_register_bus(pci_grackle_set_irq, pic, 0);
+
+ pci_mem_config = cpu_register_io_memory(0, pci_grackle_config_read,
+ pci_grackle_config_write, s);
+ pci_mem_data = cpu_register_io_memory(0, pci_grackle_read,
+ pci_grackle_write, s);
+ cpu_register_physical_memory(base, 0x1000, pci_mem_config);
+ cpu_register_physical_memory(base + 0x00200000, 0x1000, pci_mem_data);
+ d = pci_register_device(s->bus, "Grackle host bridge", sizeof(PCIDevice),
+ 0, NULL, NULL);
+ d->config[0x00] = 0x57; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x02; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x09] = 0x01;
+ d->config[0x0a] = 0x00; // class_sub = host
+ d->config[0x0b] = 0x06; // class_base = PCI_bridge
+ d->config[0x0e] = 0x00; // header_type
+
+ d->config[0x18] = 0x00; // primary_bus
+ d->config[0x19] = 0x01; // secondary_bus
+ d->config[0x1a] = 0x00; // subordinate_bus
+ d->config[0x1c] = 0x00;
+ d->config[0x1d] = 0x00;
+
+ d->config[0x20] = 0x00; // memory_base
+ d->config[0x21] = 0x00;
+ d->config[0x22] = 0x01; // memory_limit
+ d->config[0x23] = 0x00;
+
+ d->config[0x24] = 0x00; // prefetchable_memory_base
+ d->config[0x25] = 0x00;
+ d->config[0x26] = 0x00; // prefetchable_memory_limit
+ d->config[0x27] = 0x00;
+
+#if 0
+ /* PCI2PCI bridge same values as PearPC - check this */
+ d->config[0x00] = 0x11; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x26; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x02; // revision
+ d->config[0x0a] = 0x04; // class_sub = pci2pci
+ d->config[0x0b] = 0x06; // class_base = PCI_bridge
+ d->config[0x0e] = 0x01; // header_type
+
+ d->config[0x18] = 0x0; // primary_bus
+ d->config[0x19] = 0x1; // secondary_bus
+ d->config[0x1a] = 0x1; // subordinate_bus
+ d->config[0x1c] = 0x10; // io_base
+ d->config[0x1d] = 0x20; // io_limit
+
+ d->config[0x20] = 0x80; // memory_base
+ d->config[0x21] = 0x80;
+ d->config[0x22] = 0x90; // memory_limit
+ d->config[0x23] = 0x80;
+
+ d->config[0x24] = 0x00; // prefetchable_memory_base
+ d->config[0x25] = 0x84;
+ d->config[0x26] = 0x00; // prefetchable_memory_limit
+ d->config[0x27] = 0x85;
+#endif
+ return s->bus;
+}
+
diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c
new file mode 100644
index 0000000..4980cef
--- /dev/null
+++ b/hw/heathrow_pic.c
@@ -0,0 +1,168 @@
+/*
+ * Heathrow PIC support (standard PowerMac PIC)
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG
+
+typedef struct HeathrowPIC {
+ uint32_t events;
+ uint32_t mask;
+ uint32_t levels;
+ uint32_t level_triggered;
+} HeathrowPIC;
+
+struct HeathrowPICS {
+ HeathrowPIC pics[2];
+};
+
+static inline int check_irq(HeathrowPIC *pic)
+{
+ return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
+}
+
+/* update the CPU irq state */
+static void heathrow_pic_update(HeathrowPICS *s)
+{
+ if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+
+ value = bswap32(value);
+#ifdef DEBUG
+ printf("pic_writel: %08x: %08x\n",
+ addr, value);
+#endif
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ if (n >= 2)
+ return;
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x04:
+ pic->mask = value;
+ heathrow_pic_update(s);
+ break;
+ case 0x08:
+ /* do not reset level triggered IRQs */
+ value &= ~pic->level_triggered;
+ pic->events &= ~value;
+ heathrow_pic_update(s);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pic_readl (void *opaque, target_phys_addr_t addr)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+ uint32_t value;
+
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ if (n >= 2) {
+ value = 0;
+ } else {
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x0:
+ value = pic->events;
+ break;
+ case 0x4:
+ value = pic->mask;
+ break;
+ case 0xc:
+ value = pic->levels;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ }
+#ifdef DEBUG
+ printf("pic_readl: %08x: %08x\n",
+ addr, value);
+#endif
+ value = bswap32(value);
+ return value;
+}
+
+static CPUWriteMemoryFunc *pic_write[] = {
+ &pic_writel,
+ &pic_writel,
+ &pic_writel,
+};
+
+static CPUReadMemoryFunc *pic_read[] = {
+ &pic_readl,
+ &pic_readl,
+ &pic_readl,
+};
+
+
+void heathrow_pic_set_irq(void *opaque, int num, int level)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int irq_bit;
+
+#if defined(DEBUG)
+ {
+ static int last_level[64];
+ if (last_level[num] != level) {
+ printf("set_irq: num=0x%02x level=%d\n", num, level);
+ last_level[num] = level;
+ }
+ }
+#endif
+ pic = &s->pics[1 - (num >> 5)];
+ irq_bit = 1 << (num & 0x1f);
+ if (level) {
+ pic->events |= irq_bit & ~pic->level_triggered;
+ pic->levels |= irq_bit;
+ } else {
+ pic->levels &= ~irq_bit;
+ }
+ heathrow_pic_update(s);
+}
+
+HeathrowPICS *heathrow_pic_init(int *pmem_index)
+{
+ HeathrowPICS *s;
+
+ s = qemu_mallocz(sizeof(HeathrowPICS));
+ s->pics[0].level_triggered = 0;
+ s->pics[1].level_triggered = 0x1ff00000;
+ *pmem_index = cpu_register_io_memory(0, pic_read, pic_write, s);
+ return s;
+}
diff --git a/hw/i8254.c b/hw/i8254.c
new file mode 100644
index 0000000..a409763
--- /dev/null
+++ b/hw/i8254.c
@@ -0,0 +1,482 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_PIT
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+typedef struct PITChannelState {
+ int count; /* can be 65536 */
+ uint16_t latched_count;
+ uint8_t count_latched;
+ uint8_t status_latched;
+ uint8_t status;
+ uint8_t read_state;
+ uint8_t write_state;
+ uint8_t write_latch;
+ uint8_t rw_mode;
+ uint8_t mode;
+ uint8_t bcd; /* not supported */
+ uint8_t gate; /* timer start */
+ int64_t count_load_time;
+ /* irq handling */
+ int64_t next_transition_time;
+ QEMUTimer *irq_timer;
+ int irq;
+} PITChannelState;
+
+struct PITState {
+ PITChannelState channels[3];
+};
+
+static PITState pit_state;
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
+
+static int pit_get_count(PITChannelState *s)
+{
+ uint64_t d;
+ int counter;
+
+ d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
+ switch(s->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (s->count - d) & 0xffff;
+ break;
+ case 3:
+ /* XXX: may be incorrect for odd counts */
+ counter = s->count - ((2 * d) % s->count);
+ break;
+ default:
+ counter = s->count - (d % s->count);
+ break;
+ }
+ return counter;
+}
+
+/* get pit output bit */
+static int pit_get_out1(PITChannelState *s, int64_t current_time)
+{
+ uint64_t d;
+ int out;
+
+ d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
+ switch(s->mode) {
+ default:
+ case 0:
+ out = (d >= s->count);
+ break;
+ case 1:
+ out = (d < s->count);
+ break;
+ case 2:
+ if ((d % s->count) == 0 && d != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case 3:
+ out = (d % s->count) < ((s->count + 1) >> 1);
+ break;
+ case 4:
+ case 5:
+ out = (d == s->count);
+ break;
+ }
+ return out;
+}
+
+int pit_get_out(PITState *pit, int channel, int64_t current_time)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return pit_get_out1(s, current_time);
+}
+
+/* return -1 if no transition will occur. */
+static int64_t pit_get_next_transition_time(PITChannelState *s,
+ int64_t current_time)
+{
+ uint64_t d, next_time, base;
+ int period2;
+
+ d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
+ switch(s->mode) {
+ default:
+ case 0:
+ case 1:
+ if (d < s->count)
+ next_time = s->count;
+ else
+ return -1;
+ break;
+ case 2:
+ base = (d / s->count) * s->count;
+ if ((d - base) == 0 && d != 0)
+ next_time = base + s->count;
+ else
+ next_time = base + s->count + 1;
+ break;
+ case 3:
+ base = (d / s->count) * s->count;
+ period2 = ((s->count + 1) >> 1);
+ if ((d - base) < period2)
+ next_time = base + period2;
+ else
+ next_time = base + s->count;
+ break;
+ case 4:
+ case 5:
+ if (d < s->count)
+ next_time = s->count;
+ else if (d == s->count)
+ next_time = s->count + 1;
+ else
+ return -1;
+ break;
+ }
+ /* convert to timer units */
+ next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
+ /* fix potential rounding problems */
+ /* XXX: better solution: use a clock at PIT_FREQ Hz */
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+/* val must be 0 or 1 */
+void pit_set_gate(PITState *pit, int channel, int val)
+{
+ PITChannelState *s = &pit->channels[channel];
+
+ switch(s->mode) {
+ default:
+ case 0:
+ case 4:
+ /* XXX: just disable/enable counting */
+ break;
+ case 1:
+ case 5:
+ if (s->gate < val) {
+ /* restart counting on rising edge */
+ s->count_load_time = qemu_get_clock(vm_clock);
+ pit_irq_timer_update(s, s->count_load_time);
+ }
+ break;
+ case 2:
+ case 3:
+ if (s->gate < val) {
+ /* restart counting on rising edge */
+ s->count_load_time = qemu_get_clock(vm_clock);
+ pit_irq_timer_update(s, s->count_load_time);
+ }
+ /* XXX: disable/enable counting */
+ break;
+ }
+ s->gate = val;
+}
+
+int pit_get_gate(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->gate;
+}
+
+int pit_get_initial_count(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->count;
+}
+
+int pit_get_mode(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->mode;
+}
+
+static inline void pit_load_count(PITChannelState *s, int val)
+{
+ if (val == 0)
+ val = 0x10000;
+ s->count_load_time = qemu_get_clock(vm_clock);
+ s->count = val;
+ pit_irq_timer_update(s, s->count_load_time);
+}
+
+/* if already latched, do not latch again */
+static void pit_latch_count(PITChannelState *s)
+{
+ if (!s->count_latched) {
+ s->latched_count = pit_get_count(s);
+ s->count_latched = s->rw_mode;
+ }
+}
+
+static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PITState *pit = opaque;
+ int channel, access;
+ PITChannelState *s;
+
+ addr &= 3;
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3) {
+ /* read back command */
+ for(channel = 0; channel < 3; channel++) {
+ s = &pit->channels[channel];
+ if (val & (2 << channel)) {
+ if (!(val & 0x20)) {
+ pit_latch_count(s);
+ }
+ if (!(val & 0x10) && !s->status_latched) {
+ /* status latch */
+ /* XXX: add BCD and null count */
+ s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
+ (s->rw_mode << 4) |
+ (s->mode << 1) |
+ s->bcd;
+ s->status_latched = 1;
+ }
+ }
+ }
+ } else {
+ s = &pit->channels[channel];
+ access = (val >> 4) & 3;
+ if (access == 0) {
+ pit_latch_count(s);
+ } else {
+ s->rw_mode = access;
+ s->read_state = access;
+ s->write_state = access;
+
+ s->mode = (val >> 1) & 7;
+ s->bcd = val & 1;
+ /* XXX: update irq timer ? */
+ }
+ }
+ } else {
+ s = &pit->channels[addr];
+ switch(s->write_state) {
+ default:
+ case RW_STATE_LSB:
+ pit_load_count(s, val);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(s, val << 8);
+ break;
+ case RW_STATE_WORD0:
+ s->write_latch = val;
+ s->write_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ pit_load_count(s, s->write_latch | (val << 8));
+ s->write_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+}
+
+static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
+{
+ PITState *pit = opaque;
+ int ret, count;
+ PITChannelState *s;
+
+ addr &= 3;
+ s = &pit->channels[addr];
+ if (s->status_latched) {
+ s->status_latched = 0;
+ ret = s->status;
+ } else if (s->count_latched) {
+ switch(s->count_latched) {
+ default:
+ case RW_STATE_LSB:
+ ret = s->latched_count & 0xff;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_MSB:
+ ret = s->latched_count >> 8;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_WORD0:
+ ret = s->latched_count & 0xff;
+ s->count_latched = RW_STATE_MSB;
+ break;
+ }
+ } else {
+ switch(s->read_state) {
+ default:
+ case RW_STATE_LSB:
+ count = pit_get_count(s);
+ ret = count & 0xff;
+ break;
+ case RW_STATE_MSB:
+ count = pit_get_count(s);
+ ret = (count >> 8) & 0xff;
+ break;
+ case RW_STATE_WORD0:
+ count = pit_get_count(s);
+ ret = count & 0xff;
+ s->read_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ count = pit_get_count(s);
+ ret = (count >> 8) & 0xff;
+ s->read_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
+{
+ int64_t expire_time;
+ int irq_level;
+
+ if (!s->irq_timer)
+ return;
+ expire_time = pit_get_next_transition_time(s, current_time);
+ irq_level = pit_get_out1(s, current_time);
+ pic_set_irq(s->irq, irq_level);
+#ifdef DEBUG_PIT
+ printf("irq_level=%d next_delay=%f\n",
+ irq_level,
+ (double)(expire_time - current_time) / ticks_per_sec);
+#endif
+ s->next_transition_time = expire_time;
+ if (expire_time != -1)
+ qemu_mod_timer(s->irq_timer, expire_time);
+ else
+ qemu_del_timer(s->irq_timer);
+}
+
+static void pit_irq_timer(void *opaque)
+{
+ PITChannelState *s = opaque;
+
+ pit_irq_timer_update(s, s->next_transition_time);
+}
+
+static void pit_save(QEMUFile *f, void *opaque)
+{
+ PITState *pit = opaque;
+ PITChannelState *s;
+ int i;
+
+ for(i = 0; i < 3; i++) {
+ s = &pit->channels[i];
+ qemu_put_be32s(f, &s->count);
+ qemu_put_be16s(f, &s->latched_count);
+ qemu_put_8s(f, &s->count_latched);
+ qemu_put_8s(f, &s->status_latched);
+ qemu_put_8s(f, &s->status);
+ qemu_put_8s(f, &s->read_state);
+ qemu_put_8s(f, &s->write_state);
+ qemu_put_8s(f, &s->write_latch);
+ qemu_put_8s(f, &s->rw_mode);
+ qemu_put_8s(f, &s->mode);
+ qemu_put_8s(f, &s->bcd);
+ qemu_put_8s(f, &s->gate);
+ qemu_put_be64s(f, &s->count_load_time);
+ if (s->irq_timer) {
+ qemu_put_be64s(f, &s->next_transition_time);
+ qemu_put_timer(f, s->irq_timer);
+ }
+ }
+}
+
+static int pit_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PITState *pit = opaque;
+ PITChannelState *s;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for(i = 0; i < 3; i++) {
+ s = &pit->channels[i];
+ qemu_get_be32s(f, &s->count);
+ qemu_get_be16s(f, &s->latched_count);
+ qemu_get_8s(f, &s->count_latched);
+ qemu_get_8s(f, &s->status_latched);
+ qemu_get_8s(f, &s->status);
+ qemu_get_8s(f, &s->read_state);
+ qemu_get_8s(f, &s->write_state);
+ qemu_get_8s(f, &s->write_latch);
+ qemu_get_8s(f, &s->rw_mode);
+ qemu_get_8s(f, &s->mode);
+ qemu_get_8s(f, &s->bcd);
+ qemu_get_8s(f, &s->gate);
+ qemu_get_be64s(f, &s->count_load_time);
+ if (s->irq_timer) {
+ qemu_get_be64s(f, &s->next_transition_time);
+ qemu_get_timer(f, s->irq_timer);
+ }
+ }
+ return 0;
+}
+
+static void pit_reset(void *opaque)
+{
+ PITState *pit = opaque;
+ PITChannelState *s;
+ int i;
+
+ for(i = 0;i < 3; i++) {
+ s = &pit->channels[i];
+ s->mode = 3;
+ s->gate = (i != 2);
+ pit_load_count(s, 0);
+ }
+}
+
+PITState *pit_init(int base, int irq)
+{
+ PITState *pit = &pit_state;
+ PITChannelState *s;
+
+ s = &pit->channels[0];
+ /* the timer 0 is connected to an IRQ */
+ s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
+ s->irq = irq;
+
+ register_savevm("i8254", base, 1, pit_save, pit_load, pit);
+
+ qemu_register_reset(pit_reset, pit);
+ register_ioport_write(base, 4, 1, pit_ioport_write, pit);
+ register_ioport_read(base, 3, 1, pit_ioport_read, pit);
+
+ pit_reset(pit);
+
+ return pit;
+}
diff --git a/hw/i8259.c b/hw/i8259.c
new file mode 100644
index 0000000..c747f10
--- /dev/null
+++ b/hw/i8259.c
@@ -0,0 +1,561 @@
+/*
+ * QEMU 8259 interrupt controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+//#define DEBUG_IRQ_LATENCY
+//#define DEBUG_IRQ_COUNT
+
+typedef struct PicState {
+ uint8_t last_irr; /* edge detection */
+ uint8_t irr; /* interrupt request register */
+ uint8_t imr; /* interrupt mask register */
+ uint8_t isr; /* interrupt service register */
+ uint8_t priority_add; /* highest irq priority */
+ uint8_t irq_base;
+ uint8_t read_reg_select;
+ uint8_t poll;
+ uint8_t special_mask;
+ uint8_t init_state;
+ uint8_t auto_eoi;
+ uint8_t rotate_on_auto_eoi;
+ uint8_t special_fully_nested_mode;
+ uint8_t init4; /* true if 4 byte init */
+ uint8_t elcr; /* PIIX edge/trigger selection*/
+ uint8_t elcr_mask;
+ PicState2 *pics_state;
+} PicState;
+
+struct PicState2 {
+ /* 0 is master pic, 1 is slave pic */
+ /* XXX: better separation between the two pics */
+ PicState pics[2];
+ IRQRequestFunc *irq_request;
+ void *irq_request_opaque;
+ /* IOAPIC callback support */
+ SetIRQFunc *alt_irq_func;
+ void *alt_irq_opaque;
+};
+
+#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT)
+static int irq_level[16];
+#endif
+#ifdef DEBUG_IRQ_COUNT
+static uint64_t irq_count[16];
+#endif
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static inline void pic_set_irq1(PicState *s, int irq, int level)
+{
+ int mask;
+ mask = 1 << irq;
+ if (s->elcr & mask) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->irr &= ~mask;
+ s->last_irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ if ((s->last_irr & mask) == 0)
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->last_irr &= ~mask;
+ }
+ }
+}
+
+/* return the highest priority found in mask (highest = smallest
+ number). Return 8 if no irq */
+static inline int get_priority(PicState *s, int mask)
+{
+ int priority;
+ if (mask == 0)
+ return 8;
+ priority = 0;
+ while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
+ priority++;
+ return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PicState *s)
+{
+ int mask, cur_priority, priority;
+
+ mask = s->irr & ~s->imr;
+ priority = get_priority(s, mask);
+ if (priority == 8)
+ return -1;
+ /* compute current priority. If special fully nested mode on the
+ master, the IRQ coming from the slave is not taken into account
+ for the priority computation. */
+ mask = s->isr;
+ if (s->special_fully_nested_mode && s == &s->pics_state->pics[0])
+ mask &= ~(1 << 2);
+ cur_priority = get_priority(s, mask);
+ if (priority < cur_priority) {
+ /* higher priority found: an irq should be generated */
+ return (priority + s->priority_add) & 7;
+ } else {
+ return -1;
+ }
+}
+
+/* raise irq to CPU if necessary. must be called every time the active
+ irq may change */
+/* XXX: should not export it, but it is needed for an APIC kludge */
+void pic_update_irq(PicState2 *s)
+{
+ int irq2, irq;
+
+ /* first look at slave pic */
+ irq2 = pic_get_irq(&s->pics[1]);
+ if (irq2 >= 0) {
+ /* if irq request by slave pic, signal master PIC */
+ pic_set_irq1(&s->pics[0], 2, 1);
+ pic_set_irq1(&s->pics[0], 2, 0);
+ }
+ /* look at requested irq */
+ irq = pic_get_irq(&s->pics[0]);
+ if (irq >= 0) {
+#if defined(DEBUG_PIC)
+ {
+ int i;
+ for(i = 0; i < 2; i++) {
+ printf("pic%d: imr=%x irr=%x padd=%d\n",
+ i, s->pics[i].imr, s->pics[i].irr,
+ s->pics[i].priority_add);
+
+ }
+ }
+ printf("pic: cpu_interrupt\n");
+#endif
+ s->irq_request(s->irq_request_opaque, 1);
+ }
+}
+
+#ifdef DEBUG_IRQ_LATENCY
+int64_t irq_time[16];
+#endif
+
+void pic_set_irq_new(void *opaque, int irq, int level)
+{
+ PicState2 *s = opaque;
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+ if (level != irq_level[irq]) {
+#if defined(DEBUG_PIC)
+ printf("pic_set_irq: irq=%d level=%d\n", irq, level);
+#endif
+ irq_level[irq] = level;
+#ifdef DEBUG_IRQ_COUNT
+ if (level == 1)
+ irq_count[irq]++;
+#endif
+ }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+ if (level) {
+ irq_time[irq] = qemu_get_clock(vm_clock);
+ }
+#endif
+ pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
+ /* used for IOAPIC irqs */
+ if (s->alt_irq_func)
+ s->alt_irq_func(s->alt_irq_opaque, irq, level);
+ pic_update_irq(s);
+}
+
+/* obsolete function */
+void pic_set_irq(int irq, int level)
+{
+ pic_set_irq_new(isa_pic, irq, level);
+}
+
+/* acknowledge interrupt 'irq' */
+static inline void pic_intack(PicState *s, int irq)
+{
+ if (s->auto_eoi) {
+ if (s->rotate_on_auto_eoi)
+ s->priority_add = (irq + 1) & 7;
+ } else {
+ s->isr |= (1 << irq);
+ }
+ /* We don't clear a level sensitive interrupt here */
+ if (!(s->elcr & (1 << irq)))
+ s->irr &= ~(1 << irq);
+}
+
+int pic_read_irq(PicState2 *s)
+{
+ int irq, irq2, intno;
+
+ irq = pic_get_irq(&s->pics[0]);
+ if (irq >= 0) {
+ pic_intack(&s->pics[0], irq);
+ if (irq == 2) {
+ irq2 = pic_get_irq(&s->pics[1]);
+ if (irq2 >= 0) {
+ pic_intack(&s->pics[1], irq2);
+ } else {
+ /* spurious IRQ on slave controller */
+ irq2 = 7;
+ }
+ intno = s->pics[1].irq_base + irq2;
+ irq = irq2 + 8;
+ } else {
+ intno = s->pics[0].irq_base + irq;
+ }
+ } else {
+ /* spurious IRQ on host controller */
+ irq = 7;
+ intno = s->pics[0].irq_base + irq;
+ }
+ pic_update_irq(s);
+
+#ifdef DEBUG_IRQ_LATENCY
+ printf("IRQ%d latency=%0.3fus\n",
+ irq,
+ (double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / ticks_per_sec);
+#endif
+#if defined(DEBUG_PIC)
+ printf("pic_interrupt: irq=%d\n", irq);
+#endif
+ return intno;
+}
+
+static void pic_reset(void *opaque)
+{
+ PicState *s = opaque;
+
+ s->last_irr = 0;
+ s->irr = 0;
+ s->imr = 0;
+ s->isr = 0;
+ s->priority_add = 0;
+ s->irq_base = 0;
+ s->read_reg_select = 0;
+ s->poll = 0;
+ s->special_mask = 0;
+ s->init_state = 0;
+ s->auto_eoi = 0;
+ s->rotate_on_auto_eoi = 0;
+ s->special_fully_nested_mode = 0;
+ s->init4 = 0;
+ /* Note: ELCR is not reset */
+}
+
+static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PicState *s = opaque;
+ int priority, cmd, irq;
+
+#ifdef DEBUG_PIC
+ printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ addr &= 1;
+ if (addr == 0) {
+ if (val & 0x10) {
+ /* init */
+ pic_reset(s);
+ /* deassert a pending interrupt */
+ s->pics_state->irq_request(s->pics_state->irq_request_opaque, 0);
+ s->init_state = 1;
+ s->init4 = val & 1;
+ if (val & 0x02)
+ hw_error("single mode not supported");
+ if (val & 0x08)
+ hw_error("level sensitive irq not supported");
+ } else if (val & 0x08) {
+ if (val & 0x04)
+ s->poll = 1;
+ if (val & 0x02)
+ s->read_reg_select = val & 1;
+ if (val & 0x40)
+ s->special_mask = (val >> 5) & 1;
+ } else {
+ cmd = val >> 5;
+ switch(cmd) {
+ case 0:
+ case 4:
+ s->rotate_on_auto_eoi = cmd >> 2;
+ break;
+ case 1: /* end of interrupt */
+ case 5:
+ priority = get_priority(s, s->isr);
+ if (priority != 8) {
+ irq = (priority + s->priority_add) & 7;
+ s->isr &= ~(1 << irq);
+ if (cmd == 5)
+ s->priority_add = (irq + 1) & 7;
+ pic_update_irq(s->pics_state);
+ }
+ break;
+ case 3:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ pic_update_irq(s->pics_state);
+ break;
+ case 6:
+ s->priority_add = (val + 1) & 7;
+ pic_update_irq(s->pics_state);
+ break;
+ case 7:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ s->priority_add = (irq + 1) & 7;
+ pic_update_irq(s->pics_state);
+ break;
+ default:
+ /* no operation */
+ break;
+ }
+ }
+ } else {
+ switch(s->init_state) {
+ case 0:
+ /* normal mode */
+ s->imr = val;
+ pic_update_irq(s->pics_state);
+ break;
+ case 1:
+ s->irq_base = val & 0xf8;
+ s->init_state = 2;
+ break;
+ case 2:
+ if (s->init4) {
+ s->init_state = 3;
+ } else {
+ s->init_state = 0;
+ }
+ break;
+ case 3:
+ s->special_fully_nested_mode = (val >> 4) & 1;
+ s->auto_eoi = (val >> 1) & 1;
+ s->init_state = 0;
+ break;
+ }
+ }
+}
+
+static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
+{
+ int ret;
+
+ ret = pic_get_irq(s);
+ if (ret >= 0) {
+ if (addr1 >> 7) {
+ s->pics_state->pics[0].isr &= ~(1 << 2);
+ s->pics_state->pics[0].irr &= ~(1 << 2);
+ }
+ s->irr &= ~(1 << ret);
+ s->isr &= ~(1 << ret);
+ if (addr1 >> 7 || ret != 2)
+ pic_update_irq(s->pics_state);
+ } else {
+ ret = 0x07;
+ pic_update_irq(s->pics_state);
+ }
+
+ return ret;
+}
+
+static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
+{
+ PicState *s = opaque;
+ unsigned int addr;
+ int ret;
+
+ addr = addr1;
+ addr &= 1;
+ if (s->poll) {
+ ret = pic_poll_read(s, addr1);
+ s->poll = 0;
+ } else {
+ if (addr == 0) {
+ if (s->read_reg_select)
+ ret = s->isr;
+ else
+ ret = s->irr;
+ } else {
+ ret = s->imr;
+ }
+ }
+#ifdef DEBUG_PIC
+ printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret);
+#endif
+ return ret;
+}
+
+/* memory mapped interrupt status */
+/* XXX: may be the same than pic_read_irq() */
+uint32_t pic_intack_read(PicState2 *s)
+{
+ int ret;
+
+ ret = pic_poll_read(&s->pics[0], 0x00);
+ if (ret == 2)
+ ret = pic_poll_read(&s->pics[1], 0x80) + 8;
+ /* Prepare for ISR read */
+ s->pics[0].read_reg_select = 1;
+
+ return ret;
+}
+
+static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PicState *s = opaque;
+ s->elcr = val & s->elcr_mask;
+}
+
+static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
+{
+ PicState *s = opaque;
+ return s->elcr;
+}
+
+static void pic_save(QEMUFile *f, void *opaque)
+{
+ PicState *s = opaque;
+
+ qemu_put_8s(f, &s->last_irr);
+ qemu_put_8s(f, &s->irr);
+ qemu_put_8s(f, &s->imr);
+ qemu_put_8s(f, &s->isr);
+ qemu_put_8s(f, &s->priority_add);
+ qemu_put_8s(f, &s->irq_base);
+ qemu_put_8s(f, &s->read_reg_select);
+ qemu_put_8s(f, &s->poll);
+ qemu_put_8s(f, &s->special_mask);
+ qemu_put_8s(f, &s->init_state);
+ qemu_put_8s(f, &s->auto_eoi);
+ qemu_put_8s(f, &s->rotate_on_auto_eoi);
+ qemu_put_8s(f, &s->special_fully_nested_mode);
+ qemu_put_8s(f, &s->init4);
+ qemu_put_8s(f, &s->elcr);
+}
+
+static int pic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PicState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_8s(f, &s->last_irr);
+ qemu_get_8s(f, &s->irr);
+ qemu_get_8s(f, &s->imr);
+ qemu_get_8s(f, &s->isr);
+ qemu_get_8s(f, &s->priority_add);
+ qemu_get_8s(f, &s->irq_base);
+ qemu_get_8s(f, &s->read_reg_select);
+ qemu_get_8s(f, &s->poll);
+ qemu_get_8s(f, &s->special_mask);
+ qemu_get_8s(f, &s->init_state);
+ qemu_get_8s(f, &s->auto_eoi);
+ qemu_get_8s(f, &s->rotate_on_auto_eoi);
+ qemu_get_8s(f, &s->special_fully_nested_mode);
+ qemu_get_8s(f, &s->init4);
+ qemu_get_8s(f, &s->elcr);
+ return 0;
+}
+
+/* XXX: add generic master/slave system */
+static void pic_init1(int io_addr, int elcr_addr, PicState *s)
+{
+ register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
+ register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
+ if (elcr_addr >= 0) {
+ register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s);
+ register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s);
+ }
+ register_savevm("i8259", io_addr, 1, pic_save, pic_load, s);
+ qemu_register_reset(pic_reset, s);
+}
+
+void pic_info(void)
+{
+ int i;
+ PicState *s;
+
+ if (!isa_pic)
+ return;
+
+ for(i=0;i<2;i++) {
+ s = &isa_pic->pics[i];
+ term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
+ i, s->irr, s->imr, s->isr, s->priority_add,
+ s->irq_base, s->read_reg_select, s->elcr,
+ s->special_fully_nested_mode);
+ }
+}
+
+void irq_info(void)
+{
+#ifndef DEBUG_IRQ_COUNT
+ term_printf("irq statistic code not compiled.\n");
+#else
+ int i;
+ int64_t count;
+
+ term_printf("IRQ statistics:\n");
+ for (i = 0; i < 16; i++) {
+ count = irq_count[i];
+ if (count > 0)
+ term_printf("%2d: %" PRId64 "\n", i, count);
+ }
+#endif
+}
+
+PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque)
+{
+ PicState2 *s;
+ s = qemu_mallocz(sizeof(PicState2));
+ if (!s)
+ return NULL;
+ pic_init1(0x20, 0x4d0, &s->pics[0]);
+ pic_init1(0xa0, 0x4d1, &s->pics[1]);
+ s->pics[0].elcr_mask = 0xf8;
+ s->pics[1].elcr_mask = 0xde;
+ s->irq_request = irq_request;
+ s->irq_request_opaque = irq_request_opaque;
+ s->pics[0].pics_state = s;
+ s->pics[1].pics_state = s;
+ return s;
+}
+
+void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
+ void *alt_irq_opaque)
+{
+ s->alt_irq_func = alt_irq_func;
+ s->alt_irq_opaque = alt_irq_opaque;
+}
diff --git a/hw/ide.c b/hw/ide.c
new file mode 100644
index 0000000..debbc0f
--- /dev/null
+++ b/hw/ide.c
@@ -0,0 +1,2535 @@
+/*
+ * QEMU IDE disk and CD-ROM Emulator
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug IDE devices */
+//#define DEBUG_IDE
+//#define DEBUG_IDE_ATAPI
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define SRV_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define MC_ERR 0x20 /* media changed */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+/* Bits of HD_NSECTOR */
+#define CD 0x01
+#define IO 0x02
+#define REL 0x04
+#define TAG_MASK 0xf8
+
+#define IDE_CMD_RESET 0x04
+#define IDE_CMD_DISABLE_IRQ 0x02
+
+/* ATA/ATAPI Commands pre T13 Spec */
+#define WIN_NOP 0x00
+/*
+ * 0x01->0x02 Reserved
+ */
+#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */
+/*
+ * 0x04->0x07 Reserved
+ */
+#define WIN_SRST 0x08 /* ATAPI soft reset command */
+#define WIN_DEVICE_RESET 0x08
+/*
+ * 0x09->0x0F Reserved
+ */
+#define WIN_RECAL 0x10
+#define WIN_RESTORE WIN_RECAL
+/*
+ * 0x10->0x1F Reserved
+ */
+#define WIN_READ 0x20 /* 28-Bit */
+#define WIN_READ_ONCE 0x21 /* 28-Bit without retries */
+#define WIN_READ_LONG 0x22 /* 28-Bit */
+#define WIN_READ_LONG_ONCE 0x23 /* 28-Bit without retries */
+#define WIN_READ_EXT 0x24 /* 48-Bit */
+#define WIN_READDMA_EXT 0x25 /* 48-Bit */
+#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit */
+#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */
+/*
+ * 0x28
+ */
+#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */
+/*
+ * 0x2A->0x2F Reserved
+ */
+#define WIN_WRITE 0x30 /* 28-Bit */
+#define WIN_WRITE_ONCE 0x31 /* 28-Bit without retries */
+#define WIN_WRITE_LONG 0x32 /* 28-Bit */
+#define WIN_WRITE_LONG_ONCE 0x33 /* 28-Bit without retries */
+#define WIN_WRITE_EXT 0x34 /* 48-Bit */
+#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */
+#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */
+#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */
+#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */
+#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */
+/*
+ * 0x3A->0x3B Reserved
+ */
+#define WIN_WRITE_VERIFY 0x3C /* 28-Bit */
+/*
+ * 0x3D->0x3F Reserved
+ */
+#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */
+#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - without retries */
+#define WIN_VERIFY_EXT 0x42 /* 48-Bit */
+/*
+ * 0x43->0x4F Reserved
+ */
+#define WIN_FORMAT 0x50
+/*
+ * 0x51->0x5F Reserved
+ */
+#define WIN_INIT 0x60
+/*
+ * 0x61->0x5F Reserved
+ */
+#define WIN_SEEK 0x70 /* 0x70-0x7F Reserved */
+#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */
+#define WIN_DIAGNOSE 0x90
+#define WIN_SPECIFY 0x91 /* set drive geometry translation */
+#define WIN_DOWNLOAD_MICROCODE 0x92
+#define WIN_STANDBYNOW2 0x94
+#define WIN_STANDBY2 0x96
+#define WIN_SETIDLE2 0x97
+#define WIN_CHECKPOWERMODE2 0x98
+#define WIN_SLEEPNOW2 0x99
+/*
+ * 0x9A VENDOR
+ */
+#define WIN_PACKETCMD 0xA0 /* Send a packet command. */
+#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
+#define WIN_QUEUED_SERVICE 0xA2
+#define WIN_SMART 0xB0 /* self-monitoring and reporting */
+#define CFA_ERASE_SECTORS 0xC0
+#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/
+#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
+#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers */
+#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */
+#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - without retries */
+#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */
+#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - without retries */
+#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers */
+#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */
+#define WIN_GETMEDIASTATUS 0xDA
+#define WIN_ACKMEDIACHANGE 0xDB /* ATA-1, ATA-2 vendor */
+#define WIN_POSTBOOT 0xDC
+#define WIN_PREBOOT 0xDD
+#define WIN_DOORLOCK 0xDE /* lock door on removable drives */
+#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives */
+#define WIN_STANDBYNOW1 0xE0
+#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */
+#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */
+#define WIN_SETIDLE1 0xE3
+#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
+#define WIN_CHECKPOWERMODE1 0xE5
+#define WIN_SLEEPNOW1 0xE6
+#define WIN_FLUSH_CACHE 0xE7
+#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
+#define WIN_WRITE_SAME 0xE9 /* read ata-2 to use */
+ /* SET_FEATURES 0x22 or 0xDD */
+#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_MEDIAEJECT 0xED
+#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+#define EXABYTE_ENABLE_NEST 0xF0
+#define WIN_SECURITY_SET_PASS 0xF1
+#define WIN_SECURITY_UNLOCK 0xF2
+#define WIN_SECURITY_ERASE_PREPARE 0xF3
+#define WIN_SECURITY_ERASE_UNIT 0xF4
+#define WIN_SECURITY_FREEZE_LOCK 0xF5
+#define WIN_SECURITY_DISABLE 0xF6
+#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */
+#define WIN_SET_MAX 0xF9
+#define DISABLE_SEAGATE 0xFB
+
+/* set to 1 set disable mult support */
+#define MAX_MULT_SECTORS 16
+
+/* ATAPI defines */
+
+#define ATAPI_PACKET_SIZE 12
+
+/* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+#define GPCMD_BLANK 0xa1
+#define GPCMD_CLOSE_TRACK 0x5b
+#define GPCMD_FLUSH_CACHE 0x35
+#define GPCMD_FORMAT_UNIT 0x04
+#define GPCMD_GET_CONFIGURATION 0x46
+#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
+#define GPCMD_GET_PERFORMANCE 0xac
+#define GPCMD_INQUIRY 0x12
+#define GPCMD_LOAD_UNLOAD 0xa6
+#define GPCMD_MECHANISM_STATUS 0xbd
+#define GPCMD_MODE_SELECT_10 0x55
+#define GPCMD_MODE_SENSE_10 0x5a
+#define GPCMD_PAUSE_RESUME 0x4b
+#define GPCMD_PLAY_AUDIO_10 0x45
+#define GPCMD_PLAY_AUDIO_MSF 0x47
+#define GPCMD_PLAY_AUDIO_TI 0x48
+#define GPCMD_PLAY_CD 0xbc
+#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define GPCMD_READ_10 0x28
+#define GPCMD_READ_12 0xa8
+#define GPCMD_READ_CDVD_CAPACITY 0x25
+#define GPCMD_READ_CD 0xbe
+#define GPCMD_READ_CD_MSF 0xb9
+#define GPCMD_READ_DISC_INFO 0x51
+#define GPCMD_READ_DVD_STRUCTURE 0xad
+#define GPCMD_READ_FORMAT_CAPACITIES 0x23
+#define GPCMD_READ_HEADER 0x44
+#define GPCMD_READ_TRACK_RZONE_INFO 0x52
+#define GPCMD_READ_SUBCHANNEL 0x42
+#define GPCMD_READ_TOC_PMA_ATIP 0x43
+#define GPCMD_REPAIR_RZONE_TRACK 0x58
+#define GPCMD_REPORT_KEY 0xa4
+#define GPCMD_REQUEST_SENSE 0x03
+#define GPCMD_RESERVE_RZONE_TRACK 0x53
+#define GPCMD_SCAN 0xba
+#define GPCMD_SEEK 0x2b
+#define GPCMD_SEND_DVD_STRUCTURE 0xad
+#define GPCMD_SEND_EVENT 0xa2
+#define GPCMD_SEND_KEY 0xa3
+#define GPCMD_SEND_OPC 0x54
+#define GPCMD_SET_READ_AHEAD 0xa7
+#define GPCMD_SET_STREAMING 0xb6
+#define GPCMD_START_STOP_UNIT 0x1b
+#define GPCMD_STOP_PLAY_SCAN 0x4e
+#define GPCMD_TEST_UNIT_READY 0x00
+#define GPCMD_VERIFY_10 0x2f
+#define GPCMD_WRITE_10 0x2a
+#define GPCMD_WRITE_AND_VERIFY_10 0x2e
+/* This is listed as optional in ATAPI 2.6, but is (curiously)
+ * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji
+ * Table 377 as an MMC command for SCSi devices though... Most ATAPI
+ * drives support it. */
+#define GPCMD_SET_SPEED 0xbb
+/* This seems to be a SCSI specific CD-ROM opcode
+ * to play data at track/index */
+#define GPCMD_PLAYAUDIO_TI 0x48
+/*
+ * From MS Media Status Notification Support Specification. For
+ * older drives only.
+ */
+#define GPCMD_GET_MEDIA_STATUS 0xda
+
+/* Mode page codes for mode sense/set */
+#define GPMODE_R_W_ERROR_PAGE 0x01
+#define GPMODE_WRITE_PARMS_PAGE 0x05
+#define GPMODE_AUDIO_CTL_PAGE 0x0e
+#define GPMODE_POWER_PAGE 0x1a
+#define GPMODE_FAULT_FAIL_PAGE 0x1c
+#define GPMODE_TO_PROTECT_PAGE 0x1d
+#define GPMODE_CAPABILITIES_PAGE 0x2a
+#define GPMODE_ALL_PAGES 0x3f
+/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
+ * of MODE_SENSE_POWER_PAGE */
+#define GPMODE_CDROM_PAGE 0x0d
+
+#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */
+#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */
+#define ATAPI_INT_REASON_REL 0x04
+#define ATAPI_INT_REASON_TAG 0xf8
+
+/* same constants as bochs */
+#define ASC_ILLEGAL_OPCODE 0x20
+#define ASC_LOGICAL_BLOCK_OOR 0x21
+#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
+#define ASC_MEDIUM_NOT_PRESENT 0x3a
+#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
+
+#define SENSE_NONE 0
+#define SENSE_NOT_READY 2
+#define SENSE_ILLEGAL_REQUEST 5
+#define SENSE_UNIT_ATTENTION 6
+
+struct IDEState;
+
+typedef void EndTransferFunc(struct IDEState *);
+
+/* NOTE: IDEState represents in fact one drive */
+typedef struct IDEState {
+ /* ide config */
+ int is_cdrom;
+ int cylinders, heads, sectors;
+ int64_t nb_sectors;
+ int mult_sectors;
+ int identify_set;
+ uint16_t identify_data[256];
+ SetIRQFunc *set_irq;
+ void *irq_opaque;
+ int irq;
+ PCIDevice *pci_dev;
+ struct BMDMAState *bmdma;
+ int drive_serial;
+ /* ide regs */
+ uint8_t feature;
+ uint8_t error;
+ uint32_t nsector;
+ uint8_t sector;
+ uint8_t lcyl;
+ uint8_t hcyl;
+ /* other part of tf for lba48 support */
+ uint8_t hob_feature;
+ uint8_t hob_nsector;
+ uint8_t hob_sector;
+ uint8_t hob_lcyl;
+ uint8_t hob_hcyl;
+
+ uint8_t select;
+ uint8_t status;
+
+ /* 0x3f6 command, only meaningful for drive 0 */
+ uint8_t cmd;
+ /* set for lba48 access */
+ uint8_t lba48;
+ /* depends on bit 4 in select, only meaningful for drive 0 */
+ struct IDEState *cur_drive;
+ BlockDriverState *bs;
+ /* ATAPI specific */
+ uint8_t sense_key;
+ uint8_t asc;
+ int packet_transfer_size;
+ int elementary_transfer_size;
+ int io_buffer_index;
+ int lba;
+ int cd_sector_size;
+ int atapi_dma; /* true if dma is requested for the packet cmd */
+ /* ATA DMA state */
+ int io_buffer_size;
+ /* PIO transfer handling */
+ int req_nb_sectors; /* number of sectors per interrupt */
+ EndTransferFunc *end_transfer_func;
+ uint8_t *data_ptr;
+ uint8_t *data_end;
+ uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
+ QEMUTimer *sector_write_timer; /* only used for win2k instal hack */
+ uint32_t irq_count; /* counts IRQs when using win2k install hack */
+} IDEState;
+
+#define BM_STATUS_DMAING 0x01
+#define BM_STATUS_ERROR 0x02
+#define BM_STATUS_INT 0x04
+
+#define BM_CMD_START 0x01
+#define BM_CMD_READ 0x08
+
+#define IDE_TYPE_PIIX3 0
+#define IDE_TYPE_CMD646 1
+
+/* CMD646 specific */
+#define MRDMODE 0x71
+#define MRDMODE_INTR_CH0 0x04
+#define MRDMODE_INTR_CH1 0x08
+#define MRDMODE_BLK_CH0 0x10
+#define MRDMODE_BLK_CH1 0x20
+#define UDIDETCR0 0x73
+#define UDIDETCR1 0x7B
+
+typedef int IDEDMAFunc(IDEState *s,
+ target_phys_addr_t phys_addr,
+ int transfer_size1);
+
+typedef struct BMDMAState {
+ uint8_t cmd;
+ uint8_t status;
+ uint32_t addr;
+
+ struct PCIIDEState *pci_dev;
+ /* current transfer state */
+ IDEState *ide_if;
+ IDEDMAFunc *dma_cb;
+} BMDMAState;
+
+typedef struct PCIIDEState {
+ PCIDevice dev;
+ IDEState ide_if[4];
+ BMDMAState bmdma[2];
+ int type; /* see IDE_TYPE_xxx */
+} PCIIDEState;
+
+static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb);
+
+static void padstr(char *str, const char *src, int len)
+{
+ int i, v;
+ for(i = 0; i < len; i++) {
+ if (*src)
+ v = *src++;
+ else
+ v = ' ';
+ *(char *)((long)str ^ 1) = v;
+ str++;
+ }
+}
+
+static void padstr8(uint8_t *buf, int buf_size, const char *src)
+{
+ int i;
+ for(i = 0; i < buf_size; i++) {
+ if (*src)
+ buf[i] = *src++;
+ else
+ buf[i] = ' ';
+ }
+}
+
+static void put_le16(uint16_t *p, unsigned int v)
+{
+ *p = cpu_to_le16(v);
+}
+
+static void ide_identify(IDEState *s)
+{
+ uint16_t *p;
+ unsigned int oldsize;
+ char buf[20];
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
+ memset(s->io_buffer, 0, 512);
+ p = (uint16_t *)s->io_buffer;
+ put_le16(p + 0, 0x0040);
+ put_le16(p + 1, s->cylinders);
+ put_le16(p + 3, s->heads);
+ put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+ put_le16(p + 5, 512); /* XXX: retired, remove ? */
+ put_le16(p + 6, s->sectors);
+ snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
+ padstr((uint8_t *)(p + 10), buf, 20); /* serial number */
+ put_le16(p + 20, 3); /* XXX: retired, remove ? */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
+ padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
+ padstr((uint8_t *)(p + 27), "QEMU HARDDISK", 40); /* model */
+#if MAX_MULT_SECTORS > 1
+ put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
+#endif
+ put_le16(p + 48, 1); /* dword I/O */
+ put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
+ put_le16(p + 51, 0x200); /* PIO transfer cycle */
+ put_le16(p + 52, 0x200); /* DMA transfer cycle */
+ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */
+ put_le16(p + 54, s->cylinders);
+ put_le16(p + 55, s->heads);
+ put_le16(p + 56, s->sectors);
+ oldsize = s->cylinders * s->heads * s->sectors;
+ put_le16(p + 57, oldsize);
+ put_le16(p + 58, oldsize >> 16);
+ if (s->mult_sectors)
+ put_le16(p + 59, 0x100 | s->mult_sectors);
+ put_le16(p + 60, s->nb_sectors);
+ put_le16(p + 61, s->nb_sectors >> 16);
+ put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 65, 120);
+ put_le16(p + 66, 120);
+ put_le16(p + 67, 120);
+ put_le16(p + 68, 120);
+ put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+ put_le16(p + 81, 0x16); /* conforms to ata5 */
+ put_le16(p + 82, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 84, (1 << 14));
+ put_le16(p + 85, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 87, (1 << 14));
+ put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+ put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+ put_le16(p + 100, s->nb_sectors);
+ put_le16(p + 101, s->nb_sectors >> 16);
+ put_le16(p + 102, s->nb_sectors >> 32);
+ put_le16(p + 103, s->nb_sectors >> 48);
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
+}
+
+static void ide_atapi_identify(IDEState *s)
+{
+ uint16_t *p;
+ char buf[20];
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
+ memset(s->io_buffer, 0, 512);
+ p = (uint16_t *)s->io_buffer;
+ /* Removable CDROM, 50us response, 12 byte packets */
+ put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
+ snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
+ padstr((uint8_t *)(p + 10), buf, 20); /* serial number */
+ put_le16(p + 20, 3); /* buffer type */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
+ padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
+ padstr((uint8_t *)(p + 27), "QEMU CD-ROM", 40); /* model */
+ put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
+ put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
+ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
+ put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
+ put_le16(p + 64, 1); /* PIO modes */
+ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
+ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
+ put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
+ put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
+
+ put_le16(p + 71, 30); /* in ns */
+ put_le16(p + 72, 30); /* in ns */
+
+ put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
+}
+
+static void ide_set_signature(IDEState *s)
+{
+ s->select &= 0xf0; /* clear head */
+ /* put signature */
+ s->nsector = 1;
+ s->sector = 1;
+ if (s->is_cdrom) {
+ s->lcyl = 0x14;
+ s->hcyl = 0xeb;
+ } else if (s->bs) {
+ s->lcyl = 0;
+ s->hcyl = 0;
+ } else {
+ s->lcyl = 0xff;
+ s->hcyl = 0xff;
+ }
+}
+
+static inline void ide_abort_command(IDEState *s)
+{
+ s->status = READY_STAT | ERR_STAT;
+ s->error = ABRT_ERR;
+}
+
+static inline void ide_set_irq(IDEState *s)
+{
+ BMDMAState *bm = s->bmdma;
+ if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) {
+ if (bm) {
+ bm->status |= BM_STATUS_INT;
+ }
+ s->set_irq(s->irq_opaque, s->irq, 1);
+ }
+}
+
+/* prepare data transfer and tell what to do after */
+static void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
+{
+ s->end_transfer_func = end_transfer_func;
+ s->data_ptr = buf;
+ s->data_end = buf + size;
+ s->status |= DRQ_STAT;
+}
+
+static void ide_transfer_stop(IDEState *s)
+{
+ s->end_transfer_func = ide_transfer_stop;
+ s->data_ptr = s->io_buffer;
+ s->data_end = s->io_buffer;
+ s->status &= ~DRQ_STAT;
+}
+
+static int64_t ide_get_sector(IDEState *s)
+{
+ int64_t sector_num;
+ if (s->select & 0x40) {
+ /* lba */
+ if (!s->lba48) {
+ sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+ (s->lcyl << 8) | s->sector;
+ } else {
+ sector_num = ((int64_t)s->hob_hcyl << 40) |
+ ((int64_t) s->hob_lcyl << 32) |
+ ((int64_t) s->hob_sector << 24) |
+ ((int64_t) s->hcyl << 16) |
+ ((int64_t) s->lcyl << 8) | s->sector;
+ }
+ } else {
+ sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
+ (s->select & 0x0f) * s->sectors + (s->sector - 1);
+ }
+ return sector_num;
+}
+
+static void ide_set_sector(IDEState *s, int64_t sector_num)
+{
+ unsigned int cyl, r;
+ if (s->select & 0x40) {
+ if (!s->lba48) {
+ s->select = (s->select & 0xf0) | (sector_num >> 24);
+ s->hcyl = (sector_num >> 16);
+ s->lcyl = (sector_num >> 8);
+ s->sector = (sector_num);
+ } else {
+ s->sector = sector_num;
+ s->lcyl = sector_num >> 8;
+ s->hcyl = sector_num >> 16;
+ s->hob_sector = sector_num >> 24;
+ s->hob_lcyl = sector_num >> 32;
+ s->hob_hcyl = sector_num >> 40;
+ }
+ } else {
+ cyl = sector_num / (s->heads * s->sectors);
+ r = sector_num % (s->heads * s->sectors);
+ s->hcyl = cyl >> 8;
+ s->lcyl = cyl;
+ s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f);
+ s->sector = (r % s->sectors) + 1;
+ }
+}
+
+static void ide_sector_read(IDEState *s)
+{
+ int64_t sector_num;
+ int ret, n;
+
+ s->status = READY_STAT | SEEK_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
+ sector_num = ide_get_sector(s);
+ n = s->nsector;
+ if (n == 0) {
+ /* no more sector to read from disk */
+ ide_transfer_stop(s);
+ } else {
+#if defined(DEBUG_IDE)
+ printf("read sector=%Ld\n", sector_num);
+#endif
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ret = bdrv_read(s->bs, sector_num, s->io_buffer, n);
+ ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read);
+ ide_set_irq(s);
+ ide_set_sector(s, sector_num + n);
+ s->nsector -= n;
+ }
+}
+
+static int ide_read_dma_cb(IDEState *s,
+ target_phys_addr_t phys_addr,
+ int transfer_size1)
+{
+ int len, transfer_size, n;
+ int64_t sector_num;
+
+ transfer_size = transfer_size1;
+ while (transfer_size > 0) {
+ len = s->io_buffer_size - s->io_buffer_index;
+ if (len <= 0) {
+ /* transfert next data */
+ n = s->nsector;
+ if (n == 0)
+ break;
+ if (n > MAX_MULT_SECTORS)
+ n = MAX_MULT_SECTORS;
+ sector_num = ide_get_sector(s);
+ bdrv_read(s->bs, sector_num, s->io_buffer, n);
+ s->io_buffer_index = 0;
+ s->io_buffer_size = n * 512;
+ len = s->io_buffer_size;
+ sector_num += n;
+ ide_set_sector(s, sector_num);
+ s->nsector -= n;
+ }
+ if (len > transfer_size)
+ len = transfer_size;
+ cpu_physical_memory_write(phys_addr,
+ s->io_buffer + s->io_buffer_index, len);
+ s->io_buffer_index += len;
+ transfer_size -= len;
+ phys_addr += len;
+ }
+ if (s->io_buffer_index >= s->io_buffer_size && s->nsector == 0) {
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+#ifdef DEBUG_IDE_ATAPI
+ printf("dma status=0x%x\n", s->status);
+#endif
+ return 0;
+ }
+ return transfer_size1 - transfer_size;
+}
+
+static void ide_sector_read_dma(IDEState *s)
+{
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = 0;
+ ide_dma_start(s, ide_read_dma_cb);
+}
+
+static void ide_sector_write_timer_cb(void *opaque)
+{
+ IDEState *s = opaque;
+ ide_set_irq(s);
+}
+
+static void ide_sector_write(IDEState *s)
+{
+ int64_t sector_num;
+ int ret, n, n1;
+
+ s->status = READY_STAT | SEEK_STAT;
+ sector_num = ide_get_sector(s);
+#if defined(DEBUG_IDE)
+ printf("write sector=%Ld\n", sector_num);
+#endif
+ n = s->nsector;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ret = bdrv_write(s->bs, sector_num, s->io_buffer, n);
+ s->nsector -= n;
+ if (s->nsector == 0) {
+ /* no more sector to write */
+ ide_transfer_stop(s);
+ } else {
+ n1 = s->nsector;
+ if (n1 > s->req_nb_sectors)
+ n1 = s->req_nb_sectors;
+ ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write);
+ }
+ ide_set_sector(s, sector_num + n);
+
+#ifdef TARGET_I386
+ if (win2k_install_hack && ((++s->irq_count % 16) == 0)) {
+ /* It seems there is a bug in the Windows 2000 installer HDD
+ IDE driver which fills the disk with empty logs when the
+ IDE write IRQ comes too early. This hack tries to correct
+ that at the expense of slower write performances. Use this
+ option _only_ to install Windows 2000. You must disable it
+ for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 1000));
+ } else
+#endif
+ {
+ ide_set_irq(s);
+ }
+}
+
+static int ide_write_dma_cb(IDEState *s,
+ target_phys_addr_t phys_addr,
+ int transfer_size1)
+{
+ int len, transfer_size, n;
+ int64_t sector_num;
+
+ transfer_size = transfer_size1;
+ for(;;) {
+ len = s->io_buffer_size - s->io_buffer_index;
+ if (len == 0) {
+ n = s->io_buffer_size >> 9;
+ sector_num = ide_get_sector(s);
+ bdrv_write(s->bs, sector_num, s->io_buffer,
+ s->io_buffer_size >> 9);
+ sector_num += n;
+ ide_set_sector(s, sector_num);
+ s->nsector -= n;
+ n = s->nsector;
+ if (n == 0) {
+ /* end of transfer */
+ s->status = READY_STAT | SEEK_STAT;
+#ifdef TARGET_I386
+ if (win2k_install_hack && ((++s->irq_count % 16) == 0)) {
+ /* It seems there is a bug in the Windows 2000 installer
+ HDD IDE driver which fills the disk with empty logs
+ when the IDE write IRQ comes too early. This hack tries
+ to correct that at the expense of slower write
+ performances. Use this option _only_ to install Windows
+ 2000. You must disable it for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 1000));
+ } else
+#endif
+ ide_set_irq(s);
+ return 0;
+ }
+ if (n > MAX_MULT_SECTORS)
+ n = MAX_MULT_SECTORS;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = n * 512;
+ len = s->io_buffer_size;
+ }
+ if (transfer_size <= 0)
+ break;
+ if (len > transfer_size)
+ len = transfer_size;
+ cpu_physical_memory_read(phys_addr,
+ s->io_buffer + s->io_buffer_index, len);
+ s->io_buffer_index += len;
+ transfer_size -= len;
+ phys_addr += len;
+ }
+ return transfer_size1 - transfer_size;
+}
+
+static void ide_sector_write_dma(IDEState *s)
+{
+ int n;
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ n = s->nsector;
+ if (n > MAX_MULT_SECTORS)
+ n = MAX_MULT_SECTORS;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = n * 512;
+ ide_dma_start(s, ide_write_dma_cb);
+}
+
+static void ide_atapi_cmd_ok(IDEState *s)
+{
+ s->error = 0;
+ s->status = READY_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s);
+}
+
+static void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc);
+#endif
+ s->error = sense_key << 4;
+ s->status = READY_STAT | ERR_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ s->sense_key = sense_key;
+ s->asc = asc;
+ ide_set_irq(s);
+}
+
+static inline void cpu_to_ube16(uint8_t *buf, int val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val;
+}
+
+static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+}
+
+static inline int ube16_to_cpu(const uint8_t *buf)
+{
+ return (buf[0] << 8) | buf[1];
+}
+
+static inline int ube32_to_cpu(const uint8_t *buf)
+{
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+
+static void cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf,
+ int sector_size)
+{
+ switch(sector_size) {
+ case 2048:
+ bdrv_read(bs, (int64_t)lba << 2, buf, 4);
+ break;
+ case 2352:
+ /* sync bytes */
+ buf[0] = 0x00;
+ memset(buf + 1, 0xff, 10);
+ buf[11] = 0x00;
+ buf += 12;
+ /* MSF */
+ lba_to_msf(buf, lba);
+ buf[3] = 0x01; /* mode 1 data */
+ buf += 4;
+ /* data */
+ bdrv_read(bs, (int64_t)lba << 2, buf, 4);
+ buf += 2048;
+ /* ECC */
+ memset(buf, 0, 288);
+ break;
+ default:
+ break;
+ }
+}
+
+/* The whole ATAPI transfer logic is handled in this function */
+static void ide_atapi_cmd_reply_end(IDEState *s)
+{
+ int byte_count_limit, size;
+#ifdef DEBUG_IDE_ATAPI
+ printf("reply: tx_size=%d elem_tx_size=%d index=%d\n",
+ s->packet_transfer_size,
+ s->elementary_transfer_size,
+ s->io_buffer_index);
+#endif
+ if (s->packet_transfer_size <= 0) {
+ /* end of transfer */
+ ide_transfer_stop(s);
+ s->status = READY_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s);
+#ifdef DEBUG_IDE_ATAPI
+ printf("status=0x%x\n", s->status);
+#endif
+ } else {
+ /* see if a new sector must be read */
+ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
+ cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
+ s->lba++;
+ s->io_buffer_index = 0;
+ }
+ if (s->elementary_transfer_size > 0) {
+ /* there are some data left to transmit in this elementary
+ transfer */
+ size = s->cd_sector_size - s->io_buffer_index;
+ if (size > s->elementary_transfer_size)
+ size = s->elementary_transfer_size;
+ ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
+ size, ide_atapi_cmd_reply_end);
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+ } else {
+ /* a new transfer is needed */
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ byte_count_limit = s->lcyl | (s->hcyl << 8);
+#ifdef DEBUG_IDE_ATAPI
+ printf("byte_count_limit=%d\n", byte_count_limit);
+#endif
+ if (byte_count_limit == 0xffff)
+ byte_count_limit--;
+ size = s->packet_transfer_size;
+ if (size > byte_count_limit) {
+ /* byte count limit must be even if this case */
+ if (byte_count_limit & 1)
+ byte_count_limit--;
+ size = byte_count_limit;
+ }
+ s->lcyl = size;
+ s->hcyl = size >> 8;
+ s->elementary_transfer_size = size;
+ /* we cannot transmit more than one sector at a time */
+ if (s->lba != -1) {
+ if (size > (s->cd_sector_size - s->io_buffer_index))
+ size = (s->cd_sector_size - s->io_buffer_index);
+ }
+ ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
+ size, ide_atapi_cmd_reply_end);
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+ ide_set_irq(s);
+#ifdef DEBUG_IDE_ATAPI
+ printf("status=0x%x\n", s->status);
+#endif
+ }
+ }
+}
+
+/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
+static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
+{
+ if (size > max_size)
+ size = max_size;
+ s->lba = -1; /* no sector read */
+ s->packet_transfer_size = size;
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = 0;
+
+ s->status = READY_STAT;
+ ide_atapi_cmd_reply_end(s);
+}
+
+/* start a CD-CDROM read command */
+static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+ s->lba = lba;
+ s->packet_transfer_size = nb_sectors * sector_size;
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = sector_size;
+ s->cd_sector_size = sector_size;
+
+ s->status = READY_STAT;
+ ide_atapi_cmd_reply_end(s);
+}
+
+/* ATAPI DMA support */
+static int ide_atapi_cmd_read_dma_cb(IDEState *s,
+ target_phys_addr_t phys_addr,
+ int transfer_size1)
+{
+ int len, transfer_size;
+
+ transfer_size = transfer_size1;
+ while (transfer_size > 0) {
+#ifdef DEBUG_IDE_ATAPI
+ printf("transfer_size: %d phys_addr=%08x\n", transfer_size, phys_addr);
+#endif
+ if (s->packet_transfer_size <= 0)
+ break;
+ len = s->cd_sector_size - s->io_buffer_index;
+ if (len <= 0) {
+ /* transfert next data */
+ cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
+ s->lba++;
+ s->io_buffer_index = 0;
+ len = s->cd_sector_size;
+ }
+ if (len > transfer_size)
+ len = transfer_size;
+ cpu_physical_memory_write(phys_addr,
+ s->io_buffer + s->io_buffer_index, len);
+ s->packet_transfer_size -= len;
+ s->io_buffer_index += len;
+ transfer_size -= len;
+ phys_addr += len;
+ }
+ if (s->packet_transfer_size <= 0) {
+ s->status = READY_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s);
+#ifdef DEBUG_IDE_ATAPI
+ printf("dma status=0x%x\n", s->status);
+#endif
+ return 0;
+ }
+ return transfer_size1 - transfer_size;
+}
+
+/* start a CD-CDROM read command with DMA */
+/* XXX: test if DMA is available */
+static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+ s->lba = lba;
+ s->packet_transfer_size = nb_sectors * sector_size;
+ s->io_buffer_index = sector_size;
+ s->cd_sector_size = sector_size;
+
+ s->status = READY_STAT | DRQ_STAT;
+ ide_dma_start(s, ide_atapi_cmd_read_dma_cb);
+}
+
+static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("read: LBA=%d nb_sectors=%d\n", lba, nb_sectors);
+#endif
+ if (s->atapi_dma) {
+ ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
+ } else {
+ ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
+ }
+}
+
+static void ide_atapi_cmd(IDEState *s)
+{
+ const uint8_t *packet;
+ uint8_t *buf;
+ int max_len;
+
+ packet = s->io_buffer;
+ buf = s->io_buffer;
+#ifdef DEBUG_IDE_ATAPI
+ {
+ int i;
+ printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
+ for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
+ printf(" %02x", packet[i]);
+ }
+ printf("\n");
+ }
+#endif
+ switch(s->io_buffer[0]) {
+ case GPCMD_TEST_UNIT_READY:
+ if (bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_ok(s);
+ } else {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ }
+ break;
+ case GPCMD_MODE_SENSE_10:
+ {
+ int action, code;
+ max_len = ube16_to_cpu(packet + 7);
+ action = packet[2] >> 6;
+ code = packet[2] & 0x3f;
+ switch(action) {
+ case 0: /* current values */
+ switch(code) {
+ case 0x01: /* error recovery */
+ cpu_to_ube16(&buf[0], 16 + 6);
+ buf[2] = 0x70;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ buf[8] = 0x01;
+ buf[9] = 0x06;
+ buf[10] = 0x00;
+ buf[11] = 0x05;
+ buf[12] = 0x00;
+ buf[13] = 0x00;
+ buf[14] = 0x00;
+ buf[15] = 0x00;
+ ide_atapi_cmd_reply(s, 16, max_len);
+ break;
+ case 0x2a:
+ cpu_to_ube16(&buf[0], 28 + 6);
+ buf[2] = 0x70;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ buf[8] = 0x2a;
+ buf[9] = 0x12;
+ buf[10] = 0x00;
+ buf[11] = 0x00;
+
+ buf[12] = 0x70;
+ buf[13] = 3 << 5;
+ buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
+ if (bdrv_is_locked(s->bs))
+ buf[6] |= 1 << 1;
+ buf[15] = 0x00;
+ cpu_to_ube16(&buf[16], 706);
+ buf[18] = 0;
+ buf[19] = 2;
+ cpu_to_ube16(&buf[20], 512);
+ cpu_to_ube16(&buf[22], 706);
+ buf[24] = 0;
+ buf[25] = 0;
+ buf[26] = 0;
+ buf[27] = 0;
+ ide_atapi_cmd_reply(s, 28, max_len);
+ break;
+ default:
+ goto error_cmd;
+ }
+ break;
+ case 1: /* changeable values */
+ goto error_cmd;
+ case 2: /* default values */
+ goto error_cmd;
+ default:
+ case 3: /* saved values */
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+ break;
+ }
+ }
+ break;
+ case GPCMD_REQUEST_SENSE:
+ max_len = packet[4];
+ memset(buf, 0, 18);
+ buf[0] = 0x70 | (1 << 7);
+ buf[2] = s->sense_key;
+ buf[7] = 10;
+ buf[12] = s->asc;
+ ide_atapi_cmd_reply(s, 18, max_len);
+ break;
+ case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ if (bdrv_is_inserted(s->bs)) {
+ bdrv_set_locked(s->bs, packet[4] & 1);
+ ide_atapi_cmd_ok(s);
+ } else {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ }
+ break;
+ case GPCMD_READ_10:
+ case GPCMD_READ_12:
+ {
+ int nb_sectors, lba;
+
+ if (!bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ if (packet[0] == GPCMD_READ_10)
+ nb_sectors = ube16_to_cpu(packet + 7);
+ else
+ nb_sectors = ube32_to_cpu(packet + 6);
+ lba = ube32_to_cpu(packet + 2);
+ if (nb_sectors == 0) {
+ ide_atapi_cmd_ok(s);
+ break;
+ }
+ if (((int64_t)(lba + nb_sectors) << 2) > s->nb_sectors) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+ }
+ break;
+ case GPCMD_READ_CD:
+ {
+ int nb_sectors, lba, transfer_request;
+
+ if (!bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
+ lba = ube32_to_cpu(packet + 2);
+ if (nb_sectors == 0) {
+ ide_atapi_cmd_ok(s);
+ break;
+ }
+ if (((int64_t)(lba + nb_sectors) << 2) > s->nb_sectors) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ transfer_request = packet[9];
+ switch(transfer_request & 0xf8) {
+ case 0x00:
+ /* nothing */
+ ide_atapi_cmd_ok(s);
+ break;
+ case 0x10:
+ /* normal read */
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+ break;
+ case 0xf8:
+ /* read all data */
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
+ break;
+ default:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+ break;
+ case GPCMD_SEEK:
+ {
+ int lba;
+ if (!bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ lba = ube32_to_cpu(packet + 2);
+ if (((int64_t)lba << 2) > s->nb_sectors) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ ide_atapi_cmd_ok(s);
+ }
+ break;
+ case GPCMD_START_STOP_UNIT:
+ {
+ int start, eject;
+ start = packet[4] & 1;
+ eject = (packet[4] >> 1) & 1;
+
+ if (eject && !start) {
+ /* eject the disk */
+ bdrv_close(s->bs);
+ }
+ ide_atapi_cmd_ok(s);
+ }
+ break;
+ case GPCMD_MECHANISM_STATUS:
+ {
+ max_len = ube16_to_cpu(packet + 8);
+ cpu_to_ube16(buf, 0);
+ /* no current LBA */
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 1;
+ cpu_to_ube16(buf + 6, 0);
+ ide_atapi_cmd_reply(s, 8, max_len);
+ }
+ break;
+ case GPCMD_READ_TOC_PMA_ATIP:
+ {
+ int format, msf, start_track, len;
+
+ if (!bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ max_len = ube16_to_cpu(packet + 7);
+ format = packet[9] >> 6;
+ msf = (packet[1] >> 1) & 1;
+ start_track = packet[6];
+ switch(format) {
+ case 0:
+ len = cdrom_read_toc(s->nb_sectors >> 2, buf, msf, start_track);
+ if (len < 0)
+ goto error_cmd;
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
+ case 1:
+ /* multi session : only a single session defined */
+ memset(buf, 0, 12);
+ buf[1] = 0x0a;
+ buf[2] = 0x01;
+ buf[3] = 0x01;
+ ide_atapi_cmd_reply(s, 12, max_len);
+ break;
+ case 2:
+ len = cdrom_read_toc_raw(s->nb_sectors >> 2, buf, msf, start_track);
+ if (len < 0)
+ goto error_cmd;
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
+ default:
+ error_cmd:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+ break;
+ case GPCMD_READ_CDVD_CAPACITY:
+ if (!bdrv_is_inserted(s->bs)) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ /* NOTE: it is really the number of sectors minus 1 */
+ cpu_to_ube32(buf, (s->nb_sectors >> 2) - 1);
+ cpu_to_ube32(buf + 4, 2048);
+ ide_atapi_cmd_reply(s, 8, 8);
+ break;
+ case GPCMD_INQUIRY:
+ max_len = packet[4];
+ buf[0] = 0x05; /* CD-ROM */
+ buf[1] = 0x80; /* removable */
+ buf[2] = 0x00; /* ISO */
+ buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
+ buf[4] = 31; /* additionnal length */
+ buf[5] = 0; /* reserved */
+ buf[6] = 0; /* reserved */
+ buf[7] = 0; /* reserved */
+ padstr8(buf + 8, 8, "QEMU");
+ padstr8(buf + 16, 16, "QEMU CD-ROM");
+ padstr8(buf + 32, 4, QEMU_VERSION);
+ ide_atapi_cmd_reply(s, 36, max_len);
+ break;
+ default:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_ILLEGAL_OPCODE);
+ break;
+ }
+}
+
+/* called when the inserted state of the media has changed */
+static void cdrom_change_cb(void *opaque)
+{
+ IDEState *s = opaque;
+ int64_t nb_sectors;
+
+ /* XXX: send interrupt too */
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ s->nb_sectors = nb_sectors;
+}
+
+static void ide_cmd_lba48_transform(IDEState *s, int lba48)
+{
+ s->lba48 = lba48;
+
+ /* handle the 'magic' 0 nsector count conversion here. to avoid
+ * fiddling with the rest of the read logic, we just store the
+ * full sector count in ->nsector and ignore ->hob_nsector from now
+ */
+ if (!s->lba48) {
+ if (!s->nsector)
+ s->nsector = 256;
+ } else {
+ if (!s->nsector && !s->hob_nsector)
+ s->nsector = 65536;
+ else {
+ int lo = s->nsector;
+ int hi = s->hob_nsector;
+
+ s->nsector = (hi << 8) | lo;
+ }
+ }
+}
+
+static void ide_clear_hob(IDEState *ide_if)
+{
+ /* any write clears HOB high bit of device control register */
+ ide_if[0].select &= ~(1 << 7);
+ ide_if[1].select &= ~(1 << 7);
+}
+
+static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s;
+ int unit, n;
+ int lba48 = 0;
+
+#ifdef DEBUG_IDE
+ printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+
+ addr &= 7;
+ switch(addr) {
+ case 0:
+ break;
+ case 1:
+ ide_clear_hob(ide_if);
+ /* NOTE: data is written to the two drives */
+ ide_if[0].hob_feature = ide_if[0].feature;
+ ide_if[1].hob_feature = ide_if[1].feature;
+ ide_if[0].feature = val;
+ ide_if[1].feature = val;
+ break;
+ case 2:
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_nsector = ide_if[0].nsector;
+ ide_if[1].hob_nsector = ide_if[1].nsector;
+ ide_if[0].nsector = val;
+ ide_if[1].nsector = val;
+ break;
+ case 3:
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_sector = ide_if[0].sector;
+ ide_if[1].hob_sector = ide_if[1].sector;
+ ide_if[0].sector = val;
+ ide_if[1].sector = val;
+ break;
+ case 4:
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_lcyl = ide_if[0].lcyl;
+ ide_if[1].hob_lcyl = ide_if[1].lcyl;
+ ide_if[0].lcyl = val;
+ ide_if[1].lcyl = val;
+ break;
+ case 5:
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_hcyl = ide_if[0].hcyl;
+ ide_if[1].hob_hcyl = ide_if[1].hcyl;
+ ide_if[0].hcyl = val;
+ ide_if[1].hcyl = val;
+ break;
+ case 6:
+ /* FIXME: HOB readback uses bit 7 */
+ ide_if[0].select = (val & ~0x10) | 0xa0;
+ ide_if[1].select = (val | 0x10) | 0xa0;
+ /* select drive */
+ unit = (val >> 4) & 1;
+ s = ide_if + unit;
+ ide_if->cur_drive = s;
+ break;
+ default:
+ case 7:
+ /* command */
+#if defined(DEBUG_IDE)
+ printf("ide: CMD=%02x\n", val);
+#endif
+ s = ide_if->cur_drive;
+ /* ignore commands to non existant slave */
+ if (s != ide_if && !s->bs)
+ break;
+
+ switch(val) {
+ case WIN_IDENTIFY:
+ if (s->bs && !s->is_cdrom) {
+ ide_identify(s);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+ } else {
+ if (s->is_cdrom) {
+ ide_set_signature(s);
+ }
+ ide_abort_command(s);
+ }
+ ide_set_irq(s);
+ break;
+ case WIN_SPECIFY:
+ case WIN_RECAL:
+ s->error = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_SETMULT:
+ if (s->nsector > MAX_MULT_SECTORS ||
+ s->nsector == 0 ||
+ (s->nsector & (s->nsector - 1)) != 0) {
+ ide_abort_command(s);
+ } else {
+ s->mult_sectors = s->nsector;
+ s->status = READY_STAT;
+ }
+ ide_set_irq(s);
+ break;
+ case WIN_VERIFY_EXT:
+ lba48 = 1;
+ case WIN_VERIFY:
+ case WIN_VERIFY_ONCE:
+ /* do sector number check ? */
+ ide_cmd_lba48_transform(s, lba48);
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_READ_EXT:
+ lba48 = 1;
+ case WIN_READ:
+ case WIN_READ_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->req_nb_sectors = 1;
+ ide_sector_read(s);
+ break;
+ case WIN_WRITE_EXT:
+ lba48 = 1;
+ case WIN_WRITE:
+ case WIN_WRITE_ONCE:
+ ide_cmd_lba48_transform(s, lba48);
+ s->error = 0;
+ s->status = SEEK_STAT | READY_STAT;
+ s->req_nb_sectors = 1;
+ ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
+ break;
+ case WIN_MULTREAD_EXT:
+ lba48 = 1;
+ case WIN_MULTREAD:
+ if (!s->mult_sectors)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->req_nb_sectors = s->mult_sectors;
+ ide_sector_read(s);
+ break;
+ case WIN_MULTWRITE_EXT:
+ lba48 = 1;
+ case WIN_MULTWRITE:
+ if (!s->mult_sectors)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->error = 0;
+ s->status = SEEK_STAT | READY_STAT;
+ s->req_nb_sectors = s->mult_sectors;
+ n = s->nsector;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
+ break;
+ case WIN_READDMA_EXT:
+ lba48 = 1;
+ case WIN_READDMA:
+ case WIN_READDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_read_dma(s);
+ break;
+ case WIN_WRITEDMA_EXT:
+ lba48 = 1;
+ case WIN_WRITEDMA:
+ case WIN_WRITEDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_write_dma(s);
+ break;
+ case WIN_READ_NATIVE_MAX_EXT:
+ lba48 = 1;
+ case WIN_READ_NATIVE_MAX:
+ ide_cmd_lba48_transform(s, lba48);
+ ide_set_sector(s, s->nb_sectors - 1);
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_CHECKPOWERMODE1:
+ s->nsector = 0xff; /* device active or idle */
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_SETFEATURES:
+ if (!s->bs)
+ goto abort_cmd;
+ /* XXX: valid for CDROM ? */
+ switch(s->feature) {
+ case 0x02: /* write cache enable */
+ case 0x82: /* write cache disable */
+ case 0xaa: /* read look-ahead enable */
+ case 0x55: /* read look-ahead disable */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ case 0x03: { /* set transfer mode */
+ uint8_t val = s->nsector & 0x07;
+
+ switch (s->nsector >> 3) {
+ case 0x00: /* pio default */
+ case 0x01: /* pio mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x04: /* mdma mode */
+ put_le16(s->identify_data + 63,0x07 | (1 << (val + 8)));
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x08: /* udma mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f | (1 << (val + 8)));
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ }
+ default:
+ goto abort_cmd;
+ }
+ break;
+ case WIN_FLUSH_CACHE:
+ case WIN_FLUSH_CACHE_EXT:
+ if (s->bs)
+ bdrv_flush(s->bs);
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_STANDBYNOW1:
+ case WIN_IDLEIMMEDIATE:
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ /* ATAPI commands */
+ case WIN_PIDENTIFY:
+ if (s->is_cdrom) {
+ ide_atapi_identify(s);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+ } else {
+ ide_abort_command(s);
+ }
+ ide_set_irq(s);
+ break;
+ case WIN_DIAGNOSE:
+ ide_set_signature(s);
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ s->error = 0x01;
+ break;
+ case WIN_SRST:
+ if (!s->is_cdrom)
+ goto abort_cmd;
+ ide_set_signature(s);
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ s->error = 0x01;
+ break;
+ case WIN_PACKETCMD:
+ if (!s->is_cdrom)
+ goto abort_cmd;
+ /* overlapping commands not supported */
+ if (s->feature & 0x02)
+ goto abort_cmd;
+ s->atapi_dma = s->feature & 1;
+ s->nsector = 1;
+ ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
+ ide_atapi_cmd);
+ break;
+ default:
+ abort_cmd:
+ ide_abort_command(s);
+ ide_set_irq(s);
+ break;
+ }
+ }
+}
+
+static uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
+ uint32_t addr;
+ int ret, hob;
+
+ addr = addr1 & 7;
+ /* FIXME: HOB readback uses bit 7, but it's always set right now */
+ //hob = s->select & (1 << 7);
+ hob = 0;
+ switch(addr) {
+ case 0:
+ ret = 0xff;
+ break;
+ case 1:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->error;
+ else
+ ret = s->hob_feature;
+ break;
+ case 2:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->nsector & 0xff;
+ else
+ ret = s->hob_nsector;
+ break;
+ case 3:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->sector;
+ else
+ ret = s->hob_sector;
+ break;
+ case 4:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->lcyl;
+ else
+ ret = s->hob_lcyl;
+ break;
+ case 5:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->hcyl;
+ else
+ ret = s->hob_hcyl;
+ break;
+ case 6:
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->select;
+ break;
+ default:
+ case 7:
+ if ((!ide_if[0].bs && !ide_if[1].bs) ||
+ (s != ide_if && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
+ s->set_irq(s->irq_opaque, s->irq, 0);
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("ide: read addr=0x%x val=%02x\n", addr1, ret);
+#endif
+ return ret;
+}
+
+static uint32_t ide_status_read(void *opaque, uint32_t addr)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
+ int ret;
+
+ if ((!ide_if[0].bs && !ide_if[1].bs) ||
+ (s != ide_if && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
+#ifdef DEBUG_IDE
+ printf("ide: read status addr=0x%x val=%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s;
+ int i;
+
+#ifdef DEBUG_IDE
+ printf("ide: write control addr=0x%x val=%02x\n", addr, val);
+#endif
+ /* common for both drives */
+ if (!(ide_if[0].cmd & IDE_CMD_RESET) &&
+ (val & IDE_CMD_RESET)) {
+ /* reset low to high */
+ for(i = 0;i < 2; i++) {
+ s = &ide_if[i];
+ s->status = BUSY_STAT | SEEK_STAT;
+ s->error = 0x01;
+ }
+ } else if ((ide_if[0].cmd & IDE_CMD_RESET) &&
+ !(val & IDE_CMD_RESET)) {
+ /* high to low */
+ for(i = 0;i < 2; i++) {
+ s = &ide_if[i];
+ if (s->is_cdrom)
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ else
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_signature(s);
+ }
+ }
+
+ ide_if[0].cmd = val;
+ ide_if[1].cmd = val;
+}
+
+static void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
+ uint8_t *p;
+
+ p = s->data_ptr;
+ *(uint16_t *)p = le16_to_cpu(val);
+ p += 2;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+}
+
+static uint32_t ide_data_readw(void *opaque, uint32_t addr)
+{
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
+ uint8_t *p;
+ int ret;
+ p = s->data_ptr;
+ ret = cpu_to_le16(*(uint16_t *)p);
+ p += 2;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+ return ret;
+}
+
+static void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
+ uint8_t *p;
+
+ p = s->data_ptr;
+ *(uint32_t *)p = le32_to_cpu(val);
+ p += 4;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+}
+
+static uint32_t ide_data_readl(void *opaque, uint32_t addr)
+{
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
+ uint8_t *p;
+ int ret;
+
+ p = s->data_ptr;
+ ret = cpu_to_le32(*(uint32_t *)p);
+ p += 4;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+ return ret;
+}
+
+static void ide_dummy_transfer_stop(IDEState *s)
+{
+ s->data_ptr = s->io_buffer;
+ s->data_end = s->io_buffer;
+ s->io_buffer[0] = 0xff;
+ s->io_buffer[1] = 0xff;
+ s->io_buffer[2] = 0xff;
+ s->io_buffer[3] = 0xff;
+}
+
+static void ide_reset(IDEState *s)
+{
+ s->mult_sectors = MAX_MULT_SECTORS;
+ s->cur_drive = s;
+ s->select = 0xa0;
+ s->status = READY_STAT;
+ ide_set_signature(s);
+ /* init the transfer handler so that 0xffff is returned on data
+ accesses */
+ s->end_transfer_func = ide_dummy_transfer_stop;
+ ide_dummy_transfer_stop(s);
+}
+
+struct partition {
+ uint8_t boot_ind; /* 0x80 - active */
+ uint8_t head; /* starting head */
+ uint8_t sector; /* starting sector */
+ uint8_t cyl; /* starting cylinder */
+ uint8_t sys_ind; /* What partition type */
+ uint8_t end_head; /* end head */
+ uint8_t end_sector; /* end sector */
+ uint8_t end_cyl; /* end cylinder */
+ uint32_t start_sect; /* starting sector counting from 0 */
+ uint32_t nr_sects; /* nr of sectors in partition */
+} __attribute__((packed));
+
+/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
+static int guess_disk_lchs(IDEState *s,
+ int *pcylinders, int *pheads, int *psectors)
+{
+ uint8_t buf[512];
+ int ret, i, heads, sectors, cylinders;
+ struct partition *p;
+ uint32_t nr_sects;
+
+ ret = bdrv_read(s->bs, 0, buf, 1);
+ if (ret < 0)
+ return -1;
+ /* test msdos magic */
+ if (buf[510] != 0x55 || buf[511] != 0xaa)
+ return -1;
+ for(i = 0; i < 4; i++) {
+ p = ((struct partition *)(buf + 0x1be)) + i;
+ nr_sects = le32_to_cpu(p->nr_sects);
+ if (nr_sects && p->end_head) {
+ /* We make the assumption that the partition terminates on
+ a cylinder boundary */
+ heads = p->end_head + 1;
+ sectors = p->end_sector & 63;
+ if (sectors == 0)
+ continue;
+ cylinders = s->nb_sectors / (heads * sectors);
+ if (cylinders < 1 || cylinders > 16383)
+ continue;
+ *pheads = heads;
+ *psectors = sectors;
+ *pcylinders = cylinders;
+#if 0
+ printf("guessed geometry: LCHS=%d %d %d\n",
+ cylinders, heads, sectors);
+#endif
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void ide_init2(IDEState *ide_state,
+ BlockDriverState *hd0, BlockDriverState *hd1,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
+{
+ IDEState *s;
+ static int drive_serial = 1;
+ int i, cylinders, heads, secs, translation;
+ int64_t nb_sectors;
+
+ for(i = 0; i < 2; i++) {
+ s = ide_state + i;
+ if (i == 0)
+ s->bs = hd0;
+ else
+ s->bs = hd1;
+ if (s->bs) {
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ s->nb_sectors = nb_sectors;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
+ if (cylinders != 0) {
+ s->cylinders = cylinders;
+ s->heads = heads;
+ s->sectors = secs;
+ } else {
+ if (guess_disk_lchs(s, &cylinders, &heads, &secs) == 0) {
+ if (heads > 16) {
+ /* if heads > 16, it means that a BIOS LBA
+ translation was active, so the default
+ hardware geometry is OK */
+ goto default_geometry;
+ } else {
+ s->cylinders = cylinders;
+ s->heads = heads;
+ s->sectors = secs;
+ /* disable any translation to be in sync with
+ the logical geometry */
+ translation = bdrv_get_translation_hint(s->bs);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_set_translation_hint(s->bs,
+ BIOS_ATA_TRANSLATION_NONE);
+ }
+ }
+ } else {
+ default_geometry:
+ /* if no geometry, use a standard physical disk geometry */
+ cylinders = nb_sectors / (16 * 63);
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ s->cylinders = cylinders;
+ s->heads = 16;
+ s->sectors = 63;
+ }
+ bdrv_set_geometry_hint(s->bs, s->cylinders, s->heads, s->sectors);
+ }
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ s->is_cdrom = 1;
+ bdrv_set_change_cb(s->bs, cdrom_change_cb, s);
+ }
+ }
+ s->drive_serial = drive_serial++;
+ s->set_irq = set_irq;
+ s->irq_opaque = irq_opaque;
+ s->irq = irq;
+ s->sector_write_timer = qemu_new_timer(vm_clock,
+ ide_sector_write_timer_cb, s);
+ ide_reset(s);
+ }
+}
+
+static void ide_init_ioport(IDEState *ide_state, int iobase, int iobase2)
+{
+ register_ioport_write(iobase, 8, 1, ide_ioport_write, ide_state);
+ register_ioport_read(iobase, 8, 1, ide_ioport_read, ide_state);
+ if (iobase2) {
+ register_ioport_read(iobase2, 1, 1, ide_status_read, ide_state);
+ register_ioport_write(iobase2, 1, 1, ide_cmd_write, ide_state);
+ }
+
+ /* data ports */
+ register_ioport_write(iobase, 2, 2, ide_data_writew, ide_state);
+ register_ioport_read(iobase, 2, 2, ide_data_readw, ide_state);
+ register_ioport_write(iobase, 4, 4, ide_data_writel, ide_state);
+ register_ioport_read(iobase, 4, 4, ide_data_readl, ide_state);
+}
+
+/***********************************************************/
+/* ISA IDE definitions */
+
+void isa_ide_init(int iobase, int iobase2, int irq,
+ BlockDriverState *hd0, BlockDriverState *hd1)
+{
+ IDEState *ide_state;
+
+ ide_state = qemu_mallocz(sizeof(IDEState) * 2);
+ if (!ide_state)
+ return;
+
+ ide_init2(ide_state, hd0, hd1, pic_set_irq_new, isa_pic, irq);
+ ide_init_ioport(ide_state, iobase, iobase2);
+}
+
+/***********************************************************/
+/* PCI IDE definitions */
+
+static void cmd646_update_irq(PCIIDEState *d);
+
+static void ide_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIIDEState *d = (PCIIDEState *)pci_dev;
+ IDEState *ide_state;
+
+ if (region_num <= 3) {
+ ide_state = &d->ide_if[(region_num >> 1) * 2];
+ if (region_num & 1) {
+ register_ioport_read(addr + 2, 1, 1, ide_status_read, ide_state);
+ register_ioport_write(addr + 2, 1, 1, ide_cmd_write, ide_state);
+ } else {
+ register_ioport_write(addr, 8, 1, ide_ioport_write, ide_state);
+ register_ioport_read(addr, 8, 1, ide_ioport_read, ide_state);
+
+ /* data ports */
+ register_ioport_write(addr, 2, 2, ide_data_writew, ide_state);
+ register_ioport_read(addr, 2, 2, ide_data_readw, ide_state);
+ register_ioport_write(addr, 4, 4, ide_data_writel, ide_state);
+ register_ioport_read(addr, 4, 4, ide_data_readl, ide_state);
+ }
+ }
+}
+
+/* XXX: full callback usage to prepare non blocking I/Os support -
+ error handling */
+static void ide_dma_loop(BMDMAState *bm)
+{
+ struct {
+ uint32_t addr;
+ uint32_t size;
+ } prd;
+ target_phys_addr_t cur_addr;
+ int len, i, len1;
+
+ cur_addr = bm->addr;
+ /* at most one page to avoid hanging if erroneous parameters */
+ for(i = 0; i < 512; i++) {
+ cpu_physical_memory_read(cur_addr, (uint8_t *)&prd, 8);
+ prd.addr = le32_to_cpu(prd.addr);
+ prd.size = le32_to_cpu(prd.size);
+#ifdef DEBUG_IDE
+ printf("ide: dma: prd: %08x: addr=0x%08x size=0x%08x\n",
+ (int)cur_addr, prd.addr, prd.size);
+#endif
+ len = prd.size & 0xfffe;
+ if (len == 0)
+ len = 0x10000;
+ while (len > 0) {
+ len1 = bm->dma_cb(bm->ide_if, prd.addr, len);
+ if (len1 == 0)
+ goto the_end;
+ prd.addr += len1;
+ len -= len1;
+ }
+ /* end of transfer */
+ if (prd.size & 0x80000000)
+ break;
+ cur_addr += 8;
+ }
+ /* end of transfer */
+ the_end:
+ bm->status &= ~BM_STATUS_DMAING;
+ bm->status |= BM_STATUS_INT;
+ bm->dma_cb = NULL;
+ bm->ide_if = NULL;
+}
+
+static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb)
+{
+ BMDMAState *bm = s->bmdma;
+ if(!bm)
+ return;
+ bm->ide_if = s;
+ bm->dma_cb = dma_cb;
+ if (bm->status & BM_STATUS_DMAING) {
+ ide_dma_loop(bm);
+ }
+}
+
+static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, val);
+#endif
+ if (!(val & BM_CMD_START)) {
+ /* XXX: do it better */
+ bm->status &= ~BM_STATUS_DMAING;
+ bm->cmd = val & 0x09;
+ } else {
+ bm->status |= BM_STATUS_DMAING;
+ bm->cmd = val & 0x09;
+ /* start dma transfer if possible */
+ if (bm->dma_cb)
+ ide_dma_loop(bm);
+ }
+}
+
+static uint32_t bmdma_readb(void *opaque, uint32_t addr)
+{
+ BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
+ uint32_t val;
+
+ switch(addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ val = pci_dev->dev.config[MRDMODE];
+ } else {
+ val = 0xff;
+ }
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ val = pci_dev->dev.config[UDIDETCR0];
+ else
+ val = pci_dev->dev.config[UDIDETCR1];
+ } else {
+ val = 0xff;
+ }
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
+#ifdef DEBUG_IDE
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ switch(addr & 3) {
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ pci_dev->dev.config[MRDMODE] =
+ (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
+ cmd646_update_irq(pci_dev);
+ }
+ break;
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ pci_dev->dev.config[UDIDETCR0] = val;
+ else
+ pci_dev->dev.config[UDIDETCR1] = val;
+ }
+ break;
+ }
+}
+
+static uint32_t bmdma_addr_readl(void *opaque, uint32_t addr)
+{
+ BMDMAState *bm = opaque;
+ uint32_t val;
+ val = bm->addr;
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, val);
+#endif
+ return val;
+}
+
+static void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, val);
+#endif
+ bm->addr = val & ~3;
+}
+
+static void bmdma_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIIDEState *d = (PCIIDEState *)pci_dev;
+ int i;
+
+ for(i = 0;i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+ d->ide_if[2 * i].bmdma = bm;
+ d->ide_if[2 * i + 1].bmdma = bm;
+ bm->pci_dev = (PCIIDEState *)pci_dev;
+
+ register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
+
+ register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm);
+ register_ioport_read(addr, 4, 1, bmdma_readb, bm);
+
+ register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm);
+ register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm);
+ addr += 8;
+ }
+}
+
+/* XXX: call it also when the MRDMODE is changed from the PCI config
+ registers */
+static void cmd646_update_irq(PCIIDEState *d)
+{
+ int pci_level;
+ pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
+ ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
+ pci_set_irq((PCIDevice *)d, 0, pci_level);
+}
+
+/* the PCI irq level is the logical OR of the two channels */
+static void cmd646_set_irq(void *opaque, int channel, int level)
+{
+ PCIIDEState *d = opaque;
+ int irq_mask;
+
+ irq_mask = MRDMODE_INTR_CH0 << channel;
+ if (level)
+ d->dev.config[MRDMODE] |= irq_mask;
+ else
+ d->dev.config[MRDMODE] &= ~irq_mask;
+ cmd646_update_irq(d);
+}
+
+/* CMD646 PCI IDE controller */
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+ int secondary_ide_enabled)
+{
+ PCIIDEState *d;
+ uint8_t *pci_conf;
+ int i;
+
+ d = (PCIIDEState *)pci_register_device(bus, "CMD646 IDE",
+ sizeof(PCIIDEState),
+ -1,
+ NULL, NULL);
+ d->type = IDE_TYPE_CMD646;
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0x95; // CMD646
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0x46;
+ pci_conf[0x03] = 0x06;
+
+ pci_conf[0x08] = 0x07; // IDE controller revision
+ pci_conf[0x09] = 0x8f;
+
+ pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
+ pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
+ pci_conf[0x0e] = 0x00; // header_type
+
+ if (secondary_ide_enabled) {
+ /* XXX: if not enabled, really disable the seconday IDE controller */
+ pci_conf[0x51] = 0x80; /* enable IDE1 */
+ }
+
+ pci_register_io_region((PCIDevice *)d, 0, 0x8,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 1, 0x4,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 2, 0x8,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 3, 0x4,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 4, 0x10,
+ PCI_ADDRESS_SPACE_IO, bmdma_map);
+
+ pci_conf[0x3d] = 0x01; // interrupt on pin 1
+
+ for(i = 0; i < 4; i++)
+ d->ide_if[i].pci_dev = (PCIDevice *)d;
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ cmd646_set_irq, d, 0);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ cmd646_set_irq, d, 1);
+}
+
+/* hd_table must contain 4 block drivers */
+/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */
+void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn)
+{
+ PCIIDEState *d;
+ uint8_t *pci_conf;
+
+ /* register a function 1 of PIIX3 */
+ d = (PCIIDEState *)pci_register_device(bus, "PIIX3 IDE",
+ sizeof(PCIIDEState),
+ devfn,
+ NULL, NULL);
+ d->type = IDE_TYPE_PIIX3;
+
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0x86; // Intel
+ pci_conf[0x01] = 0x80;
+ pci_conf[0x02] = 0x10;
+ pci_conf[0x03] = 0x70;
+ pci_conf[0x09] = 0x80; // legacy ATA mode
+ pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
+ pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
+ pci_conf[0x0e] = 0x00; // header_type
+
+ pci_register_io_region((PCIDevice *)d, 4, 0x10,
+ PCI_ADDRESS_SPACE_IO, bmdma_map);
+
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ pic_set_irq_new, isa_pic, 14);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ pic_set_irq_new, isa_pic, 15);
+ ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6);
+ ide_init_ioport(&d->ide_if[2], 0x170, 0x376);
+}
+
+/***********************************************************/
+/* MacIO based PowerPC IDE */
+
+/* PowerMac IDE memory IO */
+static void pmac_ide_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ ide_ioport_write(opaque, addr, val);
+ break;
+ case 8:
+ case 22:
+ ide_cmd_write(opaque, 0, val);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
+{
+ uint8_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ retval = ide_ioport_read(opaque, addr);
+ break;
+ case 8:
+ case 22:
+ retval = ide_status_read(opaque, 0);
+ break;
+ default:
+ retval = 0xFF;
+ break;
+ }
+ return retval;
+}
+
+static void pmac_ide_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ if (addr == 0) {
+ ide_data_writew(opaque, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
+{
+ uint16_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readw(opaque, 0);
+ } else {
+ retval = 0xFFFF;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap16(retval);
+#endif
+ return retval;
+}
+
+static void pmac_ide_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ if (addr == 0) {
+ ide_data_writel(opaque, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
+{
+ uint32_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readl(opaque, 0);
+ } else {
+ retval = 0xFFFFFFFF;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap32(retval);
+#endif
+ return retval;
+}
+
+static CPUWriteMemoryFunc *pmac_ide_write[] = {
+ pmac_ide_writeb,
+ pmac_ide_writew,
+ pmac_ide_writel,
+};
+
+static CPUReadMemoryFunc *pmac_ide_read[] = {
+ pmac_ide_readb,
+ pmac_ide_readw,
+ pmac_ide_readl,
+};
+
+/* hd_table must contain 4 block drivers */
+/* PowerMac uses memory mapped registers, not I/O. Return the memory
+ I/O index to access the ide. */
+int pmac_ide_init (BlockDriverState **hd_table,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
+{
+ IDEState *ide_if;
+ int pmac_ide_memory;
+
+ ide_if = qemu_mallocz(sizeof(IDEState) * 2);
+ ide_init2(&ide_if[0], hd_table[0], hd_table[1],
+ set_irq, irq_opaque, irq);
+
+ pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
+ pmac_ide_write, &ide_if[0]);
+ return pmac_ide_memory;
+}
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
new file mode 100644
index 0000000..f438af7
--- /dev/null
+++ b/hw/integratorcp.c
@@ -0,0 +1,546 @@
+/*
+ * ARM Integrator CP System emulation.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+void DMA_run (void)
+{
+}
+
+typedef struct {
+ uint32_t flash_offset;
+ uint32_t cm_osc;
+ uint32_t cm_ctrl;
+ uint32_t cm_lock;
+ uint32_t cm_auxosc;
+ uint32_t cm_sdram;
+ uint32_t cm_init;
+ uint32_t cm_flags;
+ uint32_t cm_nvflags;
+ uint32_t int_level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+} integratorcm_state;
+
+static uint8_t integrator_spd[128] = {
+ 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1,
+ 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40
+};
+
+static uint32_t integratorcm_read(void *opaque, target_phys_addr_t offset)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ offset -= 0x10000000;
+ if (offset >= 0x100 && offset < 0x200) {
+ /* CM_SPD */
+ if (offset >= 0x180)
+ return 0;
+ return integrator_spd[offset >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* CM_ID */
+ return 0x411a3001;
+ case 1: /* CM_PROC */
+ return 0;
+ case 2: /* CM_OSC */
+ return s->cm_osc;
+ case 3: /* CM_CTRL */
+ return s->cm_ctrl;
+ case 4: /* CM_STAT */
+ return 0x00100000;
+ case 5: /* CM_LOCK */
+ if (s->cm_lock == 0xa05f) {
+ return 0x1a05f;
+ } else {
+ return s->cm_lock;
+ }
+ case 6: /* CM_LMBUSCNT */
+ /* ??? High frequency timer. */
+ cpu_abort(cpu_single_env, "integratorcm_read: CM_LMBUSCNT");
+ case 7: /* CM_AUXOSC */
+ return s->cm_auxosc;
+ case 8: /* CM_SDRAM */
+ return s->cm_sdram;
+ case 9: /* CM_INIT */
+ return s->cm_init;
+ case 10: /* CM_REFCT */
+ /* ??? High frequency timer. */
+ cpu_abort(cpu_single_env, "integratorcm_read: CM_REFCT");
+ case 12: /* CM_FLAGS */
+ return s->cm_flags;
+ case 14: /* CM_NVFLAGS */
+ return s->cm_nvflags;
+ case 16: /* CM_IRQ_STAT */
+ return s->int_level & s->irq_enabled;
+ case 17: /* CM_IRQ_RSTAT */
+ return s->int_level;
+ case 18: /* CM_IRQ_ENSET */
+ return s->irq_enabled;
+ case 20: /* CM_SOFT_INTSET */
+ return s->int_level & 1;
+ case 24: /* CM_FIQ_STAT */
+ return s->int_level & s->fiq_enabled;
+ case 25: /* CM_FIQ_RSTAT */
+ return s->int_level;
+ case 26: /* CM_FIQ_ENSET */
+ return s->fiq_enabled;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ return 0;
+ default:
+ cpu_abort (cpu_single_env,
+ "integratorcm_read: Unimplemented offset 0x%x\n", offset);
+ return 0;
+ }
+}
+
+static void integratorcm_do_remap(integratorcm_state *s, int flash)
+{
+ if (flash) {
+ cpu_register_physical_memory(0, 0x100000, IO_MEM_RAM);
+ } else {
+ cpu_register_physical_memory(0, 0x100000, s->flash_offset | IO_MEM_RAM);
+ }
+ //??? tlb_flush (cpu_single_env, 1);
+}
+
+static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value)
+{
+ if (value & 8) {
+ cpu_abort(cpu_single_env, "Board reset\n");
+ }
+ if ((s->cm_init ^ value) & 4) {
+ integratorcm_do_remap(s, (value & 4) == 0);
+ }
+ if ((s->cm_init ^ value) & 1) {
+ printf("Green LED %s\n", (value & 1) ? "on" : "off");
+ }
+ s->cm_init = (s->cm_init & ~ 5) | (value ^ 5);
+}
+
+static void integratorcm_update(integratorcm_state *s)
+{
+ /* ??? The CPU irq/fiq is raised when either the core module or base PIC
+ are active. */
+ if (s->int_level & (s->irq_enabled | s->fiq_enabled))
+ cpu_abort(cpu_single_env, "Core module interrupt\n");
+}
+
+static void integratorcm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ offset -= 0x10000000;
+ switch (offset >> 2) {
+ case 2: /* CM_OSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_osc = value;
+ break;
+ case 3: /* CM_CTRL */
+ integratorcm_set_ctrl(s, value);
+ break;
+ case 5: /* CM_LOCK */
+ s->cm_lock = value & 0xffff;
+ break;
+ case 7: /* CM_AUXOSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_auxosc = value;
+ break;
+ case 8: /* CM_SDRAM */
+ s->cm_sdram = value;
+ break;
+ case 9: /* CM_INIT */
+ /* ??? This can change the memory bus frequency. */
+ s->cm_init = value;
+ break;
+ case 12: /* CM_FLAGSS */
+ s->cm_flags |= value;
+ break;
+ case 13: /* CM_FLAGSC */
+ s->cm_flags &= ~value;
+ break;
+ case 14: /* CM_NVFLAGSS */
+ s->cm_nvflags |= value;
+ break;
+ case 15: /* CM_NVFLAGSS */
+ s->cm_nvflags &= ~value;
+ break;
+ case 18: /* CM_IRQ_ENSET */
+ s->irq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 19: /* CM_IRQ_ENCLR */
+ s->irq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 20: /* CM_SOFT_INTSET */
+ s->int_level |= (value & 1);
+ integratorcm_update(s);
+ break;
+ case 21: /* CM_SOFT_INTCLR */
+ s->int_level &= ~(value & 1);
+ integratorcm_update(s);
+ break;
+ case 26: /* CM_FIQ_ENSET */
+ s->fiq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 27: /* CM_FIQ_ENCLR */
+ s->fiq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ break;
+ default:
+ cpu_abort (cpu_single_env,
+ "integratorcm_write: Unimplemented offset 0x%x\n", offset);
+ break;
+ }
+}
+
+/* Integrator/CM control registers. */
+
+static CPUReadMemoryFunc *integratorcm_readfn[] = {
+ integratorcm_read,
+ integratorcm_read,
+ integratorcm_read
+};
+
+static CPUWriteMemoryFunc *integratorcm_writefn[] = {
+ integratorcm_write,
+ integratorcm_write,
+ integratorcm_write
+};
+
+static void integratorcm_init(int memsz, uint32_t flash_offset)
+{
+ int iomemtype;
+ integratorcm_state *s;
+
+ s = (integratorcm_state *)qemu_mallocz(sizeof(integratorcm_state));
+ s->cm_osc = 0x01000048;
+ /* ??? What should the high bits of this value be? */
+ s->cm_auxosc = 0x0007feff;
+ s->cm_sdram = 0x00011122;
+ if (memsz >= 256) {
+ integrator_spd[31] = 64;
+ s->cm_sdram |= 0x10;
+ } else if (memsz >= 128) {
+ integrator_spd[31] = 32;
+ s->cm_sdram |= 0x0c;
+ } else if (memsz >= 64) {
+ integrator_spd[31] = 16;
+ s->cm_sdram |= 0x08;
+ } else if (memsz >= 32) {
+ integrator_spd[31] = 4;
+ s->cm_sdram |= 0x04;
+ } else {
+ integrator_spd[31] = 2;
+ }
+ memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
+ s->cm_init = 0x00000112;
+ s->flash_offset = flash_offset;
+
+ iomemtype = cpu_register_io_memory(0, integratorcm_readfn,
+ integratorcm_writefn, s);
+ cpu_register_physical_memory(0x10000000, 0x007fffff, iomemtype);
+ integratorcm_do_remap(s, 1);
+ /* ??? Save/restore. */
+}
+
+/* Integrator/CP hardware emulation. */
+/* Primary interrupt controller. */
+
+typedef struct icp_pic_state
+{
+ arm_pic_handler handler;
+ uint32_t base;
+ uint32_t level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+ void *parent;
+ int parent_irq;
+ int parent_fiq;
+} icp_pic_state;
+
+static void icp_pic_update(icp_pic_state *s)
+{
+ uint32_t flags;
+
+ if (s->parent_irq != -1) {
+ flags = (s->level & s->irq_enabled);
+ pic_set_irq_new(s->parent, s->parent_irq, flags != 0);
+ }
+ if (s->parent_fiq != -1) {
+ flags = (s->level & s->fiq_enabled);
+ pic_set_irq_new(s->parent, s->parent_fiq, flags != 0);
+ }
+}
+
+static void icp_pic_set_irq(void *opaque, int irq, int level)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+ if (level)
+ s->level |= 1 << irq;
+ else
+ s->level &= ~(1 << irq);
+ icp_pic_update(s);
+}
+
+static uint32_t icp_pic_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* IRQ_STATUS */
+ return s->level & s->irq_enabled;
+ case 1: /* IRQ_RAWSTAT */
+ return s->level;
+ case 2: /* IRQ_ENABLESET */
+ return s->irq_enabled;
+ case 4: /* INT_SOFTSET */
+ return s->level & 1;
+ case 8: /* FRQ_STATUS */
+ return s->level & s->fiq_enabled;
+ case 9: /* FRQ_RAWSTAT */
+ return s->level;
+ case 10: /* FRQ_ENABLESET */
+ return s->fiq_enabled;
+ case 3: /* IRQ_ENABLECLR */
+ case 5: /* INT_SOFTCLR */
+ case 11: /* FRQ_ENABLECLR */
+ default:
+ printf ("icp_pic_read: Bad register offset 0x%x\n", offset);
+ return 0;
+ }
+}
+
+static void icp_pic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+ offset -= s->base;
+
+ switch (offset >> 2) {
+ case 2: /* IRQ_ENABLESET */
+ s->irq_enabled |= value;
+ break;
+ case 3: /* IRQ_ENABLECLR */
+ s->irq_enabled &= ~value;
+ break;
+ case 4: /* INT_SOFTSET */
+ if (value & 1)
+ pic_set_irq_new(s, 0, 1);
+ break;
+ case 5: /* INT_SOFTCLR */
+ if (value & 1)
+ pic_set_irq_new(s, 0, 0);
+ break;
+ case 10: /* FRQ_ENABLESET */
+ s->fiq_enabled |= value;
+ break;
+ case 11: /* FRQ_ENABLECLR */
+ s->fiq_enabled &= ~value;
+ break;
+ case 0: /* IRQ_STATUS */
+ case 1: /* IRQ_RAWSTAT */
+ case 8: /* FRQ_STATUS */
+ case 9: /* FRQ_RAWSTAT */
+ default:
+ printf ("icp_pic_write: Bad register offset 0x%x\n", offset);
+ return;
+ }
+ icp_pic_update(s);
+}
+
+static CPUReadMemoryFunc *icp_pic_readfn[] = {
+ icp_pic_read,
+ icp_pic_read,
+ icp_pic_read
+};
+
+static CPUWriteMemoryFunc *icp_pic_writefn[] = {
+ icp_pic_write,
+ icp_pic_write,
+ icp_pic_write
+};
+
+static icp_pic_state *icp_pic_init(uint32_t base, void *parent,
+ int parent_irq, int parent_fiq)
+{
+ icp_pic_state *s;
+ int iomemtype;
+
+ s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state));
+ if (!s)
+ return NULL;
+ s->handler = icp_pic_set_irq;
+ s->base = base;
+ s->parent = parent;
+ s->parent_irq = parent_irq;
+ s->parent_fiq = parent_fiq;
+ iomemtype = cpu_register_io_memory(0, icp_pic_readfn,
+ icp_pic_writefn, s);
+ cpu_register_physical_memory(base, 0x007fffff, iomemtype);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* CP control registers. */
+typedef struct {
+ uint32_t base;
+} icp_control_state;
+
+static uint32_t icp_control_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_control_state *s = (icp_control_state *)opaque;
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* CP_IDFIELD */
+ return 0x41034003;
+ case 1: /* CP_FLASHPROG */
+ return 0;
+ case 2: /* CP_INTREG */
+ return 0;
+ case 3: /* CP_DECODE */
+ return 0x11;
+ default:
+ cpu_abort (cpu_single_env, "icp_control_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void icp_control_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_control_state *s = (icp_control_state *)opaque;
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 1: /* CP_FLASHPROG */
+ case 2: /* CP_INTREG */
+ case 3: /* CP_DECODE */
+ /* Nothing interesting implemented yet. */
+ break;
+ default:
+ cpu_abort (cpu_single_env, "icp_control_write: Bad offset %x\n", offset);
+ }
+}
+static CPUReadMemoryFunc *icp_control_readfn[] = {
+ icp_control_read,
+ icp_control_read,
+ icp_control_read
+};
+
+static CPUWriteMemoryFunc *icp_control_writefn[] = {
+ icp_control_write,
+ icp_control_write,
+ icp_control_write
+};
+
+static void icp_control_init(uint32_t base)
+{
+ int iomemtype;
+ icp_control_state *s;
+
+ s = (icp_control_state *)qemu_mallocz(sizeof(icp_control_state));
+ iomemtype = cpu_register_io_memory(0, icp_control_readfn,
+ icp_control_writefn, s);
+ cpu_register_physical_memory(base, 0x007fffff, iomemtype);
+ s->base = base;
+ /* ??? Save/restore. */
+}
+
+
+/* Board init. */
+
+static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, uint32_t cpuid)
+{
+ CPUState *env;
+ uint32_t bios_offset;
+ icp_pic_state *pic;
+ void *cpu_pic;
+
+ env = cpu_init();
+ cpu_arm_set_model(env, cpuid);
+ bios_offset = ram_size + vga_ram_size;
+ /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */
+ /* ??? RAM shoud repeat to fill physical memory space. */
+ /* SDRAM at address zero*/
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+ /* And again at address 0x80000000 */
+ cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM);
+
+ integratorcm_init(ram_size >> 20, bios_offset);
+ cpu_pic = arm_pic_init_cpu(env);
+ pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
+ icp_pic_init(0xca000000, pic, 26, -1);
+ icp_pit_init(0x13000000, pic, 5);
+ pl011_init(0x16000000, pic, 1, serial_hds[0]);
+ pl011_init(0x17000000, pic, 2, serial_hds[1]);
+ icp_control_init(0xcb000000);
+ pl050_init(0x18000000, pic, 3, 0);
+ pl050_init(0x19000000, pic, 4, 1);
+ if (nd_table[0].vlan) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "smc91c111") == 0) {
+ smc91c111_init(&nd_table[0], 0xc8000000, pic, 27);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+ pl110_init(ds, 0xc0000000, pic, 22, 0);
+
+ arm_load_kernel(ram_size, kernel_filename, kernel_cmdline,
+ initrd_filename, 0x113);
+}
+
+static void integratorcp926_init(int ram_size, int vga_ram_size,
+ int boot_device, DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
+ snapshot, kernel_filename, kernel_cmdline,
+ initrd_filename, ARM_CPUID_ARM926);
+}
+
+static void integratorcp1026_init(int ram_size, int vga_ram_size,
+ int boot_device, DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
+ snapshot, kernel_filename, kernel_cmdline,
+ initrd_filename, ARM_CPUID_ARM1026);
+}
+
+QEMUMachine integratorcp926_machine = {
+ "integratorcp926",
+ "ARM Integrator/CP (ARM926EJ-S)",
+ integratorcp926_init,
+};
+
+QEMUMachine integratorcp1026_machine = {
+ "integratorcp1026",
+ "ARM Integrator/CP (ARM1026EJ-S)",
+ integratorcp1026_init,
+};
diff --git a/hw/iommu.c b/hw/iommu.c
new file mode 100644
index 0000000..e7d96c8
--- /dev/null
+++ b/hw/iommu.c
@@ -0,0 +1,258 @@
+/*
+ * QEMU SPARC iommu emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug iommu */
+//#define DEBUG_IOMMU
+
+#ifdef DEBUG_IOMMU
+#define DPRINTF(fmt, args...) \
+do { printf("IOMMU: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+#define IOMMU_NREGS (3*4096/4)
+#define IOMMU_CTRL (0x0000 >> 2)
+#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
+#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
+#define IOMMU_VERSION 0x04000000
+#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */
+#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */
+#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */
+#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */
+#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */
+#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */
+#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */
+#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */
+#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */
+#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */
+#define IOMMU_CTRL_MASK 0x0000001d
+
+#define IOMMU_BASE (0x0004 >> 2)
+#define IOMMU_BASE_MASK 0x07fffc00
+
+#define IOMMU_TLBFLUSH (0x0014 >> 2)
+#define IOMMU_TLBFLUSH_MASK 0xffffffff
+
+#define IOMMU_PGFLUSH (0x0018 >> 2)
+#define IOMMU_PGFLUSH_MASK 0xffffffff
+
+#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */
+#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */
+#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */
+#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses
+ produced by this device as pure
+ physical. */
+#define IOMMU_SBCFG_MASK 0x00010003
+
+#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */
+#define IOMMU_ARBEN_MASK 0x001f0000
+#define IOMMU_MID 0x00000008
+
+/* The format of an iopte in the page tables */
+#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */
+#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */
+#define IOPTE_WRITE 0x00000004 /* Writeable */
+#define IOPTE_VALID 0x00000002 /* IOPTE is valid */
+#define IOPTE_WAZ 0x00000001 /* Write as zeros */
+
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+#define PAGE_MASK (PAGE_SIZE - 1)
+
+typedef struct IOMMUState {
+ uint32_t addr;
+ uint32_t regs[IOMMU_NREGS];
+ uint32_t iostart;
+} IOMMUState;
+
+static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ IOMMUState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr - s->addr) >> 2;
+ switch (saddr) {
+ default:
+ DPRINTF("read reg[%d] = %x\n", saddr, s->regs[saddr]);
+ return s->regs[saddr];
+ break;
+ }
+ return 0;
+}
+
+static void iommu_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IOMMUState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr - s->addr) >> 2;
+ DPRINTF("write reg[%d] = %x\n", saddr, val);
+ switch (saddr) {
+ case IOMMU_CTRL:
+ switch (val & IOMMU_CTRL_RNGE) {
+ case IOMMU_RNGE_16MB:
+ s->iostart = 0xff000000;
+ break;
+ case IOMMU_RNGE_32MB:
+ s->iostart = 0xfe000000;
+ break;
+ case IOMMU_RNGE_64MB:
+ s->iostart = 0xfc000000;
+ break;
+ case IOMMU_RNGE_128MB:
+ s->iostart = 0xf8000000;
+ break;
+ case IOMMU_RNGE_256MB:
+ s->iostart = 0xf0000000;
+ break;
+ case IOMMU_RNGE_512MB:
+ s->iostart = 0xe0000000;
+ break;
+ case IOMMU_RNGE_1GB:
+ s->iostart = 0xc0000000;
+ break;
+ default:
+ case IOMMU_RNGE_2GB:
+ s->iostart = 0x80000000;
+ break;
+ }
+ DPRINTF("iostart = %x\n", s->iostart);
+ s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | IOMMU_VERSION);
+ break;
+ case IOMMU_BASE:
+ s->regs[saddr] = val & IOMMU_BASE_MASK;
+ break;
+ case IOMMU_TLBFLUSH:
+ DPRINTF("tlb flush %x\n", val);
+ s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK;
+ break;
+ case IOMMU_PGFLUSH:
+ DPRINTF("page flush %x\n", val);
+ s->regs[saddr] = val & IOMMU_PGFLUSH_MASK;
+ break;
+ case IOMMU_SBCFG0:
+ case IOMMU_SBCFG1:
+ case IOMMU_SBCFG2:
+ case IOMMU_SBCFG3:
+ s->regs[saddr] = val & IOMMU_SBCFG_MASK;
+ break;
+ case IOMMU_ARBEN:
+ // XXX implement SBus probing: fault when reading unmapped
+ // addresses, fault cause and address stored to MMU/IOMMU
+ s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
+ break;
+ default:
+ s->regs[saddr] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc *iommu_mem_read[3] = {
+ iommu_mem_readw,
+ iommu_mem_readw,
+ iommu_mem_readw,
+};
+
+static CPUWriteMemoryFunc *iommu_mem_write[3] = {
+ iommu_mem_writew,
+ iommu_mem_writew,
+ iommu_mem_writew,
+};
+
+uint32_t iommu_translate_local(void *opaque, uint32_t addr)
+{
+ IOMMUState *s = opaque;
+ uint32_t iopte, pa, tmppte;
+
+ iopte = s->regs[1] << 4;
+ addr &= ~s->iostart;
+ iopte += (addr >> (PAGE_SHIFT - 2)) & ~3;
+ pa = ldl_phys(iopte);
+ tmppte = pa;
+ pa = ((pa & IOPTE_PAGE) << 4) + (addr & PAGE_MASK);
+ DPRINTF("xlate dva %x => pa %x (iopte[%x] = %x)\n", addr, pa, iopte, tmppte);
+ return pa;
+}
+
+static void iommu_save(QEMUFile *f, void *opaque)
+{
+ IOMMUState *s = opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->addr);
+ for (i = 0; i < IOMMU_NREGS; i++)
+ qemu_put_be32s(f, &s->regs[i]);
+ qemu_put_be32s(f, &s->iostart);
+}
+
+static int iommu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ IOMMUState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->addr);
+ for (i = 0; i < IOMMU_NREGS; i++)
+ qemu_put_be32s(f, &s->regs[i]);
+ qemu_get_be32s(f, &s->iostart);
+
+ return 0;
+}
+
+static void iommu_reset(void *opaque)
+{
+ IOMMUState *s = opaque;
+
+ memset(s->regs, 0, IOMMU_NREGS * 4);
+ s->iostart = 0;
+ s->regs[0] = IOMMU_VERSION;
+}
+
+void *iommu_init(uint32_t addr)
+{
+ IOMMUState *s;
+ int iommu_io_memory;
+
+ s = qemu_mallocz(sizeof(IOMMUState));
+ if (!s)
+ return NULL;
+
+ s->addr = addr;
+
+ iommu_io_memory = cpu_register_io_memory(0, iommu_mem_read, iommu_mem_write, s);
+ cpu_register_physical_memory(addr, IOMMU_NREGS * 4, iommu_io_memory);
+
+ register_savevm("iommu", addr, 1, iommu_save, iommu_load, s);
+ qemu_register_reset(iommu_reset, s);
+ return s;
+}
+
diff --git a/hw/lance.c b/hw/lance.c
new file mode 100644
index 0000000..d167937
--- /dev/null
+++ b/hw/lance.c
@@ -0,0 +1,462 @@
+/*
+ * QEMU Lance emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug LANCE card */
+//#define DEBUG_LANCE
+
+#ifdef DEBUG_LANCE
+#define DPRINTF(fmt, args...) \
+do { printf("LANCE: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define LE_CSR0 0
+#define LE_CSR1 1
+#define LE_CSR2 2
+#define LE_CSR3 3
+#define LE_NREGS (LE_CSR3 + 1)
+#define LE_MAXREG LE_CSR3
+
+#define LE_RDP 0
+#define LE_RAP 1
+
+#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */
+
+#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */
+#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */
+#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */
+#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */
+#define LE_C0_MERR 0x0800 /* ME: Memory error */
+#define LE_C0_RINT 0x0400 /* Received interrupt */
+#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */
+#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */
+#define LE_C0_INTR 0x0080 /* Interrupt or error */
+#define LE_C0_INEA 0x0040 /* Interrupt enable */
+#define LE_C0_RXON 0x0020 /* Receiver on */
+#define LE_C0_TXON 0x0010 /* Transmitter on */
+#define LE_C0_TDMD 0x0008 /* Transmitter demand */
+#define LE_C0_STOP 0x0004 /* Stop the card */
+#define LE_C0_STRT 0x0002 /* Start the card */
+#define LE_C0_INIT 0x0001 /* Init the card */
+
+#define LE_C3_BSWP 0x4 /* SWAP */
+#define LE_C3_ACON 0x2 /* ALE Control */
+#define LE_C3_BCON 0x1 /* Byte control */
+
+/* Receive message descriptor 1 */
+#define LE_R1_OWN 0x80 /* Who owns the entry */
+#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */
+#define LE_R1_FRA 0x20 /* FRA: Frame error */
+#define LE_R1_OFL 0x10 /* OFL: Frame overflow */
+#define LE_R1_CRC 0x08 /* CRC error */
+#define LE_R1_BUF 0x04 /* BUF: Buffer error */
+#define LE_R1_SOP 0x02 /* Start of packet */
+#define LE_R1_EOP 0x01 /* End of packet */
+#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+#define LE_T1_OWN 0x80 /* Lance owns the packet */
+#define LE_T1_ERR 0x40 /* Error summary */
+#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */
+#define LE_T1_EONE 0x08 /* Error: one retry needed */
+#define LE_T1_EDEF 0x04 /* Error: deferred */
+#define LE_T1_SOP 0x02 /* Start of packet */
+#define LE_T1_EOP 0x01 /* End of packet */
+#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
+
+#define LE_T3_BUF 0x8000 /* Buffer error */
+#define LE_T3_UFL 0x4000 /* Error underflow */
+#define LE_T3_LCOL 0x1000 /* Error late collision */
+#define LE_T3_CLOS 0x0800 /* Error carrier loss */
+#define LE_T3_RTY 0x0400 /* Error retry */
+#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+
+#define PKT_BUF_SZ 1544
+#define RX_BUFF_SIZE PKT_BUF_SZ
+#define TX_BUFF_SIZE PKT_BUF_SZ
+
+struct lance_rx_desc {
+ unsigned short rmd0; /* low address of packet */
+ unsigned char rmd1_bits; /* descriptor bits */
+ unsigned char rmd1_hadr; /* high address of packet */
+ short length; /* This length is 2s complement (negative)!
+ * Buffer length
+ */
+ unsigned short mblength; /* This is the actual number of bytes received */
+};
+
+struct lance_tx_desc {
+ unsigned short tmd0; /* low address of packet */
+ unsigned char tmd1_bits; /* descriptor bits */
+ unsigned char tmd1_hadr; /* high address of packet */
+ short length; /* Length is 2s complement (negative)! */
+ unsigned short misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+/* On the Sparc, this block should be on a DMA region */
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode (reg. 15) */
+ unsigned char phys_addr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter. */
+
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned short rx_ptr; /* receive descriptor addr */
+ unsigned short rx_len; /* receive len and high addr */
+ unsigned short tx_ptr; /* transmit descriptor addr */
+ unsigned short tx_len; /* transmit len and high addr */
+
+ /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
+ struct lance_rx_desc brx_ring[RX_RING_SIZE];
+ struct lance_tx_desc btx_ring[TX_RING_SIZE];
+
+ char tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
+ char pad[2]; /* align rx_buf for copy_and_sum(). */
+ char rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
+};
+
+#define LEDMA_REGS 4
+#define LEDMA_MAXADDR (LEDMA_REGS * 4 - 1)
+
+typedef struct LANCEState {
+ VLANClientState *vc;
+ uint8_t macaddr[6]; /* init mac address */
+ uint32_t leptr;
+ uint16_t addr;
+ uint16_t regs[LE_NREGS];
+ uint8_t phys[6]; /* mac address */
+ int irq;
+ unsigned int rxptr, txptr;
+ uint32_t ledmaregs[LEDMA_REGS];
+} LANCEState;
+
+static void lance_send(void *opaque);
+
+static void lance_reset(void *opaque)
+{
+ LANCEState *s = opaque;
+ memcpy(s->phys, s->macaddr, 6);
+ s->rxptr = 0;
+ s->txptr = 0;
+ memset(s->regs, 0, LE_NREGS * 2);
+ s->regs[LE_CSR0] = LE_C0_STOP;
+ memset(s->ledmaregs, 0, LEDMA_REGS * 4);
+}
+
+static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ LANCEState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr & LE_MAXREG;
+ switch (saddr >> 1) {
+ case LE_RDP:
+ DPRINTF("read dreg[%d] = %4.4x\n", s->addr, s->regs[s->addr]);
+ return s->regs[s->addr];
+ case LE_RAP:
+ DPRINTF("read areg = %4.4x\n", s->addr);
+ return s->addr;
+ default:
+ DPRINTF("read unknown(%d)\n", saddr>>1);
+ break;
+ }
+ return 0;
+}
+
+static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LANCEState *s = opaque;
+ uint32_t saddr;
+ uint16_t reg;
+
+ saddr = addr & LE_MAXREG;
+ switch (saddr >> 1) {
+ case LE_RDP:
+ DPRINTF("write dreg[%d] = %4.4x\n", s->addr, val);
+ switch(s->addr) {
+ case LE_CSR0:
+ if (val & LE_C0_STOP) {
+ s->regs[LE_CSR0] = LE_C0_STOP;
+ break;
+ }
+
+ reg = s->regs[LE_CSR0];
+
+ // 1 = clear for some bits
+ reg &= ~(val & 0x7f00);
+
+ // generated bits
+ reg &= ~(LE_C0_ERR | LE_C0_INTR);
+ if (reg & 0x7100)
+ reg |= LE_C0_ERR;
+ if (reg & 0x7f00)
+ reg |= LE_C0_INTR;
+
+ // direct bit
+ reg &= ~LE_C0_INEA;
+ reg |= val & LE_C0_INEA;
+
+ // exclusive bits
+ if (val & LE_C0_INIT) {
+ reg |= LE_C0_IDON | LE_C0_INIT;
+ reg &= ~LE_C0_STOP;
+ }
+ else if (val & LE_C0_STRT) {
+ reg |= LE_C0_STRT | LE_C0_RXON | LE_C0_TXON;
+ reg &= ~LE_C0_STOP;
+ }
+
+ s->regs[LE_CSR0] = reg;
+ break;
+ case LE_CSR1:
+ s->leptr = (s->leptr & 0xffff0000) | (val & 0xffff);
+ s->regs[s->addr] = val;
+ break;
+ case LE_CSR2:
+ s->leptr = (s->leptr & 0xffff) | ((val & 0xffff) << 16);
+ s->regs[s->addr] = val;
+ break;
+ case LE_CSR3:
+ s->regs[s->addr] = val;
+ break;
+ }
+ break;
+ case LE_RAP:
+ DPRINTF("write areg = %4.4x\n", val);
+ if (val < LE_NREGS)
+ s->addr = val;
+ break;
+ default:
+ DPRINTF("write unknown(%d) = %4.4x\n", saddr>>1, val);
+ break;
+ }
+ lance_send(s);
+}
+
+static CPUReadMemoryFunc *lance_mem_read[3] = {
+ lance_mem_readw,
+ lance_mem_readw,
+ lance_mem_readw,
+};
+
+static CPUWriteMemoryFunc *lance_mem_write[3] = {
+ lance_mem_writew,
+ lance_mem_writew,
+ lance_mem_writew,
+};
+
+
+#define MIN_BUF_SIZE 60
+
+static int lance_can_receive(void *opaque)
+{
+ return 1;
+}
+
+static void lance_receive(void *opaque, const uint8_t *buf, int size)
+{
+ LANCEState *s = opaque;
+ uint32_t dmaptr = s->leptr + s->ledmaregs[3];
+ struct lance_init_block *ib;
+ unsigned int i, old_rxptr;
+ uint16_t temp16;
+ uint8_t temp8;
+
+ DPRINTF("receive size %d\n", size);
+ if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
+ return;
+
+ ib = (void *) iommu_translate(dmaptr);
+
+ old_rxptr = s->rxptr;
+ for (i = s->rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) {
+ cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1);
+ if (temp8 == (LE_R1_OWN)) {
+ s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK;
+ temp16 = size + 4;
+ bswap16s(&temp16);
+ cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].mblength, (void *) &temp16, 2);
+ cpu_physical_memory_write((uint32_t)&ib->rx_buf[i], buf, size);
+ temp8 = LE_R1_POK;
+ cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1);
+ s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
+ if (s->regs[LE_CSR0] & LE_C0_INEA)
+ pic_set_irq(s->irq, 1);
+ DPRINTF("got packet, len %d\n", size);
+ return;
+ }
+ }
+}
+
+static void lance_send(void *opaque)
+{
+ LANCEState *s = opaque;
+ uint32_t dmaptr = s->leptr + s->ledmaregs[3];
+ struct lance_init_block *ib;
+ unsigned int i, old_txptr;
+ uint16_t temp16;
+ uint8_t temp8;
+ char pkt_buf[PKT_BUF_SZ];
+
+ DPRINTF("sending packet? (csr0 %4.4x)\n", s->regs[LE_CSR0]);
+ if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
+ return;
+
+ ib = (void *) iommu_translate(dmaptr);
+
+ DPRINTF("sending packet? (dmaptr %8.8x) (ib %p) (btx_ring %p)\n", dmaptr, ib, &ib->btx_ring);
+ old_txptr = s->txptr;
+ for (i = s->txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) {
+ cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1);
+ if (temp8 == (LE_T1_POK|LE_T1_OWN)) {
+ cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].length, (void *) &temp16, 2);
+ bswap16s(&temp16);
+ temp16 = (~temp16) + 1;
+ cpu_physical_memory_read((uint32_t)&ib->tx_buf[i], pkt_buf, temp16);
+ DPRINTF("sending packet, len %d\n", temp16);
+ qemu_send_packet(s->vc, pkt_buf, temp16);
+ temp8 = LE_T1_POK;
+ cpu_physical_memory_write((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1);
+ s->txptr = (s->txptr + 1) & TX_RING_MOD_MASK;
+ s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
+ }
+ }
+ if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
+ pic_set_irq(s->irq, 1);
+}
+
+static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ LANCEState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & LEDMA_MAXADDR) >> 2;
+ return s->ledmaregs[saddr];
+}
+
+static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LANCEState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & LEDMA_MAXADDR) >> 2;
+ s->ledmaregs[saddr] = val;
+}
+
+static CPUReadMemoryFunc *ledma_mem_read[3] = {
+ ledma_mem_readl,
+ ledma_mem_readl,
+ ledma_mem_readl,
+};
+
+static CPUWriteMemoryFunc *ledma_mem_write[3] = {
+ ledma_mem_writel,
+ ledma_mem_writel,
+ ledma_mem_writel,
+};
+
+static void lance_save(QEMUFile *f, void *opaque)
+{
+ LANCEState *s = opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->leptr);
+ qemu_put_be16s(f, &s->addr);
+ for (i = 0; i < LE_NREGS; i ++)
+ qemu_put_be16s(f, &s->regs[i]);
+ qemu_put_buffer(f, s->phys, 6);
+ qemu_put_be32s(f, &s->irq);
+ for (i = 0; i < LEDMA_REGS; i ++)
+ qemu_put_be32s(f, &s->ledmaregs[i]);
+}
+
+static int lance_load(QEMUFile *f, void *opaque, int version_id)
+{
+ LANCEState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->leptr);
+ qemu_get_be16s(f, &s->addr);
+ for (i = 0; i < LE_NREGS; i ++)
+ qemu_get_be16s(f, &s->regs[i]);
+ qemu_get_buffer(f, s->phys, 6);
+ qemu_get_be32s(f, &s->irq);
+ for (i = 0; i < LEDMA_REGS; i ++)
+ qemu_get_be32s(f, &s->ledmaregs[i]);
+ return 0;
+}
+
+void lance_init(NICInfo *nd, int irq, uint32_t leaddr, uint32_t ledaddr)
+{
+ LANCEState *s;
+ int lance_io_memory, ledma_io_memory;
+
+ s = qemu_mallocz(sizeof(LANCEState));
+ if (!s)
+ return;
+
+ s->irq = irq;
+
+ lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s);
+ cpu_register_physical_memory(leaddr, 4, lance_io_memory);
+
+ ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, s);
+ cpu_register_physical_memory(ledaddr, 16, ledma_io_memory);
+
+ memcpy(s->macaddr, nd->macaddr, 6);
+
+ lance_reset(s);
+
+ s->vc = qemu_new_vlan_client(nd->vlan, lance_receive, lance_can_receive, s);
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "lance macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ s->macaddr[0],
+ s->macaddr[1],
+ s->macaddr[2],
+ s->macaddr[3],
+ s->macaddr[4],
+ s->macaddr[5]);
+
+ register_savevm("lance", leaddr, 1, lance_save, lance_load, s);
+ qemu_register_reset(lance_reset, s);
+}
+
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
new file mode 100644
index 0000000..24dff0e
--- /dev/null
+++ b/hw/lsi53c895a.c
@@ -0,0 +1,1571 @@
+/*
+ * QEMU LSI53C895A SCSI Host Bus Adapter emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+/* ??? Need to check if the {read,write}[wl] routines work properly on
+ big-endian targets. */
+
+#include "vl.h"
+
+//#define DEBUG_LSI
+//#define DEBUG_LSI_REG
+
+#ifdef DEBUG_LSI
+#define DPRINTF(fmt, args...) \
+do { printf("lsi_scsi: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "lsi_scsi: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
+#endif
+
+#define LSI_SCNTL0_TRG 0x01
+#define LSI_SCNTL0_AAP 0x02
+#define LSI_SCNTL0_EPC 0x08
+#define LSI_SCNTL0_WATN 0x10
+#define LSI_SCNTL0_START 0x20
+
+#define LSI_SCNTL1_SST 0x01
+#define LSI_SCNTL1_IARB 0x02
+#define LSI_SCNTL1_AESP 0x04
+#define LSI_SCNTL1_RST 0x08
+#define LSI_SCNTL1_CON 0x10
+#define LSI_SCNTL1_DHP 0x20
+#define LSI_SCNTL1_ADB 0x40
+#define LSI_SCNTL1_EXC 0x80
+
+#define LSI_SCNTL2_WSR 0x01
+#define LSI_SCNTL2_VUE0 0x02
+#define LSI_SCNTL2_VUE1 0x04
+#define LSI_SCNTL2_WSS 0x08
+#define LSI_SCNTL2_SLPHBEN 0x10
+#define LSI_SCNTL2_SLPMD 0x20
+#define LSI_SCNTL2_CHM 0x40
+#define LSI_SCNTL2_SDU 0x80
+
+#define LSI_ISTAT0_DIP 0x01
+#define LSI_ISTAT0_SIP 0x02
+#define LSI_ISTAT0_INTF 0x04
+#define LSI_ISTAT0_CON 0x08
+#define LSI_ISTAT0_SEM 0x10
+#define LSI_ISTAT0_SIGP 0x20
+#define LSI_ISTAT0_SRST 0x40
+#define LSI_ISTAT0_ABRT 0x80
+
+#define LSI_ISTAT1_SI 0x01
+#define LSI_ISTAT1_SRUN 0x02
+#define LSI_ISTAT1_FLSH 0x04
+
+#define LSI_SSTAT0_SDP0 0x01
+#define LSI_SSTAT0_RST 0x02
+#define LSI_SSTAT0_WOA 0x04
+#define LSI_SSTAT0_LOA 0x08
+#define LSI_SSTAT0_AIP 0x10
+#define LSI_SSTAT0_OLF 0x20
+#define LSI_SSTAT0_ORF 0x40
+#define LSI_SSTAT0_ILF 0x80
+
+#define LSI_SIST0_PAR 0x01
+#define LSI_SIST0_RST 0x02
+#define LSI_SIST0_UDC 0x04
+#define LSI_SIST0_SGE 0x08
+#define LSI_SIST0_RSL 0x10
+#define LSI_SIST0_SEL 0x20
+#define LSI_SIST0_CMP 0x40
+#define LSI_SIST0_MA 0x80
+
+#define LSI_SIST1_HTH 0x01
+#define LSI_SIST1_GEN 0x02
+#define LSI_SIST1_STO 0x04
+#define LSI_SIST1_SBMC 0x10
+
+#define LSI_SOCL_IO 0x01
+#define LSI_SOCL_CD 0x02
+#define LSI_SOCL_MSG 0x04
+#define LSI_SOCL_ATN 0x08
+#define LSI_SOCL_SEL 0x10
+#define LSI_SOCL_BSY 0x20
+#define LSI_SOCL_ACK 0x40
+#define LSI_SOCL_REQ 0x80
+
+#define LSI_DSTAT_IID 0x01
+#define LSI_DSTAT_SIR 0x04
+#define LSI_DSTAT_SSI 0x08
+#define LSI_DSTAT_ABRT 0x10
+#define LSI_DSTAT_BF 0x20
+#define LSI_DSTAT_MDPE 0x40
+#define LSI_DSTAT_DFE 0x80
+
+#define LSI_DCNTL_COM 0x01
+#define LSI_DCNTL_IRQD 0x02
+#define LSI_DCNTL_STD 0x04
+#define LSI_DCNTL_IRQM 0x08
+#define LSI_DCNTL_SSM 0x10
+#define LSI_DCNTL_PFEN 0x20
+#define LSI_DCNTL_PFF 0x40
+#define LSI_DCNTL_CLSE 0x80
+
+#define LSI_DMODE_MAN 0x01
+#define LSI_DMODE_BOF 0x02
+#define LSI_DMODE_ERMP 0x04
+#define LSI_DMODE_ERL 0x08
+#define LSI_DMODE_DIOM 0x10
+#define LSI_DMODE_SIOM 0x20
+
+#define LSI_CTEST2_DACK 0x01
+#define LSI_CTEST2_DREQ 0x02
+#define LSI_CTEST2_TEOP 0x04
+#define LSI_CTEST2_PCICIE 0x08
+#define LSI_CTEST2_CM 0x10
+#define LSI_CTEST2_CIO 0x20
+#define LSI_CTEST2_SIGP 0x40
+#define LSI_CTEST2_DDIR 0x80
+
+#define LSI_CTEST5_BL2 0x04
+#define LSI_CTEST5_DDIR 0x08
+#define LSI_CTEST5_MASR 0x10
+#define LSI_CTEST5_DFSN 0x20
+#define LSI_CTEST5_BBCK 0x40
+#define LSI_CTEST5_ADCK 0x80
+
+#define LSI_CCNTL0_DILS 0x01
+#define LSI_CCNTL0_DISFC 0x10
+#define LSI_CCNTL0_ENNDJ 0x20
+#define LSI_CCNTL0_PMJCTL 0x40
+#define LSI_CCNTL0_ENPMJ 0x80
+
+#define PHASE_DO 0
+#define PHASE_DI 1
+#define PHASE_CMD 2
+#define PHASE_ST 3
+#define PHASE_MO 6
+#define PHASE_MI 7
+#define PHASE_MASK 7
+
+/* The HBA is ID 7, so for simplicitly limit to 7 devices. */
+#define LSI_MAX_DEVS 7
+
+typedef struct {
+ PCIDevice pci_dev;
+ int mmio_io_addr;
+ int ram_io_addr;
+ uint32_t script_ram_base;
+ uint32_t data_len;
+
+ int carry; /* ??? Should this be an a visible register somewhere? */
+ int sense;
+ uint8_t msg;
+ /* Nonzero if a Wait Reselect instruction has been issued. */
+ int waiting;
+ SCSIDevice *scsi_dev[LSI_MAX_DEVS];
+ SCSIDevice *current_dev;
+ int current_lun;
+
+ uint32_t dsa;
+ uint32_t temp;
+ uint32_t dnad;
+ uint32_t dbc;
+ uint8_t istat0;
+ uint8_t istat1;
+ uint8_t dcmd;
+ uint8_t dstat;
+ uint8_t dien;
+ uint8_t sist0;
+ uint8_t sist1;
+ uint8_t sien0;
+ uint8_t sien1;
+ uint8_t mbox0;
+ uint8_t mbox1;
+ uint8_t dfifo;
+ uint8_t ctest3;
+ uint8_t ctest4;
+ uint8_t ctest5;
+ uint8_t ccntl0;
+ uint8_t ccntl1;
+ uint32_t dsp;
+ uint32_t dsps;
+ uint8_t dmode;
+ uint8_t dcntl;
+ uint8_t scntl0;
+ uint8_t scntl1;
+ uint8_t scntl2;
+ uint8_t scntl3;
+ uint8_t sstat0;
+ uint8_t sstat1;
+ uint8_t scid;
+ uint8_t sxfer;
+ uint8_t socl;
+ uint8_t sdid;
+ uint8_t sfbr;
+ uint8_t stest1;
+ uint8_t stest2;
+ uint8_t stest3;
+ uint8_t stime0;
+ uint8_t respid0;
+ uint8_t respid1;
+ uint32_t mmrs;
+ uint32_t mmws;
+ uint32_t sfs;
+ uint32_t drs;
+ uint32_t sbms;
+ uint32_t dmbs;
+ uint32_t dnad64;
+ uint32_t pmjad1;
+ uint32_t pmjad2;
+ uint32_t rbc;
+ uint32_t ua;
+ uint32_t ia;
+ uint32_t sbc;
+ uint32_t csbc;
+ uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */
+
+ /* Script ram is stored as 32-bit words in host byteorder. */
+ uint32_t script_ram[2048];
+} LSIState;
+
+static void lsi_soft_reset(LSIState *s)
+{
+ DPRINTF("Reset\n");
+ s->carry = 0;
+
+ s->waiting = 0;
+ s->dsa = 0;
+ s->dnad = 0;
+ s->dbc = 0;
+ s->temp = 0;
+ memset(s->scratch, 0, sizeof(s->scratch));
+ s->istat0 = 0;
+ s->istat1 = 0;
+ s->dcmd = 0;
+ s->dstat = 0;
+ s->dien = 0;
+ s->sist0 = 0;
+ s->sist1 = 0;
+ s->sien0 = 0;
+ s->sien1 = 0;
+ s->mbox0 = 0;
+ s->mbox1 = 0;
+ s->dfifo = 0;
+ s->ctest3 = 0;
+ s->ctest4 = 0;
+ s->ctest5 = 0;
+ s->ccntl0 = 0;
+ s->ccntl1 = 0;
+ s->dsp = 0;
+ s->dsps = 0;
+ s->dmode = 0;
+ s->dcntl = 0;
+ s->scntl0 = 0xc0;
+ s->scntl1 = 0;
+ s->scntl2 = 0;
+ s->scntl3 = 0;
+ s->sstat0 = 0;
+ s->sstat1 = 0;
+ s->scid = 7;
+ s->sxfer = 0;
+ s->socl = 0;
+ s->stest1 = 0;
+ s->stest2 = 0;
+ s->stest3 = 0;
+ s->stime0 = 0;
+ s->respid0 = 0x80;
+ s->respid1 = 0;
+ s->mmrs = 0;
+ s->mmws = 0;
+ s->sfs = 0;
+ s->drs = 0;
+ s->sbms = 0;
+ s->dmbs = 0;
+ s->dnad64 = 0;
+ s->pmjad1 = 0;
+ s->pmjad2 = 0;
+ s->rbc = 0;
+ s->ua = 0;
+ s->ia = 0;
+ s->sbc = 0;
+ s->csbc = 0;
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset);
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
+
+static inline uint32_t read_dword(LSIState *s, uint32_t addr)
+{
+ uint32_t buf;
+
+ /* Optimize reading from SCRIPTS RAM. */
+ if ((addr & 0xffffe000) == s->script_ram_base) {
+ return s->script_ram[(addr & 0x1fff) >> 2];
+ }
+ cpu_physical_memory_read(addr, (uint8_t *)&buf, 4);
+ return cpu_to_le32(buf);
+}
+
+static void lsi_stop_script(LSIState *s)
+{
+ s->istat1 &= ~LSI_ISTAT1_SRUN;
+}
+
+static void lsi_update_irq(LSIState *s)
+{
+ int level;
+ static int last_level;
+
+ /* It's unclear whether the DIP/SIP bits should be cleared when the
+ Interrupt Status Registers are cleared or when istat0 is read.
+ We currently do the formwer, which seems to work. */
+ level = 0;
+ if (s->dstat) {
+ if (s->dstat & s->dien)
+ level = 1;
+ s->istat0 |= LSI_ISTAT0_DIP;
+ } else {
+ s->istat0 &= ~LSI_ISTAT0_DIP;
+ }
+
+ if (s->sist0 || s->sist1) {
+ if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1))
+ level = 1;
+ s->istat0 |= LSI_ISTAT0_SIP;
+ } else {
+ s->istat0 &= ~LSI_ISTAT0_SIP;
+ }
+ if (s->istat0 & LSI_ISTAT0_INTF)
+ level = 1;
+
+ if (level != last_level) {
+ DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n",
+ level, s->dstat, s->sist1, s->sist0);
+ last_level = level;
+ }
+ pci_set_irq(&s->pci_dev, 0, level);
+}
+
+/* Stop SCRIPTS execution and raise a SCSI interrupt. */
+static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
+{
+ uint32_t mask0;
+ uint32_t mask1;
+
+ DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n",
+ stat1, stat0, s->sist1, s->sist0);
+ s->sist0 |= stat0;
+ s->sist1 |= stat1;
+ /* Stop processor on fatal or unmasked interrupt. As a special hack
+ we don't stop processing when raising STO. Instead continue
+ execution and stop at the next insn that accesses the SCSI bus. */
+ mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL);
+ mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH);
+ mask1 &= ~LSI_SIST1_STO;
+ if (s->sist0 & mask0 || s->sist1 & mask1) {
+ lsi_stop_script(s);
+ }
+ lsi_update_irq(s);
+}
+
+/* Stop SCRIPTS execution and raise a DMA interrupt. */
+static void lsi_script_dma_interrupt(LSIState *s, int stat)
+{
+ DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat);
+ s->dstat |= stat;
+ lsi_update_irq(s);
+ lsi_stop_script(s);
+}
+
+static inline void lsi_set_phase(LSIState *s, int phase)
+{
+ s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase;
+}
+
+static void lsi_bad_phase(LSIState *s, int out, int new_phase)
+{
+ /* Trigger a phase mismatch. */
+ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) {
+ if ((s->ccntl0 & LSI_CCNTL0_PMJCTL) || out) {
+ s->dsp = s->pmjad1;
+ } else {
+ s->dsp = s->pmjad2;
+ }
+ DPRINTF("Data phase mismatch jump to %08x\n", s->dsp);
+ } else {
+ DPRINTF("Phase mismatch interrupt\n");
+ lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+ lsi_stop_script(s);
+ }
+ lsi_set_phase(s, new_phase);
+}
+
+static void lsi_do_dma(LSIState *s, int out)
+{
+ uint8_t buf[TARGET_PAGE_SIZE];
+ uint32_t addr;
+ uint32_t count;
+ int n;
+
+ count = s->dbc;
+ addr = s->dnad;
+ DPRINTF("DMA %s addr=0x%08x len=%d avail=%d\n", out ? "out" : "in",
+ addr, count, s->data_len);
+ /* ??? Too long transfers are truncated. Don't know if this is the
+ correct behavior. */
+ if (count > s->data_len) {
+ /* If the DMA length is greater then the device data length then
+ a phase mismatch will occur. */
+ count = s->data_len;
+ s->dbc = count;
+ lsi_bad_phase(s, out, PHASE_ST);
+ }
+
+ s->csbc += count;
+
+ /* ??? Set SFBR to first data byte. */
+ while (count) {
+ n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count;
+ if (out) {
+ cpu_physical_memory_read(addr, buf, n);
+ scsi_write_data(s->current_dev, buf, n);
+ } else {
+ scsi_read_data(s->current_dev, buf, n);
+ cpu_physical_memory_write(addr, buf, n);
+ }
+ addr += n;
+ count -= n;
+ }
+}
+
+
+static void lsi_do_command(LSIState *s)
+{
+ uint8_t buf[16];
+ int n;
+
+ DPRINTF("Send command len=%d\n", s->dbc);
+ if (s->dbc > 16)
+ s->dbc = 16;
+ cpu_physical_memory_read(s->dnad, buf, s->dbc);
+ s->sfbr = buf[0];
+ n = scsi_send_command(s->current_dev, 0, buf, s->current_lun);
+ if (n > 0) {
+ s->data_len = n;
+ lsi_set_phase(s, PHASE_DI);
+ } else if (n < 0) {
+ s->data_len = -n;
+ lsi_set_phase(s, PHASE_DO);
+ }
+}
+
+static void lsi_command_complete(void *opaque, uint32_t tag, int sense)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ DPRINTF("Command complete sense=%d\n", sense);
+ s->sense = sense;
+ lsi_set_phase(s, PHASE_ST);
+}
+
+static void lsi_do_status(LSIState *s)
+{
+ DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense);
+ if (s->dbc != 1)
+ BADF("Bad Status move\n");
+ s->dbc = 1;
+ s->msg = s->sense;
+ cpu_physical_memory_write(s->dnad, &s->msg, 1);
+ s->sfbr = s->msg;
+ lsi_set_phase(s, PHASE_MI);
+ s->msg = 0; /* COMMAND COMPLETE */
+}
+
+static void lsi_disconnect(LSIState *s)
+{
+ s->scntl1 &= ~LSI_SCNTL1_CON;
+ s->sstat1 &= ~PHASE_MASK;
+}
+
+static void lsi_do_msgin(LSIState *s)
+{
+ DPRINTF("Message in len=%d\n", s->dbc);
+ s->dbc = 1;
+ s->sfbr = s->msg;
+ cpu_physical_memory_write(s->dnad, &s->msg, 1);
+ if (s->msg == 0) {
+ lsi_disconnect(s);
+ } else {
+ /* ??? Check if ATN (not yet implemented) is asserted and maybe
+ switch to PHASE_MO. */
+ lsi_set_phase(s, PHASE_CMD);
+ }
+}
+
+static void lsi_do_msgout(LSIState *s)
+{
+ uint8_t msg;
+
+ DPRINTF("MSG out len=%d\n", s->dbc);
+ if (s->dbc != 1) {
+ /* Multibyte messages not implemented. */
+ s->msg = 7; /* MESSAGE REJECT */
+ //s->dbc = 1;
+ //lsi_bad_phase(s, 1, PHASE_MI);
+ lsi_set_phase(s, PHASE_MI);
+ return;
+ }
+ cpu_physical_memory_read(s->dnad, &msg, 1);
+ s->sfbr = msg;
+ s->dnad++;
+
+ switch (msg) {
+ case 0x00:
+ DPRINTF("Got Disconnect\n");
+ lsi_disconnect(s);
+ return;
+ case 0x08:
+ DPRINTF("Got No Operation\n");
+ lsi_set_phase(s, PHASE_CMD);
+ return;
+ }
+ if ((msg & 0x80) == 0) {
+ DPRINTF("Unimplemented message 0x%d\n", msg);
+ s->msg = 7; /* MESSAGE REJECT */
+ lsi_bad_phase(s, 1, PHASE_MI);
+ return;
+ }
+ s->current_lun = msg & 7;
+ DPRINTF("Select LUN %d\n", s->current_lun);
+ lsi_set_phase(s, PHASE_CMD);
+}
+
+/* Sign extend a 24-bit value. */
+static inline int32_t sxt24(int32_t n)
+{
+ return (n << 8) >> 8;
+}
+
+static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
+{
+ int n;
+ uint8_t buf[TARGET_PAGE_SIZE];
+
+ DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count);
+ while (count) {
+ n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count;
+ cpu_physical_memory_read(src, buf, n);
+ cpu_physical_memory_write(dest, buf, n);
+ src += n;
+ dest += n;
+ count -= n;
+ }
+}
+
+static void lsi_execute_script(LSIState *s)
+{
+ uint32_t insn;
+ uint32_t addr;
+ int opcode;
+
+ s->istat1 |= LSI_ISTAT1_SRUN;
+again:
+ insn = read_dword(s, s->dsp);
+ addr = read_dword(s, s->dsp + 4);
+ DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
+ s->dsps = addr;
+ s->dcmd = insn >> 24;
+ s->dsp += 8;
+ switch (insn >> 30) {
+ case 0: /* Block move. */
+ if (s->sist1 & LSI_SIST1_STO) {
+ DPRINTF("Delayed select timeout\n");
+ lsi_stop_script(s);
+ break;
+ }
+ s->dbc = insn & 0xffffff;
+ s->rbc = s->dbc;
+ if (insn & (1 << 29)) {
+ /* Indirect addressing. */
+ addr = read_dword(s, addr);
+ } else if (insn & (1 << 28)) {
+ uint32_t buf[2];
+ int32_t offset;
+ /* Table indirect addressing. */
+ offset = sxt24(addr);
+ cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8);
+ s->dbc = cpu_to_le32(buf[0]);
+ addr = cpu_to_le32(buf[1]);
+ }
+ if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
+ DPRINTF("Wrong phase got %d expected %d\n",
+ s->sstat1 & PHASE_MASK, (insn >> 24) & 7);
+ lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+ break;
+ }
+ s->dnad = addr;
+ switch (s->sstat1 & 0x7) {
+ case PHASE_DO:
+ lsi_do_dma(s, 1);
+ break;
+ case PHASE_DI:
+ lsi_do_dma(s, 0);
+ break;
+ case PHASE_CMD:
+ lsi_do_command(s);
+ break;
+ case PHASE_ST:
+ lsi_do_status(s);
+ break;
+ case PHASE_MO:
+ lsi_do_msgout(s);
+ break;
+ case PHASE_MI:
+ lsi_do_msgin(s);
+ break;
+ default:
+ BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK);
+ exit(1);
+ }
+ s->dfifo = s->dbc & 0xff;
+ s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3);
+ s->sbc = s->dbc;
+ s->rbc -= s->dbc;
+ s->ua = addr + s->dbc;
+ /* ??? Set ESA. */
+ s->ia = s->dsp - 8;
+ break;
+
+ case 1: /* IO or Read/Write instruction. */
+ opcode = (insn >> 27) & 7;
+ if (opcode < 5) {
+ uint32_t id;
+
+ if (insn & (1 << 25)) {
+ id = read_dword(s, s->dsa + sxt24(insn));
+ } else {
+ id = addr;
+ }
+ id = (id >> 16) & 0xf;
+ if (insn & (1 << 26)) {
+ addr = s->dsp + sxt24(addr);
+ }
+ s->dnad = addr;
+ switch (opcode) {
+ case 0: /* Select */
+ s->sstat0 |= LSI_SSTAT0_WOA;
+ s->scntl1 &= ~LSI_SCNTL1_IARB;
+ s->sdid = id;
+ if (id >= LSI_MAX_DEVS || !s->scsi_dev[id]) {
+ DPRINTF("Selected absent target %d\n", id);
+ lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
+ lsi_disconnect(s);
+ break;
+ }
+ DPRINTF("Selected target %d%s\n",
+ id, insn & (1 << 3) ? " ATN" : "");
+ /* ??? Linux drivers compain when this is set. Maybe
+ it only applies in low-level mode (unimplemented).
+ lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
+ s->current_dev = s->scsi_dev[id];
+ s->scntl1 |= LSI_SCNTL1_CON;
+ if (insn & (1 << 3)) {
+ s->socl |= LSI_SOCL_ATN;
+ }
+ lsi_set_phase(s, PHASE_MO);
+ break;
+ case 1: /* Disconnect */
+ DPRINTF("Wait Disconect\n");
+ s->scntl1 &= ~LSI_SCNTL1_CON;
+ break;
+ case 2: /* Wait Reselect */
+ DPRINTF("Wait Reselect\n");
+ s->waiting = 1;
+ break;
+ case 3: /* Set */
+ DPRINTF("Set%s%s%s%s\n",
+ insn & (1 << 3) ? " ATN" : "",
+ insn & (1 << 6) ? " ACK" : "",
+ insn & (1 << 9) ? " TM" : "",
+ insn & (1 << 10) ? " CC" : "");
+ if (insn & (1 << 3)) {
+ s->socl |= LSI_SOCL_ATN;
+ lsi_set_phase(s, PHASE_MO);
+ }
+ if (insn & (1 << 9)) {
+ BADF("Target mode not implemented\n");
+ exit(1);
+ }
+ if (insn & (1 << 10))
+ s->carry = 1;
+ break;
+ case 4: /* Clear */
+ DPRINTF("Clear%s%s%s%s\n",
+ insn & (1 << 3) ? " ATN" : "",
+ insn & (1 << 6) ? " ACK" : "",
+ insn & (1 << 9) ? " TM" : "",
+ insn & (1 << 10) ? " CC" : "");
+ if (insn & (1 << 3)) {
+ s->socl &= ~LSI_SOCL_ATN;
+ }
+ if (insn & (1 << 10))
+ s->carry = 0;
+ break;
+ }
+ } else {
+ uint8_t op0;
+ uint8_t op1;
+ uint8_t data8;
+ int reg;
+ int operator;
+#ifdef DEBUG_LSI
+ static const char *opcode_names[3] =
+ {"Write", "Read", "Read-Modify-Write"};
+ static const char *operator_names[8] =
+ {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"};
+#endif
+
+ reg = ((insn >> 16) & 0x7f) | (insn & 0x80);
+ data8 = (insn >> 8) & 0xff;
+ opcode = (insn >> 27) & 7;
+ operator = (insn >> 24) & 7;
+ DPRINTF("%s reg 0x%x %s data8 %d%s\n",
+ opcode_names[opcode - 5], reg,
+ operator_names[operator], data8,
+ (insn & (1 << 23)) ? " SFBR" : "");
+ op0 = op1 = 0;
+ switch (opcode) {
+ case 5: /* From SFBR */
+ op0 = s->sfbr;
+ op1 = data8;
+ break;
+ case 6: /* To SFBR */
+ if (operator)
+ op0 = lsi_reg_readb(s, reg);
+ op1 = data8;
+ break;
+ case 7: /* Read-modify-write */
+ if (operator)
+ op0 = lsi_reg_readb(s, reg);
+ if (insn & (1 << 23)) {
+ op1 = s->sfbr;
+ } else {
+ op1 = data8;
+ }
+ break;
+ }
+
+ switch (operator) {
+ case 0: /* move */
+ op0 = op1;
+ break;
+ case 1: /* Shift left */
+ op1 = op0 >> 7;
+ op0 = (op0 << 1) | s->carry;
+ s->carry = op1;
+ break;
+ case 2: /* OR */
+ op0 |= op1;
+ break;
+ case 3: /* XOR */
+ op0 |= op1;
+ break;
+ case 4: /* AND */
+ op0 &= op1;
+ break;
+ case 5: /* SHR */
+ op1 = op0 & 1;
+ op0 = (op0 >> 1) | (s->carry << 7);
+ break;
+ case 6: /* ADD */
+ op0 += op1;
+ s->carry = op0 < op1;
+ break;
+ case 7: /* ADC */
+ op0 += op1 + s->carry;
+ if (s->carry)
+ s->carry = op0 <= op1;
+ else
+ s->carry = op0 < op1;
+ break;
+ }
+
+ switch (opcode) {
+ case 5: /* From SFBR */
+ case 7: /* Read-modify-write */
+ lsi_reg_writeb(s, reg, op0);
+ break;
+ case 6: /* To SFBR */
+ s->sfbr = op0;
+ break;
+ }
+ }
+ break;
+
+ case 2: /* Transfer Control. */
+ {
+ int cond;
+ int jmp;
+
+ if ((insn & 0x002e0000) == 0) {
+ DPRINTF("NOP\n");
+ break;
+ }
+ if (s->sist1 & LSI_SIST1_STO) {
+ DPRINTF("Delayed select timeout\n");
+ lsi_stop_script(s);
+ break;
+ }
+ cond = jmp = (insn & (1 << 19)) != 0;
+ if (cond == jmp && (insn & (1 << 21))) {
+ DPRINTF("Compare carry %d\n", s->carry == jmp);
+ cond = s->carry != 0;
+ }
+ if (cond == jmp && (insn & (1 << 17))) {
+ DPRINTF("Compare phase %d %c= %d\n",
+ (s->sstat1 & PHASE_MASK),
+ jmp ? '=' : '!',
+ ((insn >> 24) & 7));
+ cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7);
+ }
+ if (cond == jmp && (insn & (1 << 18))) {
+ uint8_t mask;
+
+ mask = (~insn >> 8) & 0xff;
+ DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n",
+ s->sfbr, mask, jmp ? '=' : '!', insn & mask);
+ cond = (s->sfbr & mask) == (insn & mask);
+ }
+ if (cond == jmp) {
+ if (insn & (1 << 23)) {
+ /* Relative address. */
+ addr = s->dsp + sxt24(addr);
+ }
+ switch ((insn >> 27) & 7) {
+ case 0: /* Jump */
+ DPRINTF("Jump to 0x%08x\n", addr);
+ s->dsp = addr;
+ break;
+ case 1: /* Call */
+ DPRINTF("Call 0x%08x\n", addr);
+ s->temp = s->dsp;
+ s->dsp = addr;
+ break;
+ case 2: /* Return */
+ DPRINTF("Return to 0x%08x\n", s->temp);
+ s->dsp = s->temp;
+ break;
+ case 3: /* Interrupt */
+ DPRINTF("Interrupt 0x%08x\n", s->dsps);
+ if ((insn & (1 << 20)) != 0) {
+ s->istat0 |= LSI_ISTAT0_INTF;
+ lsi_update_irq(s);
+ } else {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_SIR);
+ }
+ break;
+ default:
+ DPRINTF("Illegal transfer control\n");
+ lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
+ break;
+ }
+ } else {
+ DPRINTF("Control condition failed\n");
+ }
+ }
+ break;
+
+ case 3:
+ if ((insn & (1 << 29)) == 0) {
+ /* Memory move. */
+ uint32_t dest;
+ /* ??? The docs imply the destination address is loaded into
+ the TEMP register. However the Linux drivers rely on
+ the value being presrved. */
+ dest = read_dword(s, s->dsp);
+ s->dsp += 4;
+ lsi_memcpy(s, dest, addr, insn & 0xffffff);
+ } else {
+ uint8_t data[7];
+ int reg;
+ int n;
+ int i;
+
+ if (insn & (1 << 28)) {
+ addr = s->dsa + sxt24(addr);
+ }
+ n = (insn & 7);
+ reg = (insn >> 16) & 0xff;
+ if (insn & (1 << 24)) {
+ DPRINTF("Load reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
+ cpu_physical_memory_read(addr, data, n);
+ for (i = 0; i < n; i++) {
+ lsi_reg_writeb(s, reg + i, data[i]);
+ }
+ } else {
+ DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
+ for (i = 0; i < n; i++) {
+ data[i] = lsi_reg_readb(s, reg + i);
+ }
+ cpu_physical_memory_write(addr, data, n);
+ }
+ }
+ }
+ /* ??? Need to avoid infinite loops. */
+ if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
+ if (s->dcntl & LSI_DCNTL_SSM) {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_SSI);
+ } else {
+ goto again;
+ }
+ }
+ DPRINTF("SCRIPTS execution stopped\n");
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset)
+{
+ uint8_t tmp;
+#define CASE_GET_REG32(name, addr) \
+ case addr: return s->name & 0xff; \
+ case addr + 1: return (s->name >> 8) & 0xff; \
+ case addr + 2: return (s->name >> 16) & 0xff; \
+ case addr + 3: return (s->name >> 24) & 0xff;
+
+#ifdef DEBUG_LSI_REG
+ DPRINTF("Read reg %x\n", offset);
+#endif
+ switch (offset) {
+ case 0x00: /* SCNTL0 */
+ return s->scntl0;
+ case 0x01: /* SCNTL1 */
+ return s->scntl1;
+ case 0x02: /* SCNTL2 */
+ return s->scntl2;
+ case 0x03: /* SCNTL3 */
+ return s->scntl3;
+ case 0x04: /* SCID */
+ return s->scid;
+ case 0x05: /* SXFER */
+ return s->sxfer;
+ case 0x06: /* SDID */
+ return s->sdid;
+ case 0x07: /* GPREG0 */
+ return 0x7f;
+ case 0xb: /* SBCL */
+ /* ??? This is not correct. However it's (hopefully) only
+ used for diagnostics, so should be ok. */
+ return 0;
+ case 0xc: /* DSTAT */
+ tmp = s->dstat | 0x80;
+ if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
+ s->dstat = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x0d: /* SSTAT0 */
+ return s->sstat0;
+ case 0x0e: /* SSTAT1 */
+ return s->sstat1;
+ case 0x0f: /* SSTAT2 */
+ return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2;
+ CASE_GET_REG32(dsa, 0x10)
+ case 0x14: /* ISTAT0 */
+ return s->istat0;
+ case 0x16: /* MBOX0 */
+ return s->mbox0;
+ case 0x17: /* MBOX1 */
+ return s->mbox1;
+ case 0x18: /* CTEST0 */
+ return 0xff;
+ case 0x19: /* CTEST1 */
+ return 0;
+ case 0x1a: /* CTEST2 */
+ tmp = LSI_CTEST2_DACK | LSI_CTEST2_CM;
+ if (s->istat0 & LSI_ISTAT0_SIGP) {
+ s->istat0 &= ~LSI_ISTAT0_SIGP;
+ tmp |= LSI_CTEST2_SIGP;
+ }
+ return tmp;
+ case 0x1b: /* CTEST3 */
+ return s->ctest3;
+ CASE_GET_REG32(temp, 0x1c)
+ case 0x20: /* DFIFO */
+ return 0;
+ case 0x21: /* CTEST4 */
+ return s->ctest4;
+ case 0x22: /* CTEST5 */
+ return s->ctest5;
+ case 0x24: /* DBC[0:7] */
+ return s->dbc & 0xff;
+ case 0x25: /* DBC[8:15] */
+ return (s->dbc >> 8) & 0xff;
+ case 0x26: /* DBC[16->23] */
+ return (s->dbc >> 16) & 0xff;
+ case 0x27: /* DCMD */
+ return s->dcmd;
+ CASE_GET_REG32(dsp, 0x2c)
+ CASE_GET_REG32(dsps, 0x30)
+ CASE_GET_REG32(scratch[0], 0x34)
+ case 0x38: /* DMODE */
+ return s->dmode;
+ case 0x39: /* DIEN */
+ return s->dien;
+ case 0x3b: /* DCNTL */
+ return s->dcntl;
+ case 0x40: /* SIEN0 */
+ return s->sien0;
+ case 0x41: /* SIEN1 */
+ return s->sien1;
+ case 0x42: /* SIST0 */
+ tmp = s->sist0;
+ s->sist0 = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x43: /* SIST1 */
+ tmp = s->sist1;
+ s->sist1 = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x47: /* GPCNTL0 */
+ return 0x0f;
+ case 0x48: /* STIME0 */
+ return s->stime0;
+ case 0x4a: /* RESPID0 */
+ return s->respid0;
+ case 0x4b: /* RESPID1 */
+ return s->respid1;
+ case 0x4d: /* STEST1 */
+ return s->stest1;
+ case 0x4e: /* STEST2 */
+ return s->stest2;
+ case 0x4f: /* STEST3 */
+ return s->stest3;
+ case 0x52: /* STEST4 */
+ return 0xe0;
+ case 0x56: /* CCNTL0 */
+ return s->ccntl0;
+ case 0x57: /* CCNTL1 */
+ return s->ccntl1;
+ case 0x58: case 0x59: /* SBDL */
+ return 0;
+ CASE_GET_REG32(mmrs, 0xa0)
+ CASE_GET_REG32(mmws, 0xa4)
+ CASE_GET_REG32(sfs, 0xa8)
+ CASE_GET_REG32(drs, 0xac)
+ CASE_GET_REG32(sbms, 0xb0)
+ CASE_GET_REG32(dmbs, 0xb4)
+ CASE_GET_REG32(dnad64, 0xb8)
+ CASE_GET_REG32(pmjad1, 0xc0)
+ CASE_GET_REG32(pmjad2, 0xc4)
+ CASE_GET_REG32(rbc, 0xc8)
+ CASE_GET_REG32(ua, 0xcc)
+ CASE_GET_REG32(ia, 0xd4)
+ CASE_GET_REG32(sbc, 0xd8)
+ CASE_GET_REG32(csbc, 0xdc)
+ }
+ if (offset >= 0x5c && offset < 0xa0) {
+ int n;
+ int shift;
+ n = (offset - 0x58) >> 2;
+ shift = (offset & 3) * 8;
+ return (s->scratch[n] >> shift) & 0xff;
+ }
+ BADF("readb 0x%x\n", offset);
+ exit(1);
+#undef CASE_GET_REG32
+}
+
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
+{
+#define CASE_SET_REG32(name, addr) \
+ case addr : s->name &= 0xffffff00; s->name |= val; break; \
+ case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \
+ case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \
+ case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break;
+
+#ifdef DEBUG_LSI_REG
+ DPRINTF("Write reg %x = %02x\n", offset, val);
+#endif
+ switch (offset) {
+ case 0x00: /* SCNTL0 */
+ s->scntl0 = val;
+ if (val & LSI_SCNTL0_START) {
+ BADF("Start sequence not implemented\n");
+ }
+ break;
+ case 0x01: /* SCNTL1 */
+ s->scntl1 = val & ~LSI_SCNTL1_SST;
+ if (val & LSI_SCNTL1_IARB) {
+ BADF("Immediate Arbritration not implemented\n");
+ }
+ if (val & LSI_SCNTL1_RST) {
+ s->sstat0 |= LSI_SSTAT0_RST;
+ lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
+ } else {
+ s->sstat0 &= ~LSI_SSTAT0_RST;
+ }
+ break;
+ case 0x02: /* SCNTL2 */
+ val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS);
+ s->scntl3 = val;
+ break;
+ case 0x03: /* SCNTL3 */
+ s->scntl3 = val;
+ break;
+ case 0x04: /* SCID */
+ s->scid = val;
+ break;
+ case 0x05: /* SXFER */
+ s->sxfer = val;
+ break;
+ case 0x07: /* GPREG0 */
+ break;
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ /* Linux writes to these readonly registers on startup. */
+ return;
+ CASE_SET_REG32(dsa, 0x10)
+ case 0x14: /* ISTAT0 */
+ s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0);
+ if (val & LSI_ISTAT0_ABRT) {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT);
+ }
+ if (val & LSI_ISTAT0_INTF) {
+ s->istat0 &= ~LSI_ISTAT0_INTF;
+ lsi_update_irq(s);
+ }
+ if (s->waiting && val & LSI_ISTAT0_SIGP) {
+ DPRINTF("Woken by SIGP\n");
+ s->waiting = 0;
+ s->dsp = s->dnad;
+ lsi_execute_script(s);
+ }
+ if (val & LSI_ISTAT0_SRST) {
+ lsi_soft_reset(s);
+ }
+ case 0x16: /* MBOX0 */
+ s->mbox0 = val;
+ case 0x17: /* MBOX1 */
+ s->mbox1 = val;
+ case 0x1b: /* CTEST3 */
+ s->ctest3 = val & 0x0f;
+ break;
+ CASE_SET_REG32(temp, 0x1c)
+ case 0x21: /* CTEST4 */
+ if (val & 7) {
+ BADF("Unimplemented CTEST4-FBL 0x%x\n", val);
+ }
+ s->ctest4 = val;
+ break;
+ case 0x22: /* CTEST5 */
+ if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) {
+ BADF("CTEST5 DMA increment not implemented\n");
+ }
+ s->ctest5 = val;
+ break;
+ case 0x2c: /* DSPS[0:7] */
+ s->dsp &= 0xffffff00;
+ s->dsp |= val;
+ break;
+ case 0x2d: /* DSPS[8:15] */
+ s->dsp &= 0xffff00ff;
+ s->dsp |= val << 8;
+ break;
+ case 0x2e: /* DSPS[16:23] */
+ s->dsp &= 0xff00ffff;
+ s->dsp |= val << 16;
+ break;
+ case 0x2f: /* DSPS[14:31] */
+ s->dsp &= 0x00ffffff;
+ s->dsp |= val << 24;
+ if ((s->dmode & LSI_DMODE_MAN) == 0
+ && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+ lsi_execute_script(s);
+ break;
+ CASE_SET_REG32(dsps, 0x30)
+ CASE_SET_REG32(scratch[0], 0x34)
+ case 0x38: /* DMODE */
+ if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) {
+ BADF("IO mappings not implemented\n");
+ }
+ s->dmode = val;
+ break;
+ case 0x39: /* DIEN */
+ s->dien = val;
+ lsi_update_irq(s);
+ break;
+ case 0x3b: /* DCNTL */
+ s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD);
+ if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+ lsi_execute_script(s);
+ break;
+ case 0x40: /* SIEN0 */
+ s->sien0 = val;
+ lsi_update_irq(s);
+ break;
+ case 0x41: /* SIEN1 */
+ s->sien1 = val;
+ lsi_update_irq(s);
+ break;
+ case 0x47: /* GPCNTL0 */
+ break;
+ case 0x48: /* STIME0 */
+ s->stime0 = val;
+ break;
+ case 0x49: /* STIME1 */
+ if (val & 0xf) {
+ DPRINTF("General purpose timer not implemented\n");
+ /* ??? Raising the interrupt immediately seems to be sufficient
+ to keep the FreeBSD driver happy. */
+ lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN);
+ }
+ break;
+ case 0x4a: /* RESPID0 */
+ s->respid0 = val;
+ break;
+ case 0x4b: /* RESPID1 */
+ s->respid1 = val;
+ break;
+ case 0x4d: /* STEST1 */
+ s->stest1 = val;
+ break;
+ case 0x4e: /* STEST2 */
+ if (val & 1) {
+ BADF("Low level mode not implemented\n");
+ }
+ s->stest2 = val;
+ break;
+ case 0x4f: /* STEST3 */
+ if (val & 0x41) {
+ BADF("SCSI FIFO test mode not implemented\n");
+ }
+ s->stest3 = val;
+ break;
+ case 0x56: /* CCNTL0 */
+ s->ccntl0 = val;
+ break;
+ case 0x57: /* CCNTL1 */
+ s->ccntl1 = val;
+ break;
+ CASE_SET_REG32(mmrs, 0xa0)
+ CASE_SET_REG32(mmws, 0xa4)
+ CASE_SET_REG32(sfs, 0xa8)
+ CASE_SET_REG32(drs, 0xac)
+ CASE_SET_REG32(sbms, 0xb0)
+ CASE_SET_REG32(dmbs, 0xb4)
+ CASE_SET_REG32(dnad64, 0xb8)
+ CASE_SET_REG32(pmjad1, 0xc0)
+ CASE_SET_REG32(pmjad2, 0xc4)
+ CASE_SET_REG32(rbc, 0xc8)
+ CASE_SET_REG32(ua, 0xcc)
+ CASE_SET_REG32(ia, 0xd4)
+ CASE_SET_REG32(sbc, 0xd8)
+ CASE_SET_REG32(csbc, 0xdc)
+ default:
+ if (offset >= 0x5c && offset < 0xa0) {
+ int n;
+ int shift;
+ n = (offset - 0x58) >> 2;
+ shift = (offset & 3) * 8;
+ s->scratch[n] &= ~(0xff << shift);
+ s->scratch[n] |= (val & 0xff) << shift;
+ } else {
+ BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
+ }
+ }
+#undef CASE_SET_REG32
+}
+
+static void lsi_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static void lsi_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+}
+
+static void lsi_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+ lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff);
+ lsi_reg_writeb(s, addr + 3, (val >> 24) & 0xff);
+}
+
+static uint32_t lsi_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ return lsi_reg_readb(s, addr & 0xff);
+}
+
+static uint32_t lsi_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ return val;
+}
+
+static uint32_t lsi_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ val |= lsi_reg_readb(s, addr + 2) << 16;
+ val |= lsi_reg_readb(s, addr + 3) << 24;
+ return val;
+}
+
+static CPUReadMemoryFunc *lsi_mmio_readfn[3] = {
+ lsi_mmio_readb,
+ lsi_mmio_readw,
+ lsi_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *lsi_mmio_writefn[3] = {
+ lsi_mmio_writeb,
+ lsi_mmio_writew,
+ lsi_mmio_writel,
+};
+
+static void lsi_ram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t newval;
+ int shift;
+
+ addr &= 0x1fff;
+ newval = s->script_ram[addr >> 2];
+ shift = (addr & 3) * 8;
+ newval &= ~(0xff << shift);
+ newval |= val << shift;
+ s->script_ram[addr >> 2] = newval;
+}
+
+static void lsi_ram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t newval;
+
+ addr &= 0x1fff;
+ newval = s->script_ram[addr >> 2];
+ if (addr & 2) {
+ newval = (newval & 0xffff) | (val << 16);
+ } else {
+ newval = (newval & 0xffff0000) | val;
+ }
+ s->script_ram[addr >> 2] = newval;
+}
+
+
+static void lsi_ram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ addr &= 0x1fff;
+ s->script_ram[addr >> 2] = val;
+}
+
+static uint32_t lsi_ram_readb(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+
+ addr &= 0x1fff;
+ val = s->script_ram[addr >> 2];
+ val >>= (addr & 3) * 8;
+ return val & 0xff;
+}
+
+static uint32_t lsi_ram_readw(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+
+ addr &= 0x1fff;
+ val = s->script_ram[addr >> 2];
+ if (addr & 2)
+ val >>= 16;
+ return le16_to_cpu(val);
+}
+
+static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ addr &= 0x1fff;
+ return le32_to_cpu(s->script_ram[addr >> 2]);
+}
+
+static CPUReadMemoryFunc *lsi_ram_readfn[3] = {
+ lsi_ram_readb,
+ lsi_ram_readw,
+ lsi_ram_readl,
+};
+
+static CPUWriteMemoryFunc *lsi_ram_writefn[3] = {
+ lsi_ram_writeb,
+ lsi_ram_writew,
+ lsi_ram_writel,
+};
+
+static uint32_t lsi_io_readb(void *opaque, uint32_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ return lsi_reg_readb(s, addr & 0xff);
+}
+
+static uint32_t lsi_io_readw(void *opaque, uint32_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ return val;
+}
+
+static uint32_t lsi_io_readl(void *opaque, uint32_t addr)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ val |= lsi_reg_readb(s, addr + 2) << 16;
+ val |= lsi_reg_readb(s, addr + 3) << 24;
+ return val;
+}
+
+static void lsi_io_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+ lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static void lsi_io_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+}
+
+static void lsi_io_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = (LSIState *)opaque;
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+ lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff);
+ lsi_reg_writeb(s, addr + 2, (val >> 24) & 0xff);
+}
+
+static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ LSIState *s = (LSIState *)pci_dev;
+
+ DPRINTF("Mapping IO at %08x\n", addr);
+
+ register_ioport_write(addr, 256, 1, lsi_io_writeb, s);
+ register_ioport_read(addr, 256, 1, lsi_io_readb, s);
+ register_ioport_write(addr, 256, 2, lsi_io_writew, s);
+ register_ioport_read(addr, 256, 2, lsi_io_readw, s);
+ register_ioport_write(addr, 256, 4, lsi_io_writel, s);
+ register_ioport_read(addr, 256, 4, lsi_io_readl, s);
+}
+
+static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ LSIState *s = (LSIState *)pci_dev;
+
+ DPRINTF("Mapping ram at %08x\n", addr);
+ s->script_ram_base = addr;
+ cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr);
+}
+
+static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ LSIState *s = (LSIState *)pci_dev;
+
+ DPRINTF("Mapping registers at %08x\n", addr);
+ cpu_register_physical_memory(addr + 0, 0x400, s->mmio_io_addr);
+}
+
+void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id)
+{
+ LSIState *s = (LSIState *)opaque;
+
+ if (id < 0) {
+ for (id = 0; id < LSI_MAX_DEVS; id++) {
+ if (s->scsi_dev[id] == NULL)
+ break;
+ }
+ }
+ if (id >= LSI_MAX_DEVS) {
+ BADF("Bad Device ID %d\n", id);
+ return;
+ }
+ if (s->scsi_dev[id]) {
+ DPRINTF("Destroying device %d\n", id);
+ scsi_disk_destroy(s->scsi_dev[id]);
+ }
+ DPRINTF("Attaching block device %d\n", id);
+ s->scsi_dev[id] = scsi_disk_init(bd, lsi_command_complete, s);
+}
+
+void *lsi_scsi_init(PCIBus *bus, int devfn)
+{
+ LSIState *s;
+
+ s = (LSIState *)pci_register_device(bus, "LSI53C895A SCSI HBA",
+ sizeof(*s), devfn, NULL, NULL);
+ if (s == NULL) {
+ fprintf(stderr, "lsi-scsi: Failed to register PCI device\n");
+ return NULL;
+ }
+
+ s->pci_dev.config[0x00] = 0x00;
+ s->pci_dev.config[0x01] = 0x10;
+ s->pci_dev.config[0x02] = 0x12;
+ s->pci_dev.config[0x03] = 0x00;
+ s->pci_dev.config[0x0b] = 0x01;
+ s->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
+
+ s->mmio_io_addr = cpu_register_io_memory(0, lsi_mmio_readfn,
+ lsi_mmio_writefn, s);
+ s->ram_io_addr = cpu_register_io_memory(0, lsi_ram_readfn,
+ lsi_ram_writefn, s);
+
+ pci_register_io_region((struct PCIDevice *)s, 0, 256,
+ PCI_ADDRESS_SPACE_IO, lsi_io_mapfunc);
+ pci_register_io_region((struct PCIDevice *)s, 1, 0x400,
+ PCI_ADDRESS_SPACE_MEM, lsi_mmio_mapfunc);
+ pci_register_io_region((struct PCIDevice *)s, 2, 0x2000,
+ PCI_ADDRESS_SPACE_MEM, lsi_ram_mapfunc);
+
+ lsi_soft_reset(s);
+
+ return s;
+}
+
diff --git a/hw/m48t59.c b/hw/m48t59.c
new file mode 100644
index 0000000..daa1c52
--- /dev/null
+++ b/hw/m48t59.c
@@ -0,0 +1,614 @@
+/*
+ * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "m48t59.h"
+
+//#define DEBUG_NVRAM
+
+#if defined(DEBUG_NVRAM)
+#define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
+#else
+#define NVRAM_PRINTF(fmt, args...) do { } while (0)
+#endif
+
+/*
+ * The M48T08 and M48T59 chips are very similar. The newer '59 has
+ * alarm and a watchdog timer and related control registers. In the
+ * PPC platform there is also a nvram lock function.
+ */
+struct m48t59_t {
+ /* Model parameters */
+ int type; // 8 = m48t08, 59 = m48t59
+ /* Hardware parameters */
+ int IRQ;
+ int mem_index;
+ uint32_t mem_base;
+ uint32_t io_base;
+ uint16_t size;
+ /* RTC management */
+ time_t time_offset;
+ time_t stop_time;
+ /* Alarm & watchdog */
+ time_t alarm;
+ struct QEMUTimer *alrm_timer;
+ struct QEMUTimer *wd_timer;
+ /* NVRAM storage */
+ uint8_t lock;
+ uint16_t addr;
+ uint8_t *buffer;
+};
+
+/* Fake timer functions */
+/* Generic helpers for BCD */
+static inline uint8_t toBCD (uint8_t value)
+{
+ return (((value / 10) % 10) << 4) | (value % 10);
+}
+
+static inline uint8_t fromBCD (uint8_t BCD)
+{
+ return ((BCD >> 4) * 10) + (BCD & 0x0F);
+}
+
+/* RTC management helpers */
+static void get_time (m48t59_t *NVRAM, struct tm *tm)
+{
+ time_t t;
+
+ t = time(NULL) + NVRAM->time_offset;
+#ifdef _WIN32
+ memcpy(tm,localtime(&t),sizeof(*tm));
+#else
+ localtime_r (&t, tm) ;
+#endif
+}
+
+static void set_time (m48t59_t *NVRAM, struct tm *tm)
+{
+ time_t now, new_time;
+
+ new_time = mktime(tm);
+ now = time(NULL);
+ NVRAM->time_offset = new_time - now;
+}
+
+/* Alarm management */
+static void alarm_cb (void *opaque)
+{
+ struct tm tm, tm_now;
+ uint64_t next_time;
+ m48t59_t *NVRAM = opaque;
+
+ pic_set_irq(NVRAM->IRQ, 1);
+ if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a month */
+ get_time(NVRAM, &tm_now);
+ memcpy(&tm, &tm_now, sizeof(struct tm));
+ tm.tm_mon++;
+ if (tm.tm_mon == 13) {
+ tm.tm_mon = 1;
+ tm.tm_year++;
+ }
+ next_time = mktime(&tm);
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a day */
+ next_time = 24 * 60 * 60 + mktime(&tm_now);
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once an hour */
+ next_time = 60 * 60 + mktime(&tm_now);
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a minute */
+ next_time = 60 + mktime(&tm_now);
+ } else {
+ /* Repeat once a second */
+ next_time = 1 + mktime(&tm_now);
+ }
+ qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000);
+ pic_set_irq(NVRAM->IRQ, 0);
+}
+
+
+static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
+{
+#ifdef _WIN32
+ memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm));
+#else
+ localtime_r (&NVRAM->alarm, tm);
+#endif
+}
+
+static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
+{
+ NVRAM->alarm = mktime(tm);
+ if (NVRAM->alrm_timer != NULL) {
+ qemu_del_timer(NVRAM->alrm_timer);
+ NVRAM->alrm_timer = NULL;
+ }
+ if (NVRAM->alarm - time(NULL) > 0)
+ qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000);
+}
+
+/* Watchdog management */
+static void watchdog_cb (void *opaque)
+{
+ m48t59_t *NVRAM = opaque;
+
+ NVRAM->buffer[0x1FF0] |= 0x80;
+ if (NVRAM->buffer[0x1FF7] & 0x80) {
+ NVRAM->buffer[0x1FF7] = 0x00;
+ NVRAM->buffer[0x1FFC] &= ~0x40;
+ /* May it be a hw CPU Reset instead ? */
+ qemu_system_reset_request();
+ } else {
+ pic_set_irq(NVRAM->IRQ, 1);
+ pic_set_irq(NVRAM->IRQ, 0);
+ }
+}
+
+static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value)
+{
+ uint64_t interval; /* in 1/16 seconds */
+
+ if (NVRAM->wd_timer != NULL) {
+ qemu_del_timer(NVRAM->wd_timer);
+ NVRAM->wd_timer = NULL;
+ }
+ NVRAM->buffer[0x1FF0] &= ~0x80;
+ if (value != 0) {
+ interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
+ qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
+ ((interval * 1000) >> 4));
+ }
+}
+
+/* Direct access to NVRAM */
+void m48t59_write (m48t59_t *NVRAM, uint32_t addr, uint32_t val)
+{
+ struct tm tm;
+ int tmp;
+
+ if (addr > 0x1FF8 && addr < 0x2000)
+ NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
+ if (NVRAM->type == 8 &&
+ (addr >= 0x1ff0 && addr <= 0x1ff7))
+ goto do_write;
+ switch (addr) {
+ case 0x1FF0:
+ /* flags register : read-only */
+ break;
+ case 0x1FF1:
+ /* unused */
+ break;
+ case 0x1FF2:
+ /* alarm seconds */
+ tmp = fromBCD(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_alarm(NVRAM, &tm);
+ tm.tm_sec = tmp;
+ NVRAM->buffer[0x1FF2] = val;
+ set_alarm(NVRAM, &tm);
+ }
+ break;
+ case 0x1FF3:
+ /* alarm minutes */
+ tmp = fromBCD(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_alarm(NVRAM, &tm);
+ tm.tm_min = tmp;
+ NVRAM->buffer[0x1FF3] = val;
+ set_alarm(NVRAM, &tm);
+ }
+ break;
+ case 0x1FF4:
+ /* alarm hours */
+ tmp = fromBCD(val & 0x3F);
+ if (tmp >= 0 && tmp <= 23) {
+ get_alarm(NVRAM, &tm);
+ tm.tm_hour = tmp;
+ NVRAM->buffer[0x1FF4] = val;
+ set_alarm(NVRAM, &tm);
+ }
+ break;
+ case 0x1FF5:
+ /* alarm date */
+ tmp = fromBCD(val & 0x1F);
+ if (tmp != 0) {
+ get_alarm(NVRAM, &tm);
+ tm.tm_mday = tmp;
+ NVRAM->buffer[0x1FF5] = val;
+ set_alarm(NVRAM, &tm);
+ }
+ break;
+ case 0x1FF6:
+ /* interrupts */
+ NVRAM->buffer[0x1FF6] = val;
+ break;
+ case 0x1FF7:
+ /* watchdog */
+ NVRAM->buffer[0x1FF7] = val;
+ set_up_watchdog(NVRAM, val);
+ break;
+ case 0x1FF8:
+ /* control */
+ NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
+ break;
+ case 0x1FF9:
+ /* seconds (BCD) */
+ tmp = fromBCD(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_time(NVRAM, &tm);
+ tm.tm_sec = tmp;
+ set_time(NVRAM, &tm);
+ }
+ if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) {
+ if (val & 0x80) {
+ NVRAM->stop_time = time(NULL);
+ } else {
+ NVRAM->time_offset += NVRAM->stop_time - time(NULL);
+ NVRAM->stop_time = 0;
+ }
+ }
+ NVRAM->buffer[0x1FF9] = val & 0x80;
+ break;
+ case 0x1FFA:
+ /* minutes (BCD) */
+ tmp = fromBCD(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_time(NVRAM, &tm);
+ tm.tm_min = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFB:
+ /* hours (BCD) */
+ tmp = fromBCD(val & 0x3F);
+ if (tmp >= 0 && tmp <= 23) {
+ get_time(NVRAM, &tm);
+ tm.tm_hour = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFC:
+ /* day of the week / century */
+ tmp = fromBCD(val & 0x07);
+ get_time(NVRAM, &tm);
+ tm.tm_wday = tmp;
+ set_time(NVRAM, &tm);
+ NVRAM->buffer[0x1FFC] = val & 0x40;
+ break;
+ case 0x1FFD:
+ /* date */
+ tmp = fromBCD(val & 0x1F);
+ if (tmp != 0) {
+ get_time(NVRAM, &tm);
+ tm.tm_mday = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFE:
+ /* month */
+ tmp = fromBCD(val & 0x1F);
+ if (tmp >= 1 && tmp <= 12) {
+ get_time(NVRAM, &tm);
+ tm.tm_mon = tmp - 1;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFF:
+ /* year */
+ tmp = fromBCD(val);
+ if (tmp >= 0 && tmp <= 99) {
+ get_time(NVRAM, &tm);
+ if (NVRAM->type == 8)
+ tm.tm_year = fromBCD(val) + 68; // Base year is 1968
+ else
+ tm.tm_year = fromBCD(val);
+ set_time(NVRAM, &tm);
+ }
+ break;
+ default:
+ /* Check lock registers state */
+ if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+ break;
+ if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+ break;
+ do_write:
+ if (addr < NVRAM->size) {
+ NVRAM->buffer[addr] = val & 0xFF;
+ }
+ break;
+ }
+}
+
+uint32_t m48t59_read (m48t59_t *NVRAM, uint32_t addr)
+{
+ struct tm tm;
+ uint32_t retval = 0xFF;
+
+ if (NVRAM->type == 8 &&
+ (addr >= 0x1ff0 && addr <= 0x1ff7))
+ goto do_read;
+ switch (addr) {
+ case 0x1FF0:
+ /* flags register */
+ goto do_read;
+ case 0x1FF1:
+ /* unused */
+ retval = 0;
+ break;
+ case 0x1FF2:
+ /* alarm seconds */
+ goto do_read;
+ case 0x1FF3:
+ /* alarm minutes */
+ goto do_read;
+ case 0x1FF4:
+ /* alarm hours */
+ goto do_read;
+ case 0x1FF5:
+ /* alarm date */
+ goto do_read;
+ case 0x1FF6:
+ /* interrupts */
+ goto do_read;
+ case 0x1FF7:
+ /* A read resets the watchdog */
+ set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
+ goto do_read;
+ case 0x1FF8:
+ /* control */
+ goto do_read;
+ case 0x1FF9:
+ /* seconds (BCD) */
+ get_time(NVRAM, &tm);
+ retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec);
+ break;
+ case 0x1FFA:
+ /* minutes (BCD) */
+ get_time(NVRAM, &tm);
+ retval = toBCD(tm.tm_min);
+ break;
+ case 0x1FFB:
+ /* hours (BCD) */
+ get_time(NVRAM, &tm);
+ retval = toBCD(tm.tm_hour);
+ break;
+ case 0x1FFC:
+ /* day of the week / century */
+ get_time(NVRAM, &tm);
+ retval = NVRAM->buffer[0x1FFC] | tm.tm_wday;
+ break;
+ case 0x1FFD:
+ /* date */
+ get_time(NVRAM, &tm);
+ retval = toBCD(tm.tm_mday);
+ break;
+ case 0x1FFE:
+ /* month */
+ get_time(NVRAM, &tm);
+ retval = toBCD(tm.tm_mon + 1);
+ break;
+ case 0x1FFF:
+ /* year */
+ get_time(NVRAM, &tm);
+ if (NVRAM->type == 8)
+ retval = toBCD(tm.tm_year - 68); // Base year is 1968
+ else
+ retval = toBCD(tm.tm_year);
+ break;
+ default:
+ /* Check lock registers state */
+ if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+ break;
+ if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+ break;
+ do_read:
+ if (addr < NVRAM->size) {
+ retval = NVRAM->buffer[addr];
+ }
+ break;
+ }
+ if (addr > 0x1FF9 && addr < 0x2000)
+ NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
+
+ return retval;
+}
+
+void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr)
+{
+ NVRAM->addr = addr;
+}
+
+void m48t59_toggle_lock (m48t59_t *NVRAM, int lock)
+{
+ NVRAM->lock ^= 1 << lock;
+}
+
+/* IO access to NVRAM */
+static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ m48t59_t *NVRAM = opaque;
+
+ addr -= NVRAM->io_base;
+ NVRAM_PRINTF("0x%08x => 0x%08x\n", addr, val);
+ switch (addr) {
+ case 0:
+ NVRAM->addr &= ~0x00FF;
+ NVRAM->addr |= val;
+ break;
+ case 1:
+ NVRAM->addr &= ~0xFF00;
+ NVRAM->addr |= val << 8;
+ break;
+ case 3:
+ m48t59_write(NVRAM, val, NVRAM->addr);
+ NVRAM->addr = 0x0000;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
+{
+ m48t59_t *NVRAM = opaque;
+ uint32_t retval;
+
+ addr -= NVRAM->io_base;
+ switch (addr) {
+ case 3:
+ retval = m48t59_read(NVRAM, NVRAM->addr);
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+ NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
+
+ return retval;
+}
+
+static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ m48t59_t *NVRAM = opaque;
+
+ addr -= NVRAM->mem_base;
+ m48t59_write(NVRAM, addr, value & 0xff);
+}
+
+static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ m48t59_t *NVRAM = opaque;
+
+ addr -= NVRAM->mem_base;
+ m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
+ m48t59_write(NVRAM, addr + 1, value & 0xff);
+}
+
+static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ m48t59_t *NVRAM = opaque;
+
+ addr -= NVRAM->mem_base;
+ m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
+ m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
+ m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
+ m48t59_write(NVRAM, addr + 3, value & 0xff);
+}
+
+static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+ m48t59_t *NVRAM = opaque;
+ uint32_t retval;
+
+ addr -= NVRAM->mem_base;
+ retval = m48t59_read(NVRAM, addr);
+ return retval;
+}
+
+static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
+{
+ m48t59_t *NVRAM = opaque;
+ uint32_t retval;
+
+ addr -= NVRAM->mem_base;
+ retval = m48t59_read(NVRAM, addr) << 8;
+ retval |= m48t59_read(NVRAM, addr + 1);
+ return retval;
+}
+
+static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
+{
+ m48t59_t *NVRAM = opaque;
+ uint32_t retval;
+
+ addr -= NVRAM->mem_base;
+ retval = m48t59_read(NVRAM, addr) << 24;
+ retval |= m48t59_read(NVRAM, addr + 1) << 16;
+ retval |= m48t59_read(NVRAM, addr + 2) << 8;
+ retval |= m48t59_read(NVRAM, addr + 3);
+ return retval;
+}
+
+static CPUWriteMemoryFunc *nvram_write[] = {
+ &nvram_writeb,
+ &nvram_writew,
+ &nvram_writel,
+};
+
+static CPUReadMemoryFunc *nvram_read[] = {
+ &nvram_readb,
+ &nvram_readw,
+ &nvram_readl,
+};
+
+/* Initialisation routine */
+m48t59_t *m48t59_init (int IRQ, target_ulong mem_base,
+ uint32_t io_base, uint16_t size,
+ int type)
+{
+ m48t59_t *s;
+
+ s = qemu_mallocz(sizeof(m48t59_t));
+ if (!s)
+ return NULL;
+ s->buffer = qemu_mallocz(size);
+ if (!s->buffer) {
+ qemu_free(s);
+ return NULL;
+ }
+ s->IRQ = IRQ;
+ s->size = size;
+ s->mem_base = mem_base;
+ s->io_base = io_base;
+ s->addr = 0;
+ s->type = type;
+ if (io_base != 0) {
+ register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
+ register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
+ }
+ if (mem_base != 0) {
+ s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
+ cpu_register_physical_memory(mem_base, 0x4000, s->mem_index);
+ }
+ if (type == 59) {
+ s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
+ s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
+ }
+ s->lock = 0;
+
+ return s;
+}
diff --git a/hw/m48t59.h b/hw/m48t59.h
new file mode 100644
index 0000000..af22dc1
--- /dev/null
+++ b/hw/m48t59.h
@@ -0,0 +1,13 @@
+#if !defined (__M48T59_H__)
+#define __M48T59_H__
+
+typedef struct m48t59_t m48t59_t;
+
+void m48t59_write (m48t59_t *NVRAM, uint32_t addr, uint32_t val);
+uint32_t m48t59_read (m48t59_t *NVRAM, uint32_t addr);
+void m48t59_toggle_lock (m48t59_t *NVRAM, int lock);
+m48t59_t *m48t59_init (int IRQ, target_ulong mem_base,
+ uint32_t io_base, uint16_t size,
+ int type);
+
+#endif /* !defined (__M48T59_H__) */
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
new file mode 100644
index 0000000..9d4cbed
--- /dev/null
+++ b/hw/mc146818rtc.c
@@ -0,0 +1,463 @@
+/*
+ * QEMU MC146818 RTC emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_CMOS
+
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET 0x80
+#define REG_B_PIE 0x40
+#define REG_B_AIE 0x20
+#define REG_B_UIE 0x10
+
+struct RTCState {
+ uint8_t cmos_data[128];
+ uint8_t cmos_index;
+ struct tm current_tm;
+ int irq;
+ /* periodic timer */
+ QEMUTimer *periodic_timer;
+ int64_t next_periodic_time;
+ /* second update */
+ int64_t next_second_time;
+ QEMUTimer *second_timer;
+ QEMUTimer *second_timer2;
+};
+
+static void rtc_set_time(RTCState *s);
+static void rtc_copy_date(RTCState *s);
+
+static void rtc_timer_update(RTCState *s, int64_t current_time)
+{
+ int period_code, period;
+ int64_t cur_clock, next_irq_clock;
+
+ period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+ if (period_code != 0 &&
+ (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
+ if (period_code <= 2)
+ period_code += 7;
+ /* period in 32 Khz cycles */
+ period = 1 << (period_code - 1);
+ /* compute 32 khz clock */
+ cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
+ next_irq_clock = (cur_clock & ~(period - 1)) + period;
+ s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
+ qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
+ } else {
+ qemu_del_timer(s->periodic_timer);
+ }
+}
+
+static void rtc_periodic_timer(void *opaque)
+{
+ RTCState *s = opaque;
+
+ rtc_timer_update(s, s->next_periodic_time);
+ s->cmos_data[RTC_REG_C] |= 0xc0;
+ pic_set_irq(s->irq, 1);
+}
+
+static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ RTCState *s = opaque;
+
+ if ((addr & 1) == 0) {
+ s->cmos_index = data & 0x7f;
+ } else {
+#ifdef DEBUG_CMOS
+ printf("cmos: write index=0x%02x val=0x%02x\n",
+ s->cmos_index, data);
+#endif
+ switch(s->cmos_index) {
+ case RTC_SECONDS_ALARM:
+ case RTC_MINUTES_ALARM:
+ case RTC_HOURS_ALARM:
+ /* XXX: not supported */
+ s->cmos_data[s->cmos_index] = data;
+ break;
+ case RTC_SECONDS:
+ case RTC_MINUTES:
+ case RTC_HOURS:
+ case RTC_DAY_OF_WEEK:
+ case RTC_DAY_OF_MONTH:
+ case RTC_MONTH:
+ case RTC_YEAR:
+ s->cmos_data[s->cmos_index] = data;
+ /* if in set mode, do not update the time */
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_set_time(s);
+ }
+ break;
+ case RTC_REG_A:
+ /* UIP bit is read only */
+ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
+ (s->cmos_data[RTC_REG_A] & REG_A_UIP);
+ rtc_timer_update(s, qemu_get_clock(vm_clock));
+ break;
+ case RTC_REG_B:
+ if (data & REG_B_SET) {
+ /* set mode: reset UIP mode */
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ data &= ~REG_B_UIE;
+ } else {
+ /* if disabling set mode, update the time */
+ if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ rtc_set_time(s);
+ }
+ }
+ s->cmos_data[RTC_REG_B] = data;
+ rtc_timer_update(s, qemu_get_clock(vm_clock));
+ break;
+ case RTC_REG_C:
+ case RTC_REG_D:
+ /* cannot write to them */
+ break;
+ default:
+ s->cmos_data[s->cmos_index] = data;
+ break;
+ }
+ }
+}
+
+static inline int to_bcd(RTCState *s, int a)
+{
+ if (s->cmos_data[RTC_REG_B] & 0x04) {
+ return a;
+ } else {
+ return ((a / 10) << 4) | (a % 10);
+ }
+}
+
+static inline int from_bcd(RTCState *s, int a)
+{
+ if (s->cmos_data[RTC_REG_B] & 0x04) {
+ return a;
+ } else {
+ return ((a >> 4) * 10) + (a & 0x0f);
+ }
+}
+
+static void rtc_set_time(RTCState *s)
+{
+ struct tm *tm = &s->current_tm;
+
+ tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
+ tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
+ tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
+ if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
+ (s->cmos_data[RTC_HOURS] & 0x80)) {
+ tm->tm_hour += 12;
+ }
+ tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
+ tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
+ tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
+ tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
+}
+
+static void rtc_copy_date(RTCState *s)
+{
+ const struct tm *tm = &s->current_tm;
+
+ s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
+ s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
+ if (s->cmos_data[RTC_REG_B] & 0x02) {
+ /* 24 hour format */
+ s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
+ } else {
+ /* 12 hour format */
+ s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
+ if (tm->tm_hour >= 12)
+ s->cmos_data[RTC_HOURS] |= 0x80;
+ }
+ s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
+ s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
+ s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
+ s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+ if ((unsigned )month >= 12)
+ return 31;
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+ d++;
+ }
+ return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7)
+ tm->tm_wday = 0;
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void rtc_update_second(void *opaque)
+{
+ RTCState *s = opaque;
+ int64_t delay;
+
+ /* if the oscillator is not in normal operation, we do not update */
+ if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
+ s->next_second_time += ticks_per_sec;
+ qemu_mod_timer(s->second_timer, s->next_second_time);
+ } else {
+ rtc_next_second(&s->current_tm);
+
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ /* update in progress bit */
+ s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+ }
+ /* should be 244 us = 8 / 32768 seconds, but currently the
+ timers do not have the necessary resolution. */
+ delay = (ticks_per_sec * 1) / 100;
+ if (delay < 1)
+ delay = 1;
+ qemu_mod_timer(s->second_timer2,
+ s->next_second_time + delay);
+ }
+}
+
+static void rtc_update_second2(void *opaque)
+{
+ RTCState *s = opaque;
+
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_copy_date(s);
+ }
+
+ /* check alarm */
+ if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+ if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
+ s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
+ ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
+ s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
+ ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
+ s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
+
+ s->cmos_data[RTC_REG_C] |= 0xa0;
+ pic_set_irq(s->irq, 1);
+ }
+ }
+
+ /* update ended interrupt */
+ if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
+ s->cmos_data[RTC_REG_C] |= 0x90;
+ pic_set_irq(s->irq, 1);
+ }
+
+ /* clear update in progress bit */
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+ s->next_second_time += ticks_per_sec;
+ qemu_mod_timer(s->second_timer, s->next_second_time);
+}
+
+static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+{
+ RTCState *s = opaque;
+ int ret;
+ if ((addr & 1) == 0) {
+ return 0xff;
+ } else {
+ switch(s->cmos_index) {
+ case RTC_SECONDS:
+ case RTC_MINUTES:
+ case RTC_HOURS:
+ case RTC_DAY_OF_WEEK:
+ case RTC_DAY_OF_MONTH:
+ case RTC_MONTH:
+ case RTC_YEAR:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ case RTC_REG_A:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ case RTC_REG_C:
+ ret = s->cmos_data[s->cmos_index];
+ pic_set_irq(s->irq, 0);
+ s->cmos_data[RTC_REG_C] = 0x00;
+ break;
+ default:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ }
+#ifdef DEBUG_CMOS
+ printf("cmos: read index=0x%02x val=0x%02x\n",
+ s->cmos_index, ret);
+#endif
+ return ret;
+ }
+}
+
+void rtc_set_memory(RTCState *s, int addr, int val)
+{
+ if (addr >= 0 && addr <= 127)
+ s->cmos_data[addr] = val;
+}
+
+void rtc_set_date(RTCState *s, const struct tm *tm)
+{
+ s->current_tm = *tm;
+ rtc_copy_date(s);
+}
+
+static void rtc_save(QEMUFile *f, void *opaque)
+{
+ RTCState *s = opaque;
+
+ qemu_put_buffer(f, s->cmos_data, 128);
+ qemu_put_8s(f, &s->cmos_index);
+
+ qemu_put_be32s(f, &s->current_tm.tm_sec);
+ qemu_put_be32s(f, &s->current_tm.tm_min);
+ qemu_put_be32s(f, &s->current_tm.tm_hour);
+ qemu_put_be32s(f, &s->current_tm.tm_wday);
+ qemu_put_be32s(f, &s->current_tm.tm_mday);
+ qemu_put_be32s(f, &s->current_tm.tm_mon);
+ qemu_put_be32s(f, &s->current_tm.tm_year);
+
+ qemu_put_timer(f, s->periodic_timer);
+ qemu_put_be64s(f, &s->next_periodic_time);
+
+ qemu_put_be64s(f, &s->next_second_time);
+ qemu_put_timer(f, s->second_timer);
+ qemu_put_timer(f, s->second_timer2);
+}
+
+static int rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ RTCState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_buffer(f, s->cmos_data, 128);
+ qemu_get_8s(f, &s->cmos_index);
+
+ qemu_get_be32s(f, &s->current_tm.tm_sec);
+ qemu_get_be32s(f, &s->current_tm.tm_min);
+ qemu_get_be32s(f, &s->current_tm.tm_hour);
+ qemu_get_be32s(f, &s->current_tm.tm_wday);
+ qemu_get_be32s(f, &s->current_tm.tm_mday);
+ qemu_get_be32s(f, &s->current_tm.tm_mon);
+ qemu_get_be32s(f, &s->current_tm.tm_year);
+
+ qemu_get_timer(f, s->periodic_timer);
+ qemu_get_be64s(f, &s->next_periodic_time);
+
+ qemu_get_be64s(f, &s->next_second_time);
+ qemu_get_timer(f, s->second_timer);
+ qemu_get_timer(f, s->second_timer2);
+ return 0;
+}
+
+RTCState *rtc_init(int base, int irq)
+{
+ RTCState *s;
+
+ s = qemu_mallocz(sizeof(RTCState));
+ if (!s)
+ return NULL;
+
+ s->irq = irq;
+ s->cmos_data[RTC_REG_A] = 0x26;
+ s->cmos_data[RTC_REG_B] = 0x02;
+ s->cmos_data[RTC_REG_C] = 0x00;
+ s->cmos_data[RTC_REG_D] = 0x80;
+
+ s->periodic_timer = qemu_new_timer(vm_clock,
+ rtc_periodic_timer, s);
+ s->second_timer = qemu_new_timer(vm_clock,
+ rtc_update_second, s);
+ s->second_timer2 = qemu_new_timer(vm_clock,
+ rtc_update_second2, s);
+
+ s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
+ qemu_mod_timer(s->second_timer2, s->next_second_time);
+
+ register_ioport_write(base, 2, 1, cmos_ioport_write, s);
+ register_ioport_read(base, 2, 1, cmos_ioport_read, s);
+
+ register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+ return s;
+}
+
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
new file mode 100644
index 0000000..22d742a
--- /dev/null
+++ b/hw/mips_r4k.c
@@ -0,0 +1,291 @@
+#include "vl.h"
+
+#define BIOS_FILENAME "mips_bios.bin"
+//#define BIOS_FILENAME "system.bin"
+#define KERNEL_LOAD_ADDR 0x80010000
+#define INITRD_LOAD_ADDR 0x80800000
+
+#define VIRT_TO_PHYS_ADDEND (-0x80000000LL)
+
+extern FILE *logfile;
+
+static PITState *pit;
+
+static void pic_irq_request(void *opaque, int level)
+{
+ CPUState *env = first_cpu;
+ if (level) {
+ env->CP0_Cause |= 0x00000400;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ env->CP0_Cause &= ~0x00000400;
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+}
+
+void cpu_mips_irqctrl_init (void)
+{
+}
+
+/* XXX: do not use a global */
+uint32_t cpu_mips_get_random (CPUState *env)
+{
+ static uint32_t seed = 0;
+ uint32_t idx;
+ seed = seed * 314159 + 1;
+ idx = (seed >> 16) % (MIPS_TLB_NB - env->CP0_Wired) + env->CP0_Wired;
+ return idx;
+}
+
+/* MIPS R4K timer */
+uint32_t cpu_mips_get_count (CPUState *env)
+{
+ return env->CP0_Count +
+ (uint32_t)muldiv64(qemu_get_clock(vm_clock),
+ 100 * 1000 * 1000, ticks_per_sec);
+}
+
+static void cpu_mips_update_count (CPUState *env, uint32_t count,
+ uint32_t compare)
+{
+ uint64_t now, next;
+ uint32_t tmp;
+
+ tmp = count;
+ if (count == compare)
+ tmp++;
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(compare - tmp, ticks_per_sec, 100 * 1000 * 1000);
+ if (next == now)
+ next++;
+#if 0
+ if (logfile) {
+ fprintf(logfile, "%s: 0x%08" PRIx64 " %08x %08x => 0x%08" PRIx64 "\n",
+ __func__, now, count, compare, next - now);
+ }
+#endif
+ /* Store new count and compare registers */
+ env->CP0_Compare = compare;
+ env->CP0_Count =
+ count - (uint32_t)muldiv64(now, 100 * 1000 * 1000, ticks_per_sec);
+ /* Adjust timer */
+ qemu_mod_timer(env->timer, next);
+}
+
+void cpu_mips_store_count (CPUState *env, uint32_t value)
+{
+ cpu_mips_update_count(env, value, env->CP0_Compare);
+}
+
+void cpu_mips_store_compare (CPUState *env, uint32_t value)
+{
+ cpu_mips_update_count(env, cpu_mips_get_count(env), value);
+ env->CP0_Cause &= ~0x00008000;
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+static void mips_timer_cb (void *opaque)
+{
+ CPUState *env;
+
+ env = opaque;
+#if 0
+ if (logfile) {
+ fprintf(logfile, "%s\n", __func__);
+ }
+#endif
+ cpu_mips_update_count(env, cpu_mips_get_count(env), env->CP0_Compare);
+ env->CP0_Cause |= 0x00008000;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+void cpu_mips_clock_init (CPUState *env)
+{
+ env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env);
+ env->CP0_Compare = 0;
+ cpu_mips_update_count(env, 1, 0);
+}
+
+
+static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
+#endif
+ cpu_outb(NULL, addr & 0xffff, value);
+}
+
+static uint32_t io_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inb(NULL, addr & 0xffff);
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
+#endif
+ return ret;
+}
+
+static void io_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
+#endif
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap16(value);
+#endif
+ cpu_outw(NULL, addr & 0xffff, value);
+}
+
+static uint32_t io_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inw(NULL, addr & 0xffff);
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap16(ret);
+#endif
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
+#endif
+ return ret;
+}
+
+static void io_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
+#endif
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+ cpu_outl(NULL, addr & 0xffff, value);
+}
+
+static uint32_t io_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inl(NULL, addr & 0xffff);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap32(ret);
+#endif
+#if 0
+ if (logfile)
+ fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
+#endif
+ return ret;
+}
+
+CPUWriteMemoryFunc *io_write[] = {
+ &io_writeb,
+ &io_writew,
+ &io_writel,
+};
+
+CPUReadMemoryFunc *io_read[] = {
+ &io_readb,
+ &io_readw,
+ &io_readl,
+};
+
+void mips_r4k_init (int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ char buf[1024];
+ int64_t entry = 0;
+ unsigned long bios_offset;
+ int io_memory;
+ int ret;
+ CPUState *env;
+ long kernel_size;
+
+ env = cpu_init();
+ register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ /* Try to load a BIOS image. If this fails, we continue regardless,
+ but initialize the hardware ourselves. When a kernel gets
+ preloaded we also initialize the hardware, since the BIOS wasn't
+ run. */
+ bios_offset = ram_size + vga_ram_size;
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
+ ret = load_image(buf, phys_ram_base + bios_offset);
+ if (ret == BIOS_SIZE) {
+ cpu_register_physical_memory((uint32_t)(0x1fc00000),
+ BIOS_SIZE, bios_offset | IO_MEM_ROM);
+ } else {
+ /* not fatal */
+ fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
+ buf);
+ }
+
+ kernel_size = 0;
+ if (kernel_filename) {
+ kernel_size = load_elf(kernel_filename, VIRT_TO_PHYS_ADDEND, &entry);
+ if (kernel_size >= 0)
+ env->PC = entry;
+ else {
+ kernel_size = load_image(kernel_filename,
+ phys_ram_base + KERNEL_LOAD_ADDR + VIRT_TO_PHYS_ADDEND);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ env->PC = KERNEL_LOAD_ADDR;
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ if (load_image(initrd_filename,
+ phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND)
+ == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Store command line. */
+ strcpy (phys_ram_base + (16 << 20) - 256, kernel_cmdline);
+ /* FIXME: little endian support */
+ *(int *)(phys_ram_base + (16 << 20) - 260) = tswap32 (0x12345678);
+ *(int *)(phys_ram_base + (16 << 20) - 264) = tswap32 (ram_size);
+ }
+
+ /* Init internal devices */
+ cpu_mips_clock_init(env);
+ cpu_mips_irqctrl_init();
+
+ /* Register 64 KB of ISA IO space at 0x14000000 */
+ io_memory = cpu_register_io_memory(0, io_read, io_write, NULL);
+ cpu_register_physical_memory(0x14000000, 0x00010000, io_memory);
+ isa_mem_base = 0x10000000;
+
+ isa_pic = pic_init(pic_irq_request, env);
+ pit = pit_init(0x40, 0);
+ serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
+ vga_initialize(NULL, ds, phys_ram_base + ram_size, ram_size,
+ vga_ram_size, 0, 0);
+
+ if (nd_table[0].vlan) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "ne2k_isa") == 0) {
+ isa_ne2000_init(0x300, 9, &nd_table[0]);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+}
+
+QEMUMachine mips_machine = {
+ "mips",
+ "mips r4k platform",
+ mips_r4k_init,
+};
diff --git a/hw/ne2000.c b/hw/ne2000.c
new file mode 100644
index 0000000..e23c6df
--- /dev/null
+++ b/hw/ne2000.c
@@ -0,0 +1,816 @@
+/*
+ * QEMU NE2000 emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug NE2000 card */
+//#define DEBUG_NE2000
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+#define EN1_PHYS 0x11
+#define EN1_CURPAG 0x17
+#define EN1_MULT 0x18
+
+#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */
+#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */
+
+#define EN3_CONFIG0 0x33
+#define EN3_CONFIG1 0x34
+#define EN3_CONFIG2 0x35
+#define EN3_CONFIG3 0x36
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicast address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+#define NE2000_PMEM_SIZE (32*1024)
+#define NE2000_PMEM_START (16*1024)
+#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START)
+#define NE2000_MEM_SIZE NE2000_PMEM_END
+
+typedef struct NE2000State {
+ uint8_t cmd;
+ uint32_t start;
+ uint32_t stop;
+ uint8_t boundary;
+ uint8_t tsr;
+ uint8_t tpsr;
+ uint16_t tcnt;
+ uint16_t rcnt;
+ uint32_t rsar;
+ uint8_t rsr;
+ uint8_t rxcr;
+ uint8_t isr;
+ uint8_t dcfg;
+ uint8_t imr;
+ uint8_t phys[6]; /* mac address */
+ uint8_t curpag;
+ uint8_t mult[8]; /* multicast mask array */
+ int irq;
+ PCIDevice *pci_dev;
+ VLANClientState *vc;
+ uint8_t macaddr[6];
+ uint8_t mem[NE2000_MEM_SIZE];
+} NE2000State;
+
+static void ne2000_reset(NE2000State *s)
+{
+ int i;
+
+ s->isr = ENISR_RESET;
+ memcpy(s->mem, s->macaddr, 6);
+ s->mem[14] = 0x57;
+ s->mem[15] = 0x57;
+
+ /* duplicate prom data */
+ for(i = 15;i >= 0; i--) {
+ s->mem[2 * i] = s->mem[i];
+ s->mem[2 * i + 1] = s->mem[i];
+ }
+}
+
+static void ne2000_update_irq(NE2000State *s)
+{
+ int isr;
+ isr = (s->isr & s->imr) & 0x7f;
+#if defined(DEBUG_NE2000)
+ printf("NE2000: Set IRQ line %d to %d (%02x %02x)\n",
+ s->irq, isr ? 1 : 0, s->isr, s->imr);
+#endif
+ if (s->irq == 16) {
+ /* PCI irq */
+ pci_set_irq(s->pci_dev, 0, (isr != 0));
+ } else {
+ /* ISA irq */
+ pic_set_irq(s->irq, (isr != 0));
+ }
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static int compute_mcast_idx(const uint8_t *ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return (crc >> 26);
+}
+
+static int ne2000_buffer_full(NE2000State *s)
+{
+ int avail, index, boundary;
+
+ index = s->curpag << 8;
+ boundary = s->boundary << 8;
+ if (index <= boundary)
+ avail = boundary - index;
+ else
+ avail = (s->stop - s->start) - (index - boundary);
+ if (avail < (MAX_ETH_FRAME_SIZE + 4))
+ return 1;
+ return 0;
+}
+
+static int ne2000_can_receive(void *opaque)
+{
+ NE2000State *s = opaque;
+
+ if (s->cmd & E8390_STOP)
+ return 1;
+ return !ne2000_buffer_full(s);
+}
+
+#define MIN_BUF_SIZE 60
+
+static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ NE2000State *s = opaque;
+ uint8_t *p;
+ int total_len, next, avail, len, index, mcast_idx;
+ uint8_t buf1[60];
+ static const uint8_t broadcast_macaddr[6] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(DEBUG_NE2000)
+ printf("NE2000: received len=%d\n", size);
+#endif
+
+ if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
+ return;
+
+ /* XXX: check this */
+ if (s->rxcr & 0x10) {
+ /* promiscuous: receive all */
+ } else {
+ if (!memcmp(buf, broadcast_macaddr, 6)) {
+ /* broadcast address */
+ if (!(s->rxcr & 0x04))
+ return;
+ } else if (buf[0] & 0x01) {
+ /* multicast */
+ if (!(s->rxcr & 0x08))
+ return;
+ mcast_idx = compute_mcast_idx(buf);
+ if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+ return;
+ } else if (s->mem[0] == buf[0] &&
+ s->mem[2] == buf[1] &&
+ s->mem[4] == buf[2] &&
+ s->mem[6] == buf[3] &&
+ s->mem[8] == buf[4] &&
+ s->mem[10] == buf[5]) {
+ /* match */
+ } else {
+ return;
+ }
+ }
+
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ index = s->curpag << 8;
+ /* 4 bytes for header */
+ total_len = size + 4;
+ /* address for next packet (4 bytes for CRC) */
+ next = index + ((total_len + 4 + 255) & ~0xff);
+ if (next >= s->stop)
+ next -= (s->stop - s->start);
+ /* prepare packet header */
+ p = s->mem + index;
+ s->rsr = ENRSR_RXOK; /* receive status */
+ /* XXX: check this */
+ if (buf[0] & 0x01)
+ s->rsr |= ENRSR_PHY;
+ p[0] = s->rsr;
+ p[1] = next >> 8;
+ p[2] = total_len;
+ p[3] = total_len >> 8;
+ index += 4;
+
+ /* write packet data */
+ while (size > 0) {
+ avail = s->stop - index;
+ len = size;
+ if (len > avail)
+ len = avail;
+ memcpy(s->mem + index, buf, len);
+ buf += len;
+ index += len;
+ if (index == s->stop)
+ index = s->start;
+ size -= len;
+ }
+ s->curpag = next >> 8;
+
+ /* now we can signal we have receive something */
+ s->isr |= ENISR_RX;
+ ne2000_update_irq(s);
+}
+
+static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+ int offset, page, index;
+
+ addr &= 0xf;
+#ifdef DEBUG_NE2000
+ printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+ if (addr == E8390_CMD) {
+ /* control register */
+ s->cmd = val;
+ if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
+ s->isr &= ~ENISR_RESET;
+ /* test specific case: zero length transfert */
+ if ((val & (E8390_RREAD | E8390_RWRITE)) &&
+ s->rcnt == 0) {
+ s->isr |= ENISR_RDC;
+ ne2000_update_irq(s);
+ }
+ if (val & E8390_TRANS) {
+ index = (s->tpsr << 8);
+ /* XXX: next 2 lines are a hack to make netware 3.11 work */
+ if (index >= NE2000_PMEM_END)
+ index -= NE2000_PMEM_SIZE;
+ /* fail safe: check range on the transmitted length */
+ if (index + s->tcnt <= NE2000_PMEM_END) {
+ qemu_send_packet(s->vc, s->mem + index, s->tcnt);
+ }
+ /* signal end of transfert */
+ s->tsr = ENTSR_PTX;
+ s->isr |= ENISR_TX;
+ s->cmd &= ~E8390_TRANS;
+ ne2000_update_irq(s);
+ }
+ }
+ } else {
+ page = s->cmd >> 6;
+ offset = addr | (page << 4);
+ switch(offset) {
+ case EN0_STARTPG:
+ s->start = val << 8;
+ break;
+ case EN0_STOPPG:
+ s->stop = val << 8;
+ break;
+ case EN0_BOUNDARY:
+ s->boundary = val;
+ break;
+ case EN0_IMR:
+ s->imr = val;
+ ne2000_update_irq(s);
+ break;
+ case EN0_TPSR:
+ s->tpsr = val;
+ break;
+ case EN0_TCNTLO:
+ s->tcnt = (s->tcnt & 0xff00) | val;
+ break;
+ case EN0_TCNTHI:
+ s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
+ break;
+ case EN0_RSARLO:
+ s->rsar = (s->rsar & 0xff00) | val;
+ break;
+ case EN0_RSARHI:
+ s->rsar = (s->rsar & 0x00ff) | (val << 8);
+ break;
+ case EN0_RCNTLO:
+ s->rcnt = (s->rcnt & 0xff00) | val;
+ break;
+ case EN0_RCNTHI:
+ s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
+ break;
+ case EN0_RXCR:
+ s->rxcr = val;
+ break;
+ case EN0_DCFG:
+ s->dcfg = val;
+ break;
+ case EN0_ISR:
+ s->isr &= ~(val & 0x7f);
+ ne2000_update_irq(s);
+ break;
+ case EN1_PHYS ... EN1_PHYS + 5:
+ s->phys[offset - EN1_PHYS] = val;
+ break;
+ case EN1_CURPAG:
+ s->curpag = val;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ s->mult[offset - EN1_MULT] = val;
+ break;
+ }
+ }
+}
+
+static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int offset, page, ret;
+
+ addr &= 0xf;
+ if (addr == E8390_CMD) {
+ ret = s->cmd;
+ } else {
+ page = s->cmd >> 6;
+ offset = addr | (page << 4);
+ switch(offset) {
+ case EN0_TSR:
+ ret = s->tsr;
+ break;
+ case EN0_BOUNDARY:
+ ret = s->boundary;
+ break;
+ case EN0_ISR:
+ ret = s->isr;
+ break;
+ case EN0_RSARLO:
+ ret = s->rsar & 0x00ff;
+ break;
+ case EN0_RSARHI:
+ ret = s->rsar >> 8;
+ break;
+ case EN1_PHYS ... EN1_PHYS + 5:
+ ret = s->phys[offset - EN1_PHYS];
+ break;
+ case EN1_CURPAG:
+ ret = s->curpag;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ ret = s->mult[offset - EN1_MULT];
+ break;
+ case EN0_RSR:
+ ret = s->rsr;
+ break;
+ case EN2_STARTPG:
+ ret = s->start >> 8;
+ break;
+ case EN2_STOPPG:
+ ret = s->stop >> 8;
+ break;
+ case EN0_RTL8029ID0:
+ ret = 0x50;
+ break;
+ case EN0_RTL8029ID1:
+ ret = 0x43;
+ break;
+ case EN3_CONFIG0:
+ ret = 0; /* 10baseT media */
+ break;
+ case EN3_CONFIG2:
+ ret = 0x40; /* 10baseT active */
+ break;
+ case EN3_CONFIG3:
+ ret = 0x40; /* Full duplex */
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+ }
+#ifdef DEBUG_NE2000
+ printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ s->mem[addr] = val;
+ }
+}
+
+static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
+ }
+}
+
+static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
+ }
+}
+
+static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
+{
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return s->mem[addr];
+ } else {
+ return 0xff;
+ }
+}
+
+static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return le16_to_cpu(*(uint16_t *)(s->mem + addr));
+ } else {
+ return 0xffff;
+ }
+}
+
+static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return le32_to_cpupu((uint32_t *)(s->mem + addr));
+ } else {
+ return 0xffffffff;
+ }
+}
+
+static inline void ne2000_dma_update(NE2000State *s, int len)
+{
+ s->rsar += len;
+ /* wrap */
+ /* XXX: check what to do if rsar > stop */
+ if (s->rsar == s->stop)
+ s->rsar = s->start;
+
+ if (s->rcnt <= len) {
+ s->rcnt = 0;
+ /* signal end of transfert */
+ s->isr |= ENISR_RDC;
+ ne2000_update_irq(s);
+ } else {
+ s->rcnt -= len;
+ }
+}
+
+static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic write val=0x%04x\n", val);
+#endif
+ if (s->rcnt == 0)
+ return;
+ if (s->dcfg & 0x01) {
+ /* 16 bit access */
+ ne2000_mem_writew(s, s->rsar, val);
+ ne2000_dma_update(s, 2);
+ } else {
+ /* 8 bit access */
+ ne2000_mem_writeb(s, s->rsar, val);
+ ne2000_dma_update(s, 1);
+ }
+}
+
+static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int ret;
+
+ if (s->dcfg & 0x01) {
+ /* 16 bit access */
+ ret = ne2000_mem_readw(s, s->rsar);
+ ne2000_dma_update(s, 2);
+ } else {
+ /* 8 bit access */
+ ret = ne2000_mem_readb(s, s->rsar);
+ ne2000_dma_update(s, 1);
+ }
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic read val=0x%04x\n", ret);
+#endif
+ return ret;
+}
+
+static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic writel val=0x%04x\n", val);
+#endif
+ if (s->rcnt == 0)
+ return;
+ /* 32 bit access */
+ ne2000_mem_writel(s, s->rsar, val);
+ ne2000_dma_update(s, 4);
+}
+
+static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int ret;
+
+ /* 32 bit access */
+ ret = ne2000_mem_readl(s, s->rsar);
+ ne2000_dma_update(s, 4);
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic readl val=0x%04x\n", ret);
+#endif
+ return ret;
+}
+
+static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ /* nothing to do (end of reset pulse) */
+}
+
+static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ ne2000_reset(s);
+ return 0;
+}
+
+static void ne2000_save(QEMUFile* f,void* opaque)
+{
+ NE2000State* s=(NE2000State*)opaque;
+
+ qemu_put_8s(f, &s->rxcr);
+
+ qemu_put_8s(f, &s->cmd);
+ qemu_put_be32s(f, &s->start);
+ qemu_put_be32s(f, &s->stop);
+ qemu_put_8s(f, &s->boundary);
+ qemu_put_8s(f, &s->tsr);
+ qemu_put_8s(f, &s->tpsr);
+ qemu_put_be16s(f, &s->tcnt);
+ qemu_put_be16s(f, &s->rcnt);
+ qemu_put_be32s(f, &s->rsar);
+ qemu_put_8s(f, &s->rsr);
+ qemu_put_8s(f, &s->isr);
+ qemu_put_8s(f, &s->dcfg);
+ qemu_put_8s(f, &s->imr);
+ qemu_put_buffer(f, s->phys, 6);
+ qemu_put_8s(f, &s->curpag);
+ qemu_put_buffer(f, s->mult, 8);
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE);
+}
+
+static int ne2000_load(QEMUFile* f,void* opaque,int version_id)
+{
+ NE2000State* s=(NE2000State*)opaque;
+
+ if (version_id == 2) {
+ qemu_get_8s(f, &s->rxcr);
+ } else if (version_id == 1) {
+ s->rxcr = 0x0c;
+ } else {
+ return -EINVAL;
+ }
+
+ qemu_get_8s(f, &s->cmd);
+ qemu_get_be32s(f, &s->start);
+ qemu_get_be32s(f, &s->stop);
+ qemu_get_8s(f, &s->boundary);
+ qemu_get_8s(f, &s->tsr);
+ qemu_get_8s(f, &s->tpsr);
+ qemu_get_be16s(f, &s->tcnt);
+ qemu_get_be16s(f, &s->rcnt);
+ qemu_get_be32s(f, &s->rsar);
+ qemu_get_8s(f, &s->rsr);
+ qemu_get_8s(f, &s->isr);
+ qemu_get_8s(f, &s->dcfg);
+ qemu_get_8s(f, &s->imr);
+ qemu_get_buffer(f, s->phys, 6);
+ qemu_get_8s(f, &s->curpag);
+ qemu_get_buffer(f, s->mult, 8);
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE);
+
+ return 0;
+}
+
+void isa_ne2000_init(int base, int irq, NICInfo *nd)
+{
+ NE2000State *s;
+
+ s = qemu_mallocz(sizeof(NE2000State));
+ if (!s)
+ return;
+
+ register_ioport_write(base, 16, 1, ne2000_ioport_write, s);
+ register_ioport_read(base, 16, 1, ne2000_ioport_read, s);
+
+ register_ioport_write(base + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+ register_ioport_read(base + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+ register_ioport_write(base + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+ register_ioport_read(base + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+
+ register_ioport_write(base + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+ register_ioport_read(base + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+ s->irq = irq;
+ memcpy(s->macaddr, nd->macaddr, 6);
+
+ ne2000_reset(s);
+
+ s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive,
+ ne2000_can_receive, s);
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "ne2000 macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ s->macaddr[0],
+ s->macaddr[1],
+ s->macaddr[2],
+ s->macaddr[3],
+ s->macaddr[4],
+ s->macaddr[5]);
+
+ register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s);
+}
+
+/***********************************************************/
+/* PCI NE2000 definitions */
+
+typedef struct PCINE2000State {
+ PCIDevice dev;
+ NE2000State ne2000;
+} PCINE2000State;
+
+static void ne2000_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCINE2000State *d = (PCINE2000State *)pci_dev;
+ NE2000State *s = &d->ne2000;
+
+ register_ioport_write(addr, 16, 1, ne2000_ioport_write, s);
+ register_ioport_read(addr, 16, 1, ne2000_ioport_read, s);
+
+ register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+ register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+ register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+ register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+ register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s);
+ register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s);
+
+ register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+ register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+}
+
+void pci_ne2000_init(PCIBus *bus, NICInfo *nd)
+{
+ PCINE2000State *d;
+ NE2000State *s;
+ uint8_t *pci_conf;
+
+ d = (PCINE2000State *)pci_register_device(bus,
+ "NE2000", sizeof(PCINE2000State),
+ -1,
+ NULL, NULL);
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0xec; // Realtek 8029
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0x29;
+ pci_conf[0x03] = 0x80;
+ pci_conf[0x0a] = 0x00; // ethernet network controller
+ pci_conf[0x0b] = 0x02;
+ pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x3d] = 1; // interrupt pin 0
+
+ pci_register_io_region(&d->dev, 0, 0x100,
+ PCI_ADDRESS_SPACE_IO, ne2000_map);
+ s = &d->ne2000;
+ s->irq = 16; // PCI interrupt
+ s->pci_dev = (PCIDevice *)d;
+ memcpy(s->macaddr, nd->macaddr, 6);
+ ne2000_reset(s);
+ s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive,
+ ne2000_can_receive, s);
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "ne2000 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ s->macaddr[0],
+ s->macaddr[1],
+ s->macaddr[2],
+ s->macaddr[3],
+ s->macaddr[4],
+ s->macaddr[5]);
+
+ /* XXX: instance number ? */
+ register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s);
+ register_savevm("ne2000_pci", 0, 1, generic_pci_save, generic_pci_load,
+ &d->dev);
+}
diff --git a/hw/openpic.c b/hw/openpic.c
new file mode 100644
index 0000000..3177337
--- /dev/null
+++ b/hw/openpic.c
@@ -0,0 +1,1027 @@
+/*
+ * OpenPIC emulation
+ *
+ * Copyright (c) 2004 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ *
+ * Based on OpenPic implementations:
+ * - Intel GW80314 I/O compagnion chip developper's manual
+ * - Motorola MPC8245 & MPC8540 user manuals.
+ * - Motorola MCP750 (aka Raven) programmer manual.
+ * - Motorola Harrier programmer manuel
+ *
+ * Serial interrupts, as implemented in Raven chipset are not supported yet.
+ *
+ */
+#include "vl.h"
+
+//#define DEBUG_OPENPIC
+
+#ifdef DEBUG_OPENPIC
+#define DPRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do { } while (0)
+#endif
+#define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0)
+
+#define USE_MPCxxx /* Intel model is broken, for now */
+
+#if defined (USE_INTEL_GW80314)
+/* Intel GW80314 I/O Companion chip */
+
+#define MAX_CPU 4
+#define MAX_IRQ 32
+#define MAX_DBL 4
+#define MAX_MBX 4
+#define MAX_TMR 4
+#define VECTOR_BITS 8
+#define MAX_IPI 0
+
+#define VID (0x00000000)
+
+#define OPENPIC_LITTLE_ENDIAN 1
+#define OPENPIC_BIG_ENDIAN 0
+
+#elif defined(USE_MPCxxx)
+
+#define MAX_CPU 2
+#define MAX_IRQ 64
+#define EXT_IRQ 48
+#define MAX_DBL 0
+#define MAX_MBX 0
+#define MAX_TMR 4
+#define VECTOR_BITS 8
+#define MAX_IPI 4
+#define VID 0x03 /* MPIC version ID */
+#define VENI 0x00000000 /* Vendor ID */
+
+enum {
+ IRQ_IPVP = 0,
+ IRQ_IDE,
+};
+
+#define OPENPIC_LITTLE_ENDIAN 1
+#define OPENPIC_BIG_ENDIAN 0
+
+#else
+#error "Please select which OpenPic implementation is to be emulated"
+#endif
+
+#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
+ (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
+#define OPENPIC_SWAP
+#endif
+
+/* Interrupt definitions */
+#define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */
+#define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */
+#define IRQ_TIM0 (EXT_IRQ + 2) /* First timer IRQ */
+#if MAX_IPI > 0
+#define IRQ_IPI0 (IRQ_TIM0 + MAX_TMR) /* First IPI IRQ */
+#define IRQ_DBL0 (IRQ_IPI0 + (MAX_CPU * MAX_IPI)) /* First doorbell IRQ */
+#else
+#define IRQ_DBL0 (IRQ_TIM0 + MAX_TMR) /* First doorbell IRQ */
+#define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */
+#endif
+
+#define BF_WIDTH(_bits_) \
+(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
+
+static inline void set_bit (uint32_t *field, int bit)
+{
+ field[bit >> 5] |= 1 << (bit & 0x1F);
+}
+
+static inline void reset_bit (uint32_t *field, int bit)
+{
+ field[bit >> 5] &= ~(1 << (bit & 0x1F));
+}
+
+static inline int test_bit (uint32_t *field, int bit)
+{
+ return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
+}
+
+enum {
+ IRQ_EXTERNAL = 0x01,
+ IRQ_INTERNAL = 0x02,
+ IRQ_TIMER = 0x04,
+ IRQ_SPECIAL = 0x08,
+} IRQ_src_type;
+
+typedef struct IRQ_queue_t {
+ uint32_t queue[BF_WIDTH(MAX_IRQ)];
+ int next;
+ int priority;
+} IRQ_queue_t;
+
+typedef struct IRQ_src_t {
+ uint32_t ipvp; /* IRQ vector/priority register */
+ uint32_t ide; /* IRQ destination register */
+ int type;
+ int last_cpu;
+ int pending; /* TRUE if IRQ is pending */
+} IRQ_src_t;
+
+enum IPVP_bits {
+ IPVP_MASK = 31,
+ IPVP_ACTIVITY = 30,
+ IPVP_MODE = 29,
+ IPVP_POLARITY = 23,
+ IPVP_SENSE = 22,
+};
+#define IPVP_PRIORITY_MASK (0x1F << 16)
+#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16))
+#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1)
+#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
+
+typedef struct IRQ_dst_t {
+ uint32_t pctp; /* CPU current task priority */
+ uint32_t pcsr; /* CPU sensitivity register */
+ IRQ_queue_t raised;
+ IRQ_queue_t servicing;
+ CPUState *env;
+} IRQ_dst_t;
+
+struct openpic_t {
+ PCIDevice pci_dev;
+ int mem_index;
+ /* Global registers */
+ uint32_t frep; /* Feature reporting register */
+ uint32_t glbc; /* Global configuration register */
+ uint32_t micr; /* MPIC interrupt configuration register */
+ uint32_t veni; /* Vendor identification register */
+ uint32_t spve; /* Spurious vector register */
+ uint32_t tifr; /* Timer frequency reporting register */
+ /* Source registers */
+ IRQ_src_t src[MAX_IRQ];
+ /* Local registers per output pin */
+ IRQ_dst_t dst[MAX_CPU];
+ int nb_cpus;
+ /* Timer registers */
+ struct {
+ uint32_t ticc; /* Global timer current count register */
+ uint32_t tibc; /* Global timer base count register */
+ } timers[MAX_TMR];
+#if MAX_DBL > 0
+ /* Doorbell registers */
+ uint32_t dar; /* Doorbell activate register */
+ struct {
+ uint32_t dmr; /* Doorbell messaging register */
+ } doorbells[MAX_DBL];
+#endif
+#if MAX_MBX > 0
+ /* Mailbox registers */
+ struct {
+ uint32_t mbr; /* Mailbox register */
+ } mailboxes[MAX_MAILBOXES];
+#endif
+};
+
+static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
+{
+ set_bit(q->queue, n_IRQ);
+}
+
+static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
+{
+ reset_bit(q->queue, n_IRQ);
+}
+
+static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
+{
+ return test_bit(q->queue, n_IRQ);
+}
+
+static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
+{
+ int next, i;
+ int priority;
+
+ next = -1;
+ priority = -1;
+ for (i = 0; i < MAX_IRQ; i++) {
+ if (IRQ_testbit(q, i)) {
+ DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n",
+ i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
+ if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
+ next = i;
+ priority = IPVP_PRIORITY(opp->src[i].ipvp);
+ }
+ }
+ }
+ q->next = next;
+ q->priority = priority;
+}
+
+static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q)
+{
+ if (q->next == -1) {
+ /* XXX: optimize */
+ IRQ_check(opp, q);
+ }
+
+ return q->next;
+}
+
+static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
+{
+ IRQ_dst_t *dst;
+ IRQ_src_t *src;
+ int priority;
+
+ dst = &opp->dst[n_CPU];
+ src = &opp->src[n_IRQ];
+ priority = IPVP_PRIORITY(src->ipvp);
+ if (priority <= dst->pctp) {
+ /* Too low priority */
+ return;
+ }
+ if (IRQ_testbit(&dst->raised, n_IRQ)) {
+ /* Interrupt miss */
+ return;
+ }
+ set_bit(&src->ipvp, IPVP_ACTIVITY);
+ IRQ_setbit(&dst->raised, n_IRQ);
+ if (priority > dst->raised.priority) {
+ IRQ_get_next(opp, &dst->raised);
+ DPRINTF("Raise CPU IRQ\n");
+ cpu_interrupt(dst->env, CPU_INTERRUPT_HARD);
+ }
+}
+
+/* update pic state because registers for n_IRQ have changed value */
+static void openpic_update_irq(openpic_t *opp, int n_IRQ)
+{
+ IRQ_src_t *src;
+ int i;
+
+ src = &opp->src[n_IRQ];
+
+ if (!src->pending) {
+ /* no irq pending */
+ return;
+ }
+ if (test_bit(&src->ipvp, IPVP_MASK)) {
+ /* Interrupt source is disabled */
+ return;
+ }
+ if (IPVP_PRIORITY(src->ipvp) == 0) {
+ /* Priority set to zero */
+ return;
+ }
+ if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
+ /* IRQ already active */
+ return;
+ }
+ if (src->ide == 0x00000000) {
+ /* No target */
+ return;
+ }
+
+ if (!test_bit(&src->ipvp, IPVP_MODE) ||
+ src->ide == (1 << src->last_cpu)) {
+ /* Directed delivery mode */
+ for (i = 0; i < opp->nb_cpus; i++) {
+ if (test_bit(&src->ide, i))
+ IRQ_local_pipe(opp, i, n_IRQ);
+ }
+ } else {
+ /* Distributed delivery mode */
+ /* XXX: incorrect code */
+ for (i = src->last_cpu; i < src->last_cpu; i++) {
+ if (i == MAX_IRQ)
+ i = 0;
+ if (test_bit(&src->ide, i)) {
+ IRQ_local_pipe(opp, i, n_IRQ);
+ src->last_cpu = i;
+ break;
+ }
+ }
+ }
+}
+
+void openpic_set_irq(void *opaque, int n_IRQ, int level)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+
+ src = &opp->src[n_IRQ];
+ DPRINTF("openpic: set irq %d = %d ipvp=%08x\n",
+ n_IRQ, level, src->ipvp);
+ if (test_bit(&src->ipvp, IPVP_SENSE)) {
+ /* level-sensitive irq */
+ src->pending = level;
+ if (!level)
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ } else {
+ /* edge-sensitive irq */
+ if (level)
+ src->pending = 1;
+ }
+ openpic_update_irq(opp, n_IRQ);
+}
+
+static void openpic_reset (openpic_t *opp)
+{
+ int i;
+
+ opp->glbc = 0x80000000;
+ /* Initialise controller registers */
+ opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID;
+ opp->veni = VENI;
+ opp->spve = 0x000000FF;
+ opp->tifr = 0x003F7A00;
+ /* ? */
+ opp->micr = 0x00000000;
+ /* Initialise IRQ sources */
+ for (i = 0; i < MAX_IRQ; i++) {
+ opp->src[i].ipvp = 0xA0000000;
+ opp->src[i].ide = 0x00000000;
+ }
+ /* Initialise IRQ destinations */
+ for (i = 0; i < opp->nb_cpus; i++) {
+ opp->dst[i].pctp = 0x0000000F;
+ opp->dst[i].pcsr = 0x00000000;
+ memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t));
+ memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
+ }
+ /* Initialise timers */
+ for (i = 0; i < MAX_TMR; i++) {
+ opp->timers[i].ticc = 0x00000000;
+ opp->timers[i].tibc = 0x80000000;
+ }
+ /* Initialise doorbells */
+#if MAX_DBL > 0
+ opp->dar = 0x00000000;
+ for (i = 0; i < MAX_DBL; i++) {
+ opp->doorbells[i].dmr = 0x00000000;
+ }
+#endif
+ /* Initialise mailboxes */
+#if MAX_MBX > 0
+ for (i = 0; i < MAX_MBX; i++) { /* ? */
+ opp->mailboxes[i].mbr = 0x00000000;
+ }
+#endif
+ /* Go out of RESET state */
+ opp->glbc = 0x00000000;
+}
+
+static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg)
+{
+ uint32_t retval;
+
+ switch (reg) {
+ case IRQ_IPVP:
+ retval = opp->src[n_IRQ].ipvp;
+ break;
+ case IRQ_IDE:
+ retval = opp->src[n_IRQ].ide;
+ break;
+ }
+
+ return retval;
+}
+
+static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
+ uint32_t reg, uint32_t val)
+{
+ uint32_t tmp;
+
+ switch (reg) {
+ case IRQ_IPVP:
+ /* NOTE: not fully accurate for special IRQs, but simple and
+ sufficient */
+ /* ACTIVITY bit is read-only */
+ opp->src[n_IRQ].ipvp =
+ (opp->src[n_IRQ].ipvp & 0x40000000) |
+ (val & 0x800F00FF);
+ openpic_update_irq(opp, n_IRQ);
+ DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n",
+ n_IRQ, val, opp->src[n_IRQ].ipvp);
+ break;
+ case IRQ_IDE:
+ tmp = val & 0xC0000000;
+ tmp |= val & ((1 << MAX_CPU) - 1);
+ opp->src[n_IRQ].ide = tmp;
+ DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
+ break;
+ }
+}
+
+#if 0 // Code provision for Intel model
+#if MAX_DBL > 0
+static uint32_t read_doorbell_register (openpic_t *opp,
+ int n_dbl, uint32_t offset)
+{
+ uint32_t retval;
+
+ switch (offset) {
+ case DBL_IPVP_OFFSET:
+ retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP);
+ break;
+ case DBL_IDE_OFFSET:
+ retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE);
+ break;
+ case DBL_DMR_OFFSET:
+ retval = opp->doorbells[n_dbl].dmr;
+ break;
+ }
+
+ return retval;
+}
+
+static void write_doorbell_register (penpic_t *opp, int n_dbl,
+ uint32_t offset, uint32_t value)
+{
+ switch (offset) {
+ case DBL_IVPR_OFFSET:
+ write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value);
+ break;
+ case DBL_IDE_OFFSET:
+ write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value);
+ break;
+ case DBL_DMR_OFFSET:
+ opp->doorbells[n_dbl].dmr = value;
+ break;
+ }
+}
+#endif
+
+#if MAX_MBX > 0
+static uint32_t read_mailbox_register (openpic_t *opp,
+ int n_mbx, uint32_t offset)
+{
+ uint32_t retval;
+
+ switch (offset) {
+ case MBX_MBR_OFFSET:
+ retval = opp->mailboxes[n_mbx].mbr;
+ break;
+ case MBX_IVPR_OFFSET:
+ retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP);
+ break;
+ case MBX_DMR_OFFSET:
+ retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE);
+ break;
+ }
+
+ return retval;
+}
+
+static void write_mailbox_register (openpic_t *opp, int n_mbx,
+ uint32_t address, uint32_t value)
+{
+ switch (offset) {
+ case MBX_MBR_OFFSET:
+ opp->mailboxes[n_mbx].mbr = value;
+ break;
+ case MBX_IVPR_OFFSET:
+ write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value);
+ break;
+ case MBX_DMR_OFFSET:
+ write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value);
+ break;
+ }
+}
+#endif
+#endif /* 0 : Code provision for Intel model */
+
+static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+#if defined OPENPIC_SWAP
+ val = bswap32(val);
+#endif
+ addr &= 0xFF;
+ switch (addr) {
+ case 0x00: /* FREP */
+ break;
+ case 0x20: /* GLBC */
+ if (val & 0x80000000)
+ openpic_reset(opp);
+ opp->glbc = val & ~0x80000000;
+ break;
+ case 0x80: /* VENI */
+ break;
+ case 0x90: /* PINT */
+ /* XXX: Should be able to reset any CPU */
+ if (val & 1) {
+ DPRINTF("Reset CPU IRQ\n");
+ // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET);
+ }
+ break;
+#if MAX_IPI > 0
+ case 0xA0: /* IPI_IPVP */
+ case 0xB0:
+ case 0xC0:
+ case 0xD0:
+ {
+ int idx;
+ idx = (addr - 0xA0) >> 4;
+ write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP, val);
+ }
+ break;
+#endif
+ case 0xE0: /* SPVE */
+ opp->spve = val & 0x000000FF;
+ break;
+ case 0xF0: /* TIFR */
+ opp->tifr = val;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t openpic_gbl_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr &= 0xFF;
+ switch (addr) {
+ case 0x00: /* FREP */
+ retval = opp->frep;
+ break;
+ case 0x20: /* GLBC */
+ retval = opp->glbc;
+ break;
+ case 0x80: /* VENI */
+ retval = opp->veni;
+ break;
+ case 0x90: /* PINT */
+ retval = 0x00000000;
+ break;
+#if MAX_IPI > 0
+ case 0xA0: /* IPI_IPVP */
+ case 0xB0:
+ case 0xC0:
+ case 0xD0:
+ {
+ int idx;
+ idx = (addr - 0xA0) >> 4;
+ retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP);
+ }
+ break;
+#endif
+ case 0xE0: /* SPVE */
+ retval = opp->spve;
+ break;
+ case 0xF0: /* TIFR */
+ retval = opp->tifr;
+ break;
+ default:
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined OPENPIC_SWAP
+ retval = bswap32(retval);
+#endif
+
+ return retval;
+}
+
+static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ int idx;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+#if defined OPENPIC_SWAP
+ val = bswap32(val);
+#endif
+ addr -= 0x1100;
+ addr &= 0xFFFF;
+ idx = (addr & 0xFFF0) >> 6;
+ addr = addr & 0x30;
+ switch (addr) {
+ case 0x00: /* TICC */
+ break;
+ case 0x10: /* TIBC */
+ if ((opp->timers[idx].ticc & 0x80000000) != 0 &&
+ (val & 0x80000000) == 0 &&
+ (opp->timers[idx].tibc & 0x80000000) != 0)
+ opp->timers[idx].ticc &= ~0x80000000;
+ opp->timers[idx].tibc = val;
+ break;
+ case 0x20: /* TIVP */
+ write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP, val);
+ break;
+ case 0x30: /* TIDE */
+ write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE, val);
+ break;
+ }
+}
+
+static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+ int idx;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr -= 0x1100;
+ addr &= 0xFFFF;
+ idx = (addr & 0xFFF0) >> 6;
+ addr = addr & 0x30;
+ switch (addr) {
+ case 0x00: /* TICC */
+ retval = opp->timers[idx].ticc;
+ break;
+ case 0x10: /* TIBC */
+ retval = opp->timers[idx].tibc;
+ break;
+ case 0x20: /* TIPV */
+ retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP);
+ break;
+ case 0x30: /* TIDE */
+ retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE);
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined OPENPIC_SWAP
+ retval = bswap32(retval);
+#endif
+
+ return retval;
+}
+
+static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ int idx;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+#if defined OPENPIC_SWAP
+ val = tswap32(val);
+#endif
+ addr = addr & 0xFFF0;
+ idx = addr >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(opp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(opp, idx, IRQ_IPVP, val);
+ }
+}
+
+static uint32_t openpic_src_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+ int idx;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr = addr & 0xFFF0;
+ idx = addr >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(opp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(opp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined OPENPIC_SWAP
+ retval = tswap32(retval);
+#endif
+
+ return retval;
+}
+
+static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+ IRQ_dst_t *dst;
+ int idx, n_IRQ;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+#if defined OPENPIC_SWAP
+ val = bswap32(val);
+#endif
+ addr &= 0x1FFF0;
+ idx = addr / 0x1000;
+ dst = &opp->dst[idx];
+ addr &= 0xFF0;
+ switch (addr) {
+#if MAX_IPI > 0
+ case 0x40: /* PIPD */
+ case 0x50:
+ case 0x60:
+ case 0x70:
+ idx = (addr - 0x40) >> 4;
+ write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE, val);
+ openpic_set_irq(opp, IRQ_IPI0 + idx, 1);
+ openpic_set_irq(opp, IRQ_IPI0 + idx, 0);
+ break;
+#endif
+ case 0x80: /* PCTP */
+ dst->pctp = val & 0x0000000F;
+ break;
+ case 0x90: /* WHOAMI */
+ /* Read-only register */
+ break;
+ case 0xA0: /* PIAC */
+ /* Read-only register */
+ break;
+ case 0xB0: /* PEOI */
+ DPRINTF("PEOI\n");
+ n_IRQ = IRQ_get_next(opp, &dst->servicing);
+ IRQ_resetbit(&dst->servicing, n_IRQ);
+ dst->servicing.next = -1;
+ src = &opp->src[n_IRQ];
+ /* Set up next servicing IRQ */
+ IRQ_get_next(opp, &dst->servicing);
+ /* Check queued interrupts. */
+ n_IRQ = IRQ_get_next(opp, &dst->raised);
+ if (n_IRQ != -1) {
+ src = &opp->src[n_IRQ];
+ if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) {
+ DPRINTF("Raise CPU IRQ\n");
+ cpu_interrupt(dst->env, CPU_INTERRUPT_HARD);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t openpic_cpu_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+ IRQ_dst_t *dst;
+ uint32_t retval;
+ int idx, n_IRQ;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr &= 0x1FFF0;
+ idx = addr / 0x1000;
+ dst = &opp->dst[idx];
+ addr &= 0xFF0;
+ switch (addr) {
+ case 0x80: /* PCTP */
+ retval = dst->pctp;
+ break;
+ case 0x90: /* WHOAMI */
+ retval = idx;
+ break;
+ case 0xA0: /* PIAC */
+ n_IRQ = IRQ_get_next(opp, &dst->raised);
+ DPRINTF("PIAC: irq=%d\n", n_IRQ);
+ if (n_IRQ == -1) {
+ /* No more interrupt pending */
+ retval = opp->spve;
+ } else {
+ src = &opp->src[n_IRQ];
+ if (!test_bit(&src->ipvp, IPVP_ACTIVITY) ||
+ !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) {
+ /* - Spurious level-sensitive IRQ
+ * - Priorities has been changed
+ * and the pending IRQ isn't allowed anymore
+ */
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ retval = IPVP_VECTOR(opp->spve);
+ } else {
+ /* IRQ enter servicing state */
+ IRQ_setbit(&dst->servicing, n_IRQ);
+ retval = IPVP_VECTOR(src->ipvp);
+ }
+ IRQ_resetbit(&dst->raised, n_IRQ);
+ dst->raised.next = -1;
+ if (!test_bit(&src->ipvp, IPVP_SENSE)) {
+ /* edge-sensitive IRQ */
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ src->pending = 0;
+ }
+ }
+ break;
+ case 0xB0: /* PEOI */
+ retval = 0;
+ break;
+#if MAX_IPI > 0
+ case 0x40: /* IDE */
+ case 0x50:
+ idx = (addr - 0x40) >> 4;
+ retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE);
+ break;
+#endif
+ default:
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+#if defined OPENPIC_SWAP
+ retval= bswap32(retval);
+#endif
+
+ return retval;
+}
+
+static void openpic_buggy_write (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ printf("Invalid OPENPIC write access !\n");
+}
+
+static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr)
+{
+ printf("Invalid OPENPIC read access !\n");
+
+ return -1;
+}
+
+static void openpic_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+
+ addr &= 0x3FFFF;
+ DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
+ if (addr < 0x1100) {
+ /* Global registers */
+ openpic_gbl_write(opp, addr, val);
+ } else if (addr < 0x10000) {
+ /* Timers registers */
+ openpic_timer_write(opp, addr, val);
+ } else if (addr < 0x20000) {
+ /* Source registers */
+ openpic_src_write(opp, addr, val);
+ } else {
+ /* CPU registers */
+ openpic_cpu_write(opp, addr, val);
+ }
+}
+
+static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+
+ addr &= 0x3FFFF;
+ DPRINTF("%s: offset %08x\n", __func__, (int)addr);
+ if (addr < 0x1100) {
+ /* Global registers */
+ retval = openpic_gbl_read(opp, addr);
+ } else if (addr < 0x10000) {
+ /* Timers registers */
+ retval = openpic_timer_read(opp, addr);
+ } else if (addr < 0x20000) {
+ /* Source registers */
+ retval = openpic_src_read(opp, addr);
+ } else {
+ /* CPU registers */
+ retval = openpic_cpu_read(opp, addr);
+ }
+
+ return retval;
+}
+
+static CPUWriteMemoryFunc *openpic_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &openpic_writel,
+};
+
+static CPUReadMemoryFunc *openpic_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &openpic_readl,
+};
+
+static void openpic_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ openpic_t *opp;
+
+ DPRINTF("Map OpenPIC\n");
+ opp = (openpic_t *)pci_dev;
+ /* Global registers */
+ DPRINTF("Register OPENPIC gbl %08x => %08x\n",
+ addr + 0x1000, addr + 0x1000 + 0x100);
+ /* Timer registers */
+ DPRINTF("Register OPENPIC timer %08x => %08x\n",
+ addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR);
+ /* Interrupt source registers */
+ DPRINTF("Register OPENPIC src %08x => %08x\n",
+ addr + 0x10000, addr + 0x10000 + 0x20 * (EXT_IRQ + 2));
+ /* Per CPU registers */
+ DPRINTF("Register OPENPIC dst %08x => %08x\n",
+ addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU);
+ cpu_register_physical_memory(addr, 0x40000, opp->mem_index);
+#if 0 // Don't implement ISU for now
+ opp_io_memory = cpu_register_io_memory(0, openpic_src_read,
+ openpic_src_write);
+ cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2),
+ opp_io_memory);
+#endif
+}
+
+openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
+ CPUPPCState **envp)
+{
+ openpic_t *opp;
+ uint8_t *pci_conf;
+ int i, m;
+
+ /* XXX: for now, only one CPU is supported */
+ if (nb_cpus != 1)
+ return NULL;
+ if (bus) {
+ opp = (openpic_t *)pci_register_device(bus, "OpenPIC", sizeof(openpic_t),
+ -1, NULL, NULL);
+ if (opp == NULL)
+ return NULL;
+ pci_conf = opp->pci_dev.config;
+ pci_conf[0x00] = 0x14; // IBM MPIC2
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0xFF;
+ pci_conf[0x03] = 0xFF;
+ pci_conf[0x0a] = 0x80; // PIC
+ pci_conf[0x0b] = 0x08;
+ pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x3d] = 0x00; // no interrupt pin
+
+ /* Register I/O spaces */
+ pci_register_io_region((PCIDevice *)opp, 0, 0x40000,
+ PCI_ADDRESS_SPACE_MEM, &openpic_map);
+ } else {
+ opp = qemu_mallocz(sizeof(openpic_t));
+ }
+
+ opp->mem_index = cpu_register_io_memory(0, openpic_read,
+ openpic_write, opp);
+
+ // isu_base &= 0xFFFC0000;
+ opp->nb_cpus = nb_cpus;
+ /* Set IRQ types */
+ for (i = 0; i < EXT_IRQ; i++) {
+ opp->src[i].type = IRQ_EXTERNAL;
+ }
+ for (; i < IRQ_TIM0; i++) {
+ opp->src[i].type = IRQ_SPECIAL;
+ }
+#if MAX_IPI > 0
+ m = IRQ_IPI0;
+#else
+ m = IRQ_DBL0;
+#endif
+ for (; i < m; i++) {
+ opp->src[i].type = IRQ_TIMER;
+ }
+ for (; i < MAX_IRQ; i++) {
+ opp->src[i].type = IRQ_INTERNAL;
+ }
+ for (i = 0; i < nb_cpus; i++)
+ opp->dst[i].env = envp[i];
+ openpic_reset(opp);
+ if (pmem_index)
+ *pmem_index = opp->mem_index;
+ return opp;
+}
diff --git a/hw/parallel.c b/hw/parallel.c
new file mode 100644
index 0000000..cba9561
--- /dev/null
+++ b/hw/parallel.c
@@ -0,0 +1,183 @@
+/*
+ * QEMU Parallel PORT emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_PARALLEL
+
+/*
+ * These are the definitions for the Printer Status Register
+ */
+#define PARA_STS_BUSY 0x80 /* Busy complement */
+#define PARA_STS_ACK 0x40 /* Acknowledge */
+#define PARA_STS_PAPER 0x20 /* Out of paper */
+#define PARA_STS_ONLINE 0x10 /* Online */
+#define PARA_STS_ERROR 0x08 /* Error complement */
+
+/*
+ * These are the definitions for the Printer Control Register
+ */
+#define PARA_CTR_INTEN 0x10 /* IRQ Enable */
+#define PARA_CTR_SELECT 0x08 /* Select In complement */
+#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */
+#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */
+#define PARA_CTR_STROBE 0x01 /* Strobe complement */
+
+struct ParallelState {
+ uint8_t data;
+ uint8_t status; /* read only register */
+ uint8_t control;
+ int irq;
+ int irq_pending;
+ CharDriverState *chr;
+ int hw_driver;
+};
+
+static void parallel_update_irq(ParallelState *s)
+{
+ if (s->irq_pending)
+ pic_set_irq(s->irq, 1);
+ else
+ pic_set_irq(s->irq, 0);
+}
+
+static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+
+ addr &= 7;
+#ifdef DEBUG_PARALLEL
+ printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ switch(addr) {
+ case 0:
+ if (s->hw_driver) {
+ s->data = val;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &s->data);
+ } else {
+ s->data = val;
+ parallel_update_irq(s);
+ }
+ break;
+ case 2:
+ if (s->hw_driver) {
+ s->control = val;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &s->control);
+ } else {
+ if ((val & PARA_CTR_INIT) == 0 ) {
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ }
+ else if (val & PARA_CTR_SELECT) {
+ if (val & PARA_CTR_STROBE) {
+ s->status &= ~PARA_STS_BUSY;
+ if ((s->control & PARA_CTR_STROBE) == 0)
+ qemu_chr_write(s->chr, &s->data, 1);
+ } else {
+ if (s->control & PARA_CTR_INTEN) {
+ s->irq_pending = 1;
+ }
+ }
+ }
+ parallel_update_irq(s);
+ s->control = val;
+ }
+ break;
+ }
+}
+
+static uint32_t parallel_ioport_read(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret = 0xff;
+
+ addr &= 7;
+ switch(addr) {
+ case 0:
+ if (s->hw_driver) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data);
+ }
+ ret = s->data;
+ break;
+ case 1:
+ if (s->hw_driver) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &s->status);
+ ret = s->status;
+ } else {
+ ret = s->status;
+ s->irq_pending = 0;
+ if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
+ /* XXX Fixme: wait 5 microseconds */
+ if (s->status & PARA_STS_ACK)
+ s->status &= ~PARA_STS_ACK;
+ else {
+ /* XXX Fixme: wait 5 microseconds */
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_BUSY;
+ }
+ }
+ parallel_update_irq(s);
+ }
+ break;
+ case 2:
+ if (s->hw_driver) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control);
+ }
+ ret = s->control;
+ break;
+ }
+#ifdef DEBUG_PARALLEL
+ printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+/* If fd is zero, it means that the parallel device uses the console */
+ParallelState *parallel_init(int base, int irq, CharDriverState *chr)
+{
+ ParallelState *s;
+ uint8_t dummy;
+
+ s = qemu_mallocz(sizeof(ParallelState));
+ if (!s)
+ return NULL;
+ s->chr = chr;
+ s->hw_driver = 0;
+ if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0)
+ s->hw_driver = 1;
+
+ s->irq = irq;
+ s->data = 0;
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ s->control = PARA_CTR_SELECT;
+ s->control |= PARA_CTR_INIT;
+
+ register_ioport_write(base, 8, 1, parallel_ioport_write, s);
+ register_ioport_read(base, 8, 1, parallel_ioport_read, s);
+ return s;
+}
diff --git a/hw/pc.c b/hw/pc.c
new file mode 100644
index 0000000..898d068
--- /dev/null
+++ b/hw/pc.c
@@ -0,0 +1,911 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* output Bochs bios info messages */
+//#define DEBUG_BIOS
+
+#define BIOS_FILENAME "bios.bin"
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+#define LINUX_BOOT_FILENAME "linux_boot.bin"
+
+#define KERNEL_LOAD_ADDR 0x00100000
+#define INITRD_LOAD_ADDR 0x00600000
+#define KERNEL_PARAMS_ADDR 0x00090000
+#define KERNEL_CMDLINE_ADDR 0x00099000
+
+static fdctrl_t *floppy_controller;
+static RTCState *rtc_state;
+static PITState *pit;
+static IOAPICState *ioapic;
+
+static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+{
+}
+
+/* MSDOS compatibility mode FPU exception support */
+/* XXX: add IGNNE support */
+void cpu_set_ferr(CPUX86State *s)
+{
+ pic_set_irq(13, 1);
+}
+
+static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ pic_set_irq(13, 0);
+}
+
+/* TSC handling */
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ /* Note: when using kqemu, it is more logical to return the host TSC
+ because kqemu does not trap the RDTSC instruction for
+ performance reasons */
+#if USE_KQEMU
+ if (env->kqemu_enabled) {
+ return cpu_get_real_ticks();
+ } else
+#endif
+ {
+ return cpu_get_ticks();
+ }
+}
+
+/* IRQ handling */
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ int intno;
+
+ intno = apic_get_interrupt(env);
+ if (intno >= 0) {
+ /* set irq request if a PIC irq is still pending */
+ /* XXX: improve that */
+ pic_update_irq(isa_pic);
+ return intno;
+ }
+ /* read the irq from the PIC */
+ intno = pic_read_irq(isa_pic);
+ return intno;
+}
+
+static void pic_irq_request(void *opaque, int level)
+{
+ CPUState *env = opaque;
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE 0x14
+#define REG_IBM_CENTURY_BYTE 0x32
+#define REG_IBM_PS2_CENTURY_BYTE 0x37
+
+
+static inline int to_bcd(RTCState *s, int a)
+{
+ return ((a / 10) << 4) | (a % 10);
+}
+
+static int cmos_get_fd_drive_type(int fd0)
+{
+ int val;
+
+ switch (fd0) {
+ case 0:
+ /* 1.44 Mb 3"5 drive */
+ val = 4;
+ break;
+ case 1:
+ /* 2.88 Mb 3"5 drive */
+ val = 5;
+ break;
+ case 2:
+ /* 1.2 Mb 5"5 drive */
+ val = 2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+{
+ RTCState *s = rtc_state;
+ int cylinders, heads, sectors;
+ bdrv_get_geometry_hint(hd, &cylinders, &heads, &sectors);
+ rtc_set_memory(s, type_ofs, 47);
+ rtc_set_memory(s, info_ofs, cylinders);
+ rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 2, heads);
+ rtc_set_memory(s, info_ofs + 3, 0xff);
+ rtc_set_memory(s, info_ofs + 4, 0xff);
+ rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
+ rtc_set_memory(s, info_ofs + 6, cylinders);
+ rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 8, sectors);
+}
+
+/* hd_table must contain 4 block drivers */
+static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table)
+{
+ RTCState *s = rtc_state;
+ int val;
+ int fd0, fd1, nb;
+ time_t ti;
+ struct tm *tm;
+ int i;
+
+ /* set the CMOS date */
+ time(&ti);
+ if (rtc_utc)
+ tm = gmtime(&ti);
+ else
+ tm = localtime(&ti);
+ rtc_set_date(s, tm);
+
+ val = to_bcd(s, (tm->tm_year / 100) + 19);
+ rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val);
+ rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val);
+
+ /* various important CMOS locations needed by PC/Bochs bios */
+
+ /* memory size */
+ val = 640; /* base memory in K */
+ rtc_set_memory(s, 0x15, val);
+ rtc_set_memory(s, 0x16, val >> 8);
+
+ val = (ram_size / 1024) - 1024;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x17, val);
+ rtc_set_memory(s, 0x18, val >> 8);
+ rtc_set_memory(s, 0x30, val);
+ rtc_set_memory(s, 0x31, val >> 8);
+
+ if (ram_size > (16 * 1024 * 1024))
+ val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+ else
+ val = 0;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x34, val);
+ rtc_set_memory(s, 0x35, val >> 8);
+
+ switch(boot_device) {
+ case 'a':
+ case 'b':
+ rtc_set_memory(s, 0x3d, 0x01); /* floppy boot */
+ if (!fd_bootchk)
+ rtc_set_memory(s, 0x38, 0x01); /* disable signature check */
+ break;
+ default:
+ case 'c':
+ rtc_set_memory(s, 0x3d, 0x02); /* hard drive boot */
+ break;
+ case 'd':
+ rtc_set_memory(s, 0x3d, 0x03); /* CD-ROM boot */
+ break;
+ }
+
+ /* floppy type */
+
+ fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+ fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+ val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+ rtc_set_memory(s, 0x10, val);
+
+ val = 0;
+ nb = 0;
+ if (fd0 < 3)
+ nb++;
+ if (fd1 < 3)
+ nb++;
+ switch (nb) {
+ case 0:
+ break;
+ case 1:
+ val |= 0x01; /* 1 drive, ready for boot */
+ break;
+ case 2:
+ val |= 0x41; /* 2 drives, ready for boot */
+ break;
+ }
+ val |= 0x02; /* FPU is there */
+ val |= 0x04; /* PS/2 mouse installed */
+ rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+
+ /* hard drives */
+
+ rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+ if (hd_table[0])
+ cmos_init_hd(0x19, 0x1b, hd_table[0]);
+ if (hd_table[1])
+ cmos_init_hd(0x1a, 0x24, hd_table[1]);
+
+ val = 0;
+ for (i = 0; i < 4; i++) {
+ if (hd_table[i]) {
+ int cylinders, heads, sectors, translation;
+ /* NOTE: bdrv_get_geometry_hint() returns the physical
+ geometry. It is always such that: 1 <= sects <= 63, 1
+ <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+ geometry can be different if a translation is done. */
+ translation = bdrv_get_translation_hint(hd_table[i]);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, &sectors);
+ if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+ /* No translation. */
+ translation = 0;
+ } else {
+ /* LBA translation. */
+ translation = 1;
+ }
+ } else {
+ translation--;
+ }
+ val |= translation << (i * 2);
+ }
+ }
+ rtc_set_memory(s, 0x39, val);
+}
+
+void ioport_set_a20(int enable)
+{
+ /* XXX: send to all CPUs ? */
+ cpu_x86_set_a20(first_cpu, enable);
+}
+
+int ioport_get_a20(void)
+{
+ return ((first_cpu->a20_mask >> 20) & 1);
+}
+
+static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ ioport_set_a20((val >> 1) & 1);
+ /* XXX: bit 0 is fast reset */
+}
+
+static uint32_t ioport92_read(void *opaque, uint32_t addr)
+{
+ return ioport_get_a20() << 1;
+}
+
+/***********************************************************/
+/* Bochs BIOS debug ports */
+
+void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ static const char shutdown_str[8] = "Shutdown";
+ static int shutdown_index = 0;
+
+ switch(addr) {
+ /* Bochs BIOS messages */
+ case 0x400:
+ case 0x401:
+ fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
+ exit(1);
+ case 0x402:
+ case 0x403:
+#ifdef DEBUG_BIOS
+ fprintf(stderr, "%c", val);
+#endif
+ break;
+ case 0x8900:
+ /* same as Bochs power off */
+ if (val == shutdown_str[shutdown_index]) {
+ shutdown_index++;
+ if (shutdown_index == 8) {
+ shutdown_index = 0;
+ qemu_system_shutdown_request();
+ }
+ } else {
+ shutdown_index = 0;
+ }
+ break;
+
+ /* LGPL'ed VGA BIOS messages */
+ case 0x501:
+ case 0x502:
+ fprintf(stderr, "VGA BIOS panic, line %d\n", val);
+ exit(1);
+ case 0x500:
+ case 0x503:
+#ifdef DEBUG_BIOS
+ fprintf(stderr, "%c", val);
+#endif
+ break;
+ }
+}
+
+void bochs_bios_init(void)
+{
+ register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
+
+ register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
+}
+
+
+int load_kernel(const char *filename, uint8_t *addr,
+ uint8_t *real_addr)
+{
+ int fd, size;
+ int setup_sects;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ /* load 16 bit code */
+ if (read(fd, real_addr, 512) != 512)
+ goto fail;
+ setup_sects = real_addr[0x1F1];
+ if (!setup_sects)
+ setup_sects = 4;
+ if (read(fd, real_addr + 512, setup_sects * 512) !=
+ setup_sects * 512)
+ goto fail;
+
+ /* load 32 bit code */
+ size = read(fd, addr, 16 * 1024 * 1024);
+ if (size < 0)
+ goto fail;
+ close(fd);
+ return size;
+ fail:
+ close(fd);
+ return -1;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+/*************************************************/
+
+static void putb(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *pp = q;
+}
+
+static void putstr(uint8_t **pp, const char *str)
+{
+ uint8_t *q;
+ q = *pp;
+ while (*str)
+ *q++ = *str++;
+ *pp = q;
+}
+
+static void putle16(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *q++ = val >> 8;
+ *pp = q;
+}
+
+static void putle32(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *q++ = val >> 8;
+ *q++ = val >> 16;
+ *q++ = val >> 24;
+ *pp = q;
+}
+
+static int mpf_checksum(const uint8_t *data, int len)
+{
+ int sum, i;
+ sum = 0;
+ for(i = 0; i < len; i++)
+ sum += data[i];
+ return sum & 0xff;
+}
+
+/* Build the Multi Processor table in the BIOS. Same values as Bochs. */
+static void bios_add_mptable(uint8_t *bios_data)
+{
+ uint8_t *mp_config_table, *q, *float_pointer_struct;
+ int ioapic_id, offset, i, len;
+
+ if (smp_cpus <= 1)
+ return;
+
+ mp_config_table = bios_data + 0xb000;
+ q = mp_config_table;
+ putstr(&q, "PCMP"); /* "PCMP signature */
+ putle16(&q, 0); /* table length (patched later) */
+ putb(&q, 4); /* spec rev */
+ putb(&q, 0); /* checksum (patched later) */
+ putstr(&q, "QEMUCPU "); /* OEM id */
+ putstr(&q, "0.1 "); /* vendor id */
+ putle32(&q, 0); /* OEM table ptr */
+ putle16(&q, 0); /* OEM table size */
+ putle16(&q, 20); /* entry count */
+ putle32(&q, 0xfee00000); /* local APIC addr */
+ putle16(&q, 0); /* ext table length */
+ putb(&q, 0); /* ext table checksum */
+ putb(&q, 0); /* reserved */
+
+ for(i = 0; i < smp_cpus; i++) {
+ putb(&q, 0); /* entry type = processor */
+ putb(&q, i); /* APIC id */
+ putb(&q, 0x11); /* local APIC version number */
+ if (i == 0)
+ putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
+ else
+ putb(&q, 1); /* cpu flags: enabled */
+ putb(&q, 0); /* cpu signature */
+ putb(&q, 6);
+ putb(&q, 0);
+ putb(&q, 0);
+ putle16(&q, 0x201); /* feature flags */
+ putle16(&q, 0);
+
+ putle16(&q, 0); /* reserved */
+ putle16(&q, 0);
+ putle16(&q, 0);
+ putle16(&q, 0);
+ }
+
+ /* isa bus */
+ putb(&q, 1); /* entry type = bus */
+ putb(&q, 0); /* bus ID */
+ putstr(&q, "ISA ");
+
+ /* ioapic */
+ ioapic_id = smp_cpus;
+ putb(&q, 2); /* entry type = I/O APIC */
+ putb(&q, ioapic_id); /* apic ID */
+ putb(&q, 0x11); /* I/O APIC version number */
+ putb(&q, 1); /* enable */
+ putle32(&q, 0xfec00000); /* I/O APIC addr */
+
+ /* irqs */
+ for(i = 0; i < 16; i++) {
+ putb(&q, 3); /* entry type = I/O interrupt */
+ putb(&q, 0); /* interrupt type = vectored interrupt */
+ putb(&q, 0); /* flags: po=0, el=0 */
+ putb(&q, 0);
+ putb(&q, 0); /* source bus ID = ISA */
+ putb(&q, i); /* source bus IRQ */
+ putb(&q, ioapic_id); /* dest I/O APIC ID */
+ putb(&q, i); /* dest I/O APIC interrupt in */
+ }
+ /* patch length */
+ len = q - mp_config_table;
+ mp_config_table[4] = len;
+ mp_config_table[5] = len >> 8;
+
+ mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table);
+
+ /* align to 16 */
+ offset = q - bios_data;
+ offset = (offset + 15) & ~15;
+ float_pointer_struct = bios_data + offset;
+
+ /* floating pointer structure */
+ q = float_pointer_struct;
+ putstr(&q, "_MP_");
+ /* pointer to MP config table */
+ putle32(&q, mp_config_table - bios_data + 0x000f0000);
+
+ putb(&q, 1); /* length in 16 byte units */
+ putb(&q, 4); /* MP spec revision */
+ putb(&q, 0); /* checksum (patched later) */
+ putb(&q, 0); /* MP feature byte 1 */
+
+ putb(&q, 0);
+ putb(&q, 0);
+ putb(&q, 0);
+ putb(&q, 0);
+ float_pointer_struct[10] =
+ -mpf_checksum(float_pointer_struct, q - float_pointer_struct);
+}
+
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+#define NE2000_NB_MAX 6
+
+static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+#ifdef HAS_AUDIO
+static void audio_init (PCIBus *pci_bus)
+{
+ struct soundhw *c;
+ int audio_enabled = 0;
+
+ for (c = soundhw; !audio_enabled && c->name; ++c) {
+ audio_enabled = c->enabled;
+ }
+
+ if (audio_enabled) {
+ AudioState *s;
+
+ s = AUD_init ();
+ if (s) {
+ for (c = soundhw; c->name; ++c) {
+ if (c->enabled) {
+ if (c->isa) {
+ c->init.init_isa (s);
+ }
+ else {
+ if (pci_bus) {
+ c->init.init_pci (pci_bus, s);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+static void pc_init_ne2k_isa(NICInfo *nd)
+{
+ static int nb_ne2k = 0;
+
+ if (nb_ne2k == NE2000_NB_MAX)
+ return;
+ isa_ne2000_init(ne2000_io[nb_ne2k], ne2000_irq[nb_ne2k], nd);
+ nb_ne2k++;
+}
+
+/* PC hardware initialisation */
+static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename,
+ int pci_enabled)
+{
+ char buf[1024];
+ int ret, linux_boot, initrd_size, i;
+ unsigned long bios_offset, vga_bios_offset;
+ int bios_size, isa_bios_size;
+ PCIBus *pci_bus;
+ int piix3_devfn = -1;
+ CPUState *env;
+ NICInfo *nd;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ for(i = 0; i < smp_cpus; i++) {
+ env = cpu_init();
+ if (i != 0)
+ env->hflags |= HF_HALTED_MASK;
+ if (smp_cpus > 1) {
+ /* XXX: enable it in all cases */
+ env->cpuid_features |= CPUID_APIC;
+ }
+ register_savevm("cpu", i, 3, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+ if (pci_enabled) {
+ apic_init(env);
+ }
+ }
+
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, 0);
+
+ /* BIOS load */
+ bios_offset = ram_size + vga_ram_size;
+ vga_bios_offset = bios_offset + 256 * 1024;
+
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
+ bios_size = get_image_size(buf);
+ if (bios_size <= 0 ||
+ (bios_size % 65536) != 0 ||
+ bios_size > (256 * 1024)) {
+ goto bios_error;
+ }
+ ret = load_image(buf, phys_ram_base + bios_offset);
+ if (ret != bios_size) {
+ bios_error:
+ fprintf(stderr, "qemu: could not load PC bios '%s'\n", buf);
+ exit(1);
+ }
+ if (bios_size == 65536) {
+ bios_add_mptable(phys_ram_base + bios_offset);
+ }
+
+ /* VGA BIOS load */
+ if (cirrus_vga_enabled) {
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME);
+ } else {
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);
+ }
+ ret = load_image(buf, phys_ram_base + vga_bios_offset);
+
+ /* setup basic memory access */
+ cpu_register_physical_memory(0xc0000, 0x10000,
+ vga_bios_offset | IO_MEM_ROM);
+
+ /* map the last 128KB of the BIOS in ISA space */
+ isa_bios_size = bios_size;
+ if (isa_bios_size > (128 * 1024))
+ isa_bios_size = 128 * 1024;
+ cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size,
+ IO_MEM_UNASSIGNED);
+ cpu_register_physical_memory(0x100000 - isa_bios_size,
+ isa_bios_size,
+ (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+ /* map all the bios at the top of memory */
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+
+ bochs_bios_init();
+
+ if (linux_boot) {
+ uint8_t bootsect[512];
+ uint8_t old_bootsect[512];
+
+ if (bs_table[0] == NULL) {
+ fprintf(stderr, "A disk image must be given for 'hda' when booting a Linux kernel\n");
+ exit(1);
+ }
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, LINUX_BOOT_FILENAME);
+ ret = load_image(buf, bootsect);
+ if (ret != sizeof(bootsect)) {
+ fprintf(stderr, "qemu: could not load linux boot sector '%s'\n",
+ buf);
+ exit(1);
+ }
+
+ if (bdrv_read(bs_table[0], 0, old_bootsect, 1) >= 0) {
+ /* copy the MSDOS partition table */
+ memcpy(bootsect + 0x1be, old_bootsect + 0x1be, 0x40);
+ }
+
+ bdrv_set_boot_sector(bs_table[0], bootsect, sizeof(bootsect));
+
+ /* now we can load the kernel */
+ ret = load_kernel(kernel_filename,
+ phys_ram_base + KERNEL_LOAD_ADDR,
+ phys_ram_base + KERNEL_PARAMS_ADDR);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ if (initrd_filename) {
+ initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+ if (initrd_size > 0) {
+ stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x218, INITRD_LOAD_ADDR);
+ stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x21c, initrd_size);
+ }
+ pstrcpy(phys_ram_base + KERNEL_CMDLINE_ADDR, 4096,
+ kernel_cmdline);
+ stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x20, 0xA33F);
+ stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x22,
+ KERNEL_CMDLINE_ADDR - KERNEL_PARAMS_ADDR);
+ /* loader type */
+ stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x210, 0x01);
+ }
+
+ if (pci_enabled) {
+ pci_bus = i440fx_init();
+ piix3_devfn = piix3_init(pci_bus);
+ } else {
+ pci_bus = NULL;
+ }
+
+ /* init basic PC hardware */
+ register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+
+ register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+ if (cirrus_vga_enabled) {
+ if (pci_enabled) {
+ pci_cirrus_vga_init(pci_bus,
+ ds, phys_ram_base + ram_size, ram_size,
+ vga_ram_size);
+ } else {
+ isa_cirrus_vga_init(ds, phys_ram_base + ram_size, ram_size,
+ vga_ram_size);
+ }
+ } else {
+ vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size,
+ vga_ram_size, 0, 0);
+ }
+
+ rtc_state = rtc_init(0x70, 8);
+
+ register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
+ register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+
+ if (pci_enabled) {
+ ioapic = ioapic_init();
+ }
+ isa_pic = pic_init(pic_irq_request, first_cpu);
+ pit = pit_init(0x40, 0);
+ pcspk_init(pit);
+ if (pci_enabled) {
+ pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic);
+ }
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_init(&pic_set_irq_new, isa_pic,
+ serial_io[i], serial_irq[i], serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]);
+ }
+ }
+
+ for(i = 0; i < nb_nics; i++) {
+ nd = &nd_table[i];
+ if (!nd->model) {
+ if (pci_enabled) {
+ nd->model = "ne2k_pci";
+ } else {
+ nd->model = "ne2k_isa";
+ }
+ }
+ if (strcmp(nd->model, "ne2k_isa") == 0) {
+ pc_init_ne2k_isa(nd);
+ } else if (pci_enabled) {
+ pci_nic_init(pci_bus, nd);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit(1);
+ }
+ }
+
+ if (pci_enabled) {
+ pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1);
+ } else {
+ for(i = 0; i < 2; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ bs_table[2 * i], bs_table[2 * i + 1]);
+ }
+ }
+
+ kbd_init();
+ DMA_init(0);
+#ifdef HAS_AUDIO
+ audio_init(pci_enabled ? pci_bus : NULL);
+#endif
+
+ floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
+
+ cmos_init(ram_size, boot_device, bs_table);
+
+ if (pci_enabled && usb_enabled) {
+ usb_uhci_init(pci_bus, piix3_devfn + 2);
+ }
+
+ if (pci_enabled && acpi_enabled) {
+ piix4_pm_init(pci_bus, piix3_devfn + 3);
+ }
+
+#if 0
+ /* ??? Need to figure out some way for the user to
+ specify SCSI devices. */
+ if (pci_enabled) {
+ void *scsi;
+ BlockDriverState *bdrv;
+
+ scsi = lsi_scsi_init(pci_bus, -1);
+ bdrv = bdrv_new("scsidisk");
+ bdrv_open(bdrv, "scsi_disk.img", 0);
+ lsi_scsi_attach(scsi, bdrv, -1);
+ bdrv = bdrv_new("scsicd");
+ bdrv_open(bdrv, "scsi_cd.iso", 0);
+ bdrv_set_type_hint(bdrv, BDRV_TYPE_CDROM);
+ lsi_scsi_attach(scsi, bdrv, -1);
+ }
+#endif
+ /* must be done after all PCI devices are instanciated */
+ /* XXX: should be done in the Bochs BIOS */
+ if (pci_enabled) {
+ pci_bios_init();
+ if (acpi_enabled)
+ acpi_bios_init();
+ }
+}
+
+static void pc_init_pci(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ pc_init1(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 1);
+}
+
+static void pc_init_isa(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ pc_init1(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 0);
+}
+
+QEMUMachine pc_machine = {
+ "pc",
+ "Standard PC",
+ pc_init_pci,
+};
+
+QEMUMachine isapc_machine = {
+ "isapc",
+ "ISA-only PC",
+ pc_init_isa,
+};
diff --git a/hw/pci.c b/hw/pci.c
new file mode 100644
index 0000000..f58f6fd
--- /dev/null
+++ b/hw/pci.c
@@ -0,0 +1,503 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_PCI
+
+struct PCIBus {
+ int bus_num;
+ int devfn_min;
+ pci_set_irq_fn set_irq;
+ uint32_t config_reg; /* XXX: suppress */
+ /* low level pic */
+ SetIRQFunc *low_set_irq;
+ void *irq_opaque;
+ PCIDevice *devices[256];
+};
+
+target_phys_addr_t pci_mem_base;
+static int pci_irq_index;
+static PCIBus *first_bus;
+
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, void *pic, int devfn_min)
+{
+ PCIBus *bus;
+ bus = qemu_mallocz(sizeof(PCIBus));
+ bus->set_irq = set_irq;
+ bus->irq_opaque = pic;
+ bus->devfn_min = devfn_min;
+ first_bus = bus;
+ return bus;
+}
+
+int pci_bus_num(PCIBus *s)
+{
+ return s->bus_num;
+}
+
+void generic_pci_save(QEMUFile* f, void *opaque)
+{
+ PCIDevice* s=(PCIDevice*)opaque;
+
+ qemu_put_buffer(f, s->config, 256);
+}
+
+int generic_pci_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIDevice* s=(PCIDevice*)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_buffer(f, s->config, 256);
+ return 0;
+}
+
+/* -1 for devfn means auto assign */
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write)
+{
+ PCIDevice *pci_dev;
+
+ if (pci_irq_index >= PCI_DEVICES_MAX)
+ return NULL;
+
+ if (devfn < 0) {
+ for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
+ if (!bus->devices[devfn])
+ goto found;
+ }
+ return NULL;
+ found: ;
+ }
+ pci_dev = qemu_mallocz(instance_size);
+ if (!pci_dev)
+ return NULL;
+ pci_dev->bus = bus;
+ pci_dev->devfn = devfn;
+ pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
+
+ if (!config_read)
+ config_read = pci_default_read_config;
+ if (!config_write)
+ config_write = pci_default_write_config;
+ pci_dev->config_read = config_read;
+ pci_dev->config_write = config_write;
+ pci_dev->irq_index = pci_irq_index++;
+ bus->devices[devfn] = pci_dev;
+ return pci_dev;
+}
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+ uint32_t size, int type,
+ PCIMapIORegionFunc *map_func)
+{
+ PCIIORegion *r;
+ uint32_t addr;
+
+ if ((unsigned int)region_num >= PCI_NUM_REGIONS)
+ return;
+ r = &pci_dev->io_regions[region_num];
+ r->addr = -1;
+ r->size = size;
+ r->type = type;
+ r->map_func = map_func;
+ if (region_num == PCI_ROM_SLOT) {
+ addr = 0x30;
+ } else {
+ addr = 0x10 + region_num * 4;
+ }
+ *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
+}
+
+target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+{
+ return addr + pci_mem_base;
+}
+
+static void pci_update_mappings(PCIDevice *d)
+{
+ PCIIORegion *r;
+ int cmd, i;
+ uint32_t last_addr, new_addr, config_ofs;
+
+ cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND));
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (i == PCI_ROM_SLOT) {
+ config_ofs = 0x30;
+ } else {
+ config_ofs = 0x10 + i * 4;
+ }
+ if (r->size != 0) {
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ if (cmd & PCI_COMMAND_IO) {
+ new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+ config_ofs));
+ new_addr = new_addr & ~(r->size - 1);
+ last_addr = new_addr + r->size - 1;
+ /* NOTE: we have only 64K ioports on PC */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr >= 0x10000) {
+ new_addr = -1;
+ }
+ } else {
+ new_addr = -1;
+ }
+ } else {
+ if (cmd & PCI_COMMAND_MEMORY) {
+ new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+ config_ofs));
+ /* the ROM slot has a specific enable bit */
+ if (i == PCI_ROM_SLOT && !(new_addr & 1))
+ goto no_mem_map;
+ new_addr = new_addr & ~(r->size - 1);
+ last_addr = new_addr + r->size - 1;
+ /* NOTE: we do not support wrapping */
+ /* XXX: as we cannot support really dynamic
+ mappings, we handle specific values as invalid
+ mappings. */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr == -1) {
+ new_addr = -1;
+ }
+ } else {
+ no_mem_map:
+ new_addr = -1;
+ }
+ }
+ /* now do the real mapping */
+ if (new_addr != r->addr) {
+ if (r->addr != -1) {
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ int class;
+ /* NOTE: specific hack for IDE in PC case:
+ only one byte must be mapped. */
+ class = d->config[0x0a] | (d->config[0x0b] << 8);
+ if (class == 0x0101 && r->size == 4) {
+ isa_unassign_ioport(r->addr + 2, 1);
+ } else {
+ isa_unassign_ioport(r->addr, r->size);
+ }
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
+ r->size,
+ IO_MEM_UNASSIGNED);
+ }
+ }
+ r->addr = new_addr;
+ if (r->addr != -1) {
+ r->map_func(d, i, r->addr, r->size, r->type);
+ }
+ }
+ }
+ }
+}
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len)
+{
+ uint32_t val;
+ switch(len) {
+ case 1:
+ val = d->config[address];
+ break;
+ case 2:
+ val = le16_to_cpu(*(uint16_t *)(d->config + address));
+ break;
+ default:
+ case 4:
+ val = le32_to_cpu(*(uint32_t *)(d->config + address));
+ break;
+ }
+ return val;
+}
+
+void pci_default_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ int can_write, i;
+ uint32_t end, addr;
+
+ if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) ||
+ (address >= 0x30 && address < 0x34))) {
+ PCIIORegion *r;
+ int reg;
+
+ if ( address >= 0x30 ) {
+ reg = PCI_ROM_SLOT;
+ }else{
+ reg = (address - 0x10) >> 2;
+ }
+ r = &d->io_regions[reg];
+ if (r->size == 0)
+ goto default_config;
+ /* compute the stored value */
+ if (reg == PCI_ROM_SLOT) {
+ /* keep ROM enable bit */
+ val &= (~(r->size - 1)) | 1;
+ } else {
+ val &= ~(r->size - 1);
+ val |= r->type;
+ }
+ *(uint32_t *)(d->config + address) = cpu_to_le32(val);
+ pci_update_mappings(d);
+ return;
+ }
+ default_config:
+ /* not efficient, but simple */
+ addr = address;
+ for(i = 0; i < len; i++) {
+ /* default read/write accesses */
+ switch(d->config[0x0e]) {
+ case 0x00:
+ case 0x80:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x10 ... 0x27: /* base */
+ case 0x30 ... 0x33: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ default:
+ case 0x01:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x38 ... 0x3b: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ }
+ if (can_write) {
+ d->config[addr] = val;
+ }
+ addr++;
+ val >>= 8;
+ }
+
+ end = address + len;
+ if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) {
+ /* if the command register is modified, we must modify the mappings */
+ pci_update_mappings(d);
+ }
+}
+
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ int config_addr, bus_num;
+
+#if defined(DEBUG_PCI) && 0
+ printf("pci_data_write: addr=%08x val=%08x len=%d\n",
+ addr, val, len);
+#endif
+ bus_num = (addr >> 16) & 0xff;
+ if (bus_num != 0)
+ return;
+ pci_dev = s->devices[(addr >> 8) & 0xff];
+ if (!pci_dev)
+ return;
+ config_addr = addr & 0xff;
+#if defined(DEBUG_PCI)
+ printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n",
+ pci_dev->name, config_addr, val, len);
+#endif
+ pci_dev->config_write(pci_dev, config_addr, val, len);
+}
+
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ int config_addr, bus_num;
+ uint32_t val;
+
+ bus_num = (addr >> 16) & 0xff;
+ if (bus_num != 0)
+ goto fail;
+ pci_dev = s->devices[(addr >> 8) & 0xff];
+ if (!pci_dev) {
+ fail:
+ switch(len) {
+ case 1:
+ val = 0xff;
+ break;
+ case 2:
+ val = 0xffff;
+ break;
+ default:
+ case 4:
+ val = 0xffffffff;
+ break;
+ }
+ goto the_end;
+ }
+ config_addr = addr & 0xff;
+ val = pci_dev->config_read(pci_dev, config_addr, len);
+#if defined(DEBUG_PCI)
+ printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n",
+ pci_dev->name, config_addr, val, len);
+#endif
+ the_end:
+#if defined(DEBUG_PCI) && 0
+ printf("pci_data_read: addr=%08x val=%08x len=%d\n",
+ addr, val, len);
+#endif
+ return val;
+}
+
+/***********************************************************/
+/* generic PCI irq support */
+
+/* 0 <= irq_num <= 3. level must be 0 or 1 */
+void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level)
+{
+ PCIBus *bus = pci_dev->bus;
+ bus->set_irq(pci_dev, bus->irq_opaque, irq_num, level);
+}
+
+/***********************************************************/
+/* monitor info on PCI */
+
+typedef struct {
+ uint16_t class;
+ const char *desc;
+} pci_class_desc;
+
+static pci_class_desc pci_class_descriptions[] =
+{
+ { 0x0101, "IDE controller"},
+ { 0x0200, "Ethernet controller"},
+ { 0x0300, "VGA controller"},
+ { 0x0600, "Host bridge"},
+ { 0x0601, "ISA bridge"},
+ { 0x0604, "PCI bridge"},
+ { 0x0c03, "USB controller"},
+ { 0, NULL}
+};
+
+static void pci_info_device(PCIDevice *d)
+{
+ int i, class;
+ PCIIORegion *r;
+ pci_class_desc *desc;
+
+ term_printf(" Bus %2d, device %3d, function %d:\n",
+ d->bus->bus_num, d->devfn >> 3, d->devfn & 7);
+ class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE)));
+ term_printf(" ");
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class)
+ desc++;
+ if (desc->desc) {
+ term_printf("%s", desc->desc);
+ } else {
+ term_printf("Class %04x", class);
+ }
+ term_printf(": PCI device %04x:%04x\n",
+ le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))),
+ le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID))));
+
+ if (d->config[PCI_INTERRUPT_PIN] != 0) {
+ term_printf(" IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]);
+ }
+ for(i = 0;i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (r->size != 0) {
+ term_printf(" BAR%d: ", i);
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ term_printf("I/O at 0x%04x [0x%04x].\n",
+ r->addr, r->addr + r->size - 1);
+ } else {
+ term_printf("32 bit memory at 0x%08x [0x%08x].\n",
+ r->addr, r->addr + r->size - 1);
+ }
+ }
+ }
+}
+
+void pci_for_each_device(void (*fn)(PCIDevice *d))
+{
+ PCIBus *bus = first_bus;
+ PCIDevice *d;
+ int devfn;
+
+ if (bus) {
+ for(devfn = 0; devfn < 256; devfn++) {
+ d = bus->devices[devfn];
+ if (d)
+ fn(d);
+ }
+ }
+}
+
+void pci_info(void)
+{
+ pci_for_each_device(pci_info_device);
+}
+
+/* Initialize a PCI NIC. */
+void pci_nic_init(PCIBus *bus, NICInfo *nd)
+{
+ if (strcmp(nd->model, "ne2k_pci") == 0) {
+ pci_ne2000_init(bus, nd);
+ } else if (strcmp(nd->model, "rtl8139") == 0) {
+ pci_rtl8139_init(bus, nd);
+ } else if (strcmp(nd->model, "pcnet") == 0) {
+ pci_pcnet_init(bus, nd);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit (1);
+ }
+}
+
diff --git a/hw/pci_host.h b/hw/pci_host.h
new file mode 100644
index 0000000..708dae2
--- /dev/null
+++ b/hw/pci_host.h
@@ -0,0 +1,93 @@
+/*
+ * QEMU Common PCI Host bridge configuration data space access routines.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Worker routines for a PCI host controller that uses an {address,data}
+ register pair to access PCI configuration space. */
+
+typedef struct {
+ uint32_t config_reg;
+ PCIBus *bus;
+} PCIHostState;
+
+static void pci_host_data_writeb(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1);
+}
+
+static void pci_host_data_writew(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2);
+}
+
+static void pci_host_data_writel(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg, val, 4);
+}
+
+static uint32_t pci_host_data_readb(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xff;
+ return pci_data_read(s->bus, s->config_reg | (addr & 3), 1);
+}
+
+static uint32_t pci_host_data_readw(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ uint32_t val;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xffff;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t pci_host_data_readl(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ uint32_t val;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xffffffff;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
diff --git a/hw/pckbd.c b/hw/pckbd.c
new file mode 100644
index 0000000..3c41e5f
--- /dev/null
+++ b/hw/pckbd.c
@@ -0,0 +1,370 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
+#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
+#define KBD_CCMD_WRITE_OBUF 0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
+ initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
+#define KBD_CCMD_RESET 0xFE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_POLL 0xEB /* Poll */
+#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
+#define AUX_SET_WRAP 0xEE /* Set wrap mode */
+#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
+#define AUX_GET_TYPE 0xF2 /* Get type */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_SET_DEFAULT 0xF6
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE 0x40
+#define MOUSE_STATUS_ENABLED 0x20
+#define MOUSE_STATUS_SCALE21 0x10
+
+#define KBD_QUEUE_SIZE 256
+
+#define KBD_PENDING_KBD 1
+#define KBD_PENDING_AUX 2
+
+typedef struct KBDState {
+ uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+ uint8_t status;
+ uint8_t mode;
+ /* Bitmask of devices with data available. */
+ uint8_t pending;
+ void *kbd;
+ void *mouse;
+} KBDState;
+
+KBDState kbd_state;
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+ incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+ int irq12_level, irq1_level;
+
+ irq1_level = 0;
+ irq12_level = 0;
+ s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+ if (s->pending) {
+ s->status |= KBD_STAT_OBF;
+ /* kdb data takes priority over aux data. */
+ if (s->pending == KBD_PENDING_AUX) {
+ s->status |= KBD_STAT_MOUSE_OBF;
+ if (s->mode & KBD_MODE_MOUSE_INT)
+ irq12_level = 1;
+ } else {
+ if ((s->mode & KBD_MODE_KBD_INT) &&
+ !(s->mode & KBD_MODE_DISABLE_KBD))
+ irq1_level = 1;
+ }
+ }
+ pic_set_irq(1, irq1_level);
+ pic_set_irq(12, irq12_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_KBD;
+ else
+ s->pending &= ~KBD_PENDING_KBD;
+ kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_AUX;
+ else
+ s->pending &= ~KBD_PENDING_AUX;
+ kbd_update_irq(s);
+}
+
+static uint32_t kbd_read_status(void *opaque, uint32_t addr)
+{
+ KBDState *s = opaque;
+ int val;
+ val = s->status;
+#if defined(DEBUG_KBD)
+ printf("kbd: read status=0x%02x\n", val);
+#endif
+ return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+ if (aux)
+ ps2_queue(s->mouse, b);
+ else
+ ps2_queue(s->kbd, b);
+}
+
+static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
+{
+ KBDState *s = opaque;
+
+#ifdef DEBUG_KBD
+ printf("kbd: write cmd=0x%02x\n", val);
+#endif
+ switch(val) {
+ case KBD_CCMD_READ_MODE:
+ kbd_queue(s, s->mode, 0);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ case KBD_CCMD_WRITE_OBUF:
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ case KBD_CCMD_WRITE_MOUSE:
+ case KBD_CCMD_WRITE_OUTPORT:
+ s->write_cmd = val;
+ break;
+ case KBD_CCMD_MOUSE_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_MOUSE_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_TEST_MOUSE:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_SELF_TEST:
+ s->status |= KBD_STAT_SELFTEST;
+ kbd_queue(s, 0x55, 0);
+ break;
+ case KBD_CCMD_KBD_TEST:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_KBD_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_KBD_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_READ_INPORT:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_READ_OUTPORT:
+ /* XXX: check that */
+#ifdef TARGET_I386
+ val = 0x01 | (ioport_get_a20() << 1);
+#else
+ val = 0x01;
+#endif
+ if (s->status & KBD_STAT_OBF)
+ val |= 0x10;
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ val |= 0x20;
+ kbd_queue(s, val, 0);
+ break;
+#ifdef TARGET_I386
+ case KBD_CCMD_ENABLE_A20:
+ ioport_set_a20(1);
+ break;
+ case KBD_CCMD_DISABLE_A20:
+ ioport_set_a20(0);
+ break;
+#endif
+ case KBD_CCMD_RESET:
+ qemu_system_reset_request();
+ break;
+ case 0xff:
+ /* ignore that - I don't know what is its use */
+ break;
+ default:
+ fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
+ break;
+ }
+}
+
+static uint32_t kbd_read_data(void *opaque, uint32_t addr)
+{
+ KBDState *s = opaque;
+
+ if (s->pending == KBD_PENDING_AUX)
+ return ps2_read_data(s->mouse);
+
+ return ps2_read_data(s->kbd);
+}
+
+void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+ KBDState *s = opaque;
+
+#ifdef DEBUG_KBD
+ printf("kbd: write data=0x%02x\n", val);
+#endif
+
+ switch(s->write_cmd) {
+ case 0:
+ ps2_write_keyboard(s->kbd, val);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ s->mode = val;
+ ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+ /* ??? */
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_WRITE_OBUF:
+ kbd_queue(s, val, 0);
+ break;
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ kbd_queue(s, val, 1);
+ break;
+ case KBD_CCMD_WRITE_OUTPORT:
+#ifdef TARGET_I386
+ ioport_set_a20((val >> 1) & 1);
+#endif
+ if (!(val & 1)) {
+ qemu_system_reset_request();
+ }
+ break;
+ case KBD_CCMD_WRITE_MOUSE:
+ ps2_write_mouse(s->mouse, val);
+ break;
+ default:
+ break;
+ }
+ s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+ KBDState *s = opaque;
+
+ s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+ s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+}
+
+static void kbd_save(QEMUFile* f, void* opaque)
+{
+ KBDState *s = (KBDState*)opaque;
+
+ qemu_put_8s(f, &s->write_cmd);
+ qemu_put_8s(f, &s->status);
+ qemu_put_8s(f, &s->mode);
+ qemu_put_8s(f, &s->pending);
+}
+
+static int kbd_load(QEMUFile* f, void* opaque, int version_id)
+{
+ KBDState *s = (KBDState*)opaque;
+
+ if (version_id != 3)
+ return -EINVAL;
+ qemu_get_8s(f, &s->write_cmd);
+ qemu_get_8s(f, &s->status);
+ qemu_get_8s(f, &s->mode);
+ qemu_get_8s(f, &s->pending);
+ return 0;
+}
+
+void kbd_init(void)
+{
+ KBDState *s = &kbd_state;
+
+ kbd_reset(s);
+ register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s);
+ register_ioport_read(0x60, 1, 1, kbd_read_data, s);
+ register_ioport_write(0x60, 1, 1, kbd_write_data, s);
+ register_ioport_read(0x64, 1, 1, kbd_read_status, s);
+ register_ioport_write(0x64, 1, 1, kbd_write_command, s);
+
+ s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+ s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+ qemu_register_reset(kbd_reset, s);
+}
diff --git a/hw/pcnet.c b/hw/pcnet.c
new file mode 100644
index 0000000..0845cdc
--- /dev/null
+++ b/hw/pcnet.c
@@ -0,0 +1,1789 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ */
+
+#include "vl.h"
+
+//#define PCNET_DEBUG
+//#define PCNET_DEBUG_IO
+//#define PCNET_DEBUG_BCR
+//#define PCNET_DEBUG_CSR
+//#define PCNET_DEBUG_RMD
+//#define PCNET_DEBUG_TMD
+//#define PCNET_DEBUG_MATCH
+
+
+#define PCNET_IOPORT_SIZE 0x20
+#define PCNET_PNPMMIO_SIZE 0x20
+
+
+typedef struct PCNetState_st PCNetState;
+
+struct PCNetState_st {
+ PCIDevice dev;
+ VLANClientState *vc;
+ NICInfo *nd;
+ QEMUTimer *poll_timer;
+ int mmio_io_addr, rap, isr, lnkst;
+ target_phys_addr_t rdra, tdra;
+ uint8_t prom[16];
+ uint16_t csr[128];
+ uint16_t bcr[32];
+ uint64_t timer;
+ int xmit_pos, recv_pos;
+ uint8_t buffer[4096];
+ int tx_busy;
+};
+
+/* XXX: using bitfields for target memory structures is almost surely
+ not portable, so it should be suppressed ASAP */
+#ifdef __GNUC__
+#define PACKED_FIELD(A) A __attribute__ ((packed))
+#else
+#error FixMe
+#endif
+
+struct qemu_ether_header {
+ uint8_t ether_dhost[6];
+ uint8_t ether_shost[6];
+ uint16_t ether_type;
+};
+
+/* BUS CONFIGURATION REGISTERS */
+#define BCR_MSRDA 0
+#define BCR_MSWRA 1
+#define BCR_MC 2
+#define BCR_LNKST 4
+#define BCR_LED1 5
+#define BCR_LED2 6
+#define BCR_LED3 7
+#define BCR_FDC 9
+#define BCR_BSBC 18
+#define BCR_EECAS 19
+#define BCR_SWS 20
+#define BCR_PLAT 22
+
+#define BCR_DWIO(S) !!((S)->bcr[BCR_BSBC] & 0x0080)
+#define BCR_SSIZE32(S) !!((S)->bcr[BCR_SWS ] & 0x0100)
+#define BCR_SWSTYLE(S) ((S)->bcr[BCR_SWS ] & 0x00FF)
+
+#define CSR_INIT(S) !!(((S)->csr[0])&0x0001)
+#define CSR_STRT(S) !!(((S)->csr[0])&0x0002)
+#define CSR_STOP(S) !!(((S)->csr[0])&0x0004)
+#define CSR_TDMD(S) !!(((S)->csr[0])&0x0008)
+#define CSR_TXON(S) !!(((S)->csr[0])&0x0010)
+#define CSR_RXON(S) !!(((S)->csr[0])&0x0020)
+#define CSR_INEA(S) !!(((S)->csr[0])&0x0040)
+#define CSR_LAPPEN(S) !!(((S)->csr[3])&0x0020)
+#define CSR_DXSUFLO(S) !!(((S)->csr[3])&0x0040)
+#define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800)
+#define CSR_DPOLL(S) !!(((S)->csr[4])&0x1000)
+#define CSR_SPND(S) !!(((S)->csr[5])&0x0001)
+#define CSR_LTINTEN(S) !!(((S)->csr[5])&0x4000)
+#define CSR_TOKINTD(S) !!(((S)->csr[5])&0x8000)
+#define CSR_DRX(S) !!(((S)->csr[15])&0x0001)
+#define CSR_DTX(S) !!(((S)->csr[15])&0x0002)
+#define CSR_LOOP(S) !!(((S)->csr[15])&0x0004)
+#define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000)
+#define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000)
+#define CSR_PROM(S) !!(((S)->csr[15])&0x8000)
+
+#define CSR_CRBC(S) ((S)->csr[40])
+#define CSR_CRST(S) ((S)->csr[41])
+#define CSR_CXBC(S) ((S)->csr[42])
+#define CSR_CXST(S) ((S)->csr[43])
+#define CSR_NRBC(S) ((S)->csr[44])
+#define CSR_NRST(S) ((S)->csr[45])
+#define CSR_POLL(S) ((S)->csr[46])
+#define CSR_PINT(S) ((S)->csr[47])
+#define CSR_RCVRC(S) ((S)->csr[72])
+#define CSR_XMTRC(S) ((S)->csr[74])
+#define CSR_RCVRL(S) ((S)->csr[76])
+#define CSR_XMTRL(S) ((S)->csr[78])
+#define CSR_MISSC(S) ((S)->csr[112])
+
+#define CSR_IADR(S) ((S)->csr[ 1] | ((S)->csr[ 2] << 16))
+#define CSR_CRBA(S) ((S)->csr[18] | ((S)->csr[19] << 16))
+#define CSR_CXBA(S) ((S)->csr[20] | ((S)->csr[21] << 16))
+#define CSR_NRBA(S) ((S)->csr[22] | ((S)->csr[23] << 16))
+#define CSR_BADR(S) ((S)->csr[24] | ((S)->csr[25] << 16))
+#define CSR_NRDA(S) ((S)->csr[26] | ((S)->csr[27] << 16))
+#define CSR_CRDA(S) ((S)->csr[28] | ((S)->csr[29] << 16))
+#define CSR_BADX(S) ((S)->csr[30] | ((S)->csr[31] << 16))
+#define CSR_NXDA(S) ((S)->csr[32] | ((S)->csr[33] << 16))
+#define CSR_CXDA(S) ((S)->csr[34] | ((S)->csr[35] << 16))
+#define CSR_NNRD(S) ((S)->csr[36] | ((S)->csr[37] << 16))
+#define CSR_NNXD(S) ((S)->csr[38] | ((S)->csr[39] << 16))
+#define CSR_PXDA(S) ((S)->csr[60] | ((S)->csr[61] << 16))
+#define CSR_NXBA(S) ((S)->csr[64] | ((S)->csr[65] << 16))
+
+#define PHYSADDR(S,A) \
+ (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(s)->csr[2])<<16))
+
+struct pcnet_initblk16 {
+ uint16_t mode;
+ uint16_t padr1;
+ uint16_t padr2;
+ uint16_t padr3;
+ uint16_t ladrf1;
+ uint16_t ladrf2;
+ uint16_t ladrf3;
+ uint16_t ladrf4;
+ unsigned PACKED_FIELD(rdra:24);
+ unsigned PACKED_FIELD(res1:5);
+ unsigned PACKED_FIELD(rlen:3);
+ unsigned PACKED_FIELD(tdra:24);
+ unsigned PACKED_FIELD(res2:5);
+ unsigned PACKED_FIELD(tlen:3);
+};
+
+struct pcnet_initblk32 {
+ uint16_t mode;
+ unsigned PACKED_FIELD(res1:4);
+ unsigned PACKED_FIELD(rlen:4);
+ unsigned PACKED_FIELD(res2:4);
+ unsigned PACKED_FIELD(tlen:4);
+ uint16_t padr1;
+ uint16_t padr2;
+ uint16_t padr3;
+ uint16_t _res;
+ uint16_t ladrf1;
+ uint16_t ladrf2;
+ uint16_t ladrf3;
+ uint16_t ladrf4;
+ uint32_t rdra;
+ uint32_t tdra;
+};
+
+struct pcnet_TMD {
+ struct {
+ unsigned tbadr:32;
+ } tmd0;
+ struct {
+ unsigned PACKED_FIELD(bcnt:12), PACKED_FIELD(ones:4), PACKED_FIELD(res:7), PACKED_FIELD(bpe:1);
+ unsigned PACKED_FIELD(enp:1), PACKED_FIELD(stp:1), PACKED_FIELD(def:1), PACKED_FIELD(one:1);
+ unsigned PACKED_FIELD(ltint:1), PACKED_FIELD(nofcs:1), PACKED_FIELD(err:1), PACKED_FIELD(own:1);
+ } tmd1;
+ struct {
+ unsigned PACKED_FIELD(trc:4), PACKED_FIELD(res:12);
+ unsigned PACKED_FIELD(tdr:10), PACKED_FIELD(rtry:1), PACKED_FIELD(lcar:1);
+ unsigned PACKED_FIELD(lcol:1), PACKED_FIELD(exdef:1), PACKED_FIELD(uflo:1), PACKED_FIELD(buff:1);
+ } tmd2;
+ struct {
+ unsigned res:32;
+ } tmd3;
+};
+
+struct pcnet_RMD {
+ struct {
+ unsigned rbadr:32;
+ } rmd0;
+ struct {
+ unsigned PACKED_FIELD(bcnt:12), PACKED_FIELD(ones:4), PACKED_FIELD(res:4);
+ unsigned PACKED_FIELD(bam:1), PACKED_FIELD(lafm:1), PACKED_FIELD(pam:1), PACKED_FIELD(bpe:1);
+ unsigned PACKED_FIELD(enp:1), PACKED_FIELD(stp:1), PACKED_FIELD(buff:1), PACKED_FIELD(crc:1);
+ unsigned PACKED_FIELD(oflo:1), PACKED_FIELD(fram:1), PACKED_FIELD(err:1), PACKED_FIELD(own:1);
+ } rmd1;
+ struct {
+ unsigned PACKED_FIELD(mcnt:12), PACKED_FIELD(zeros:4);
+ unsigned PACKED_FIELD(rpc:8), PACKED_FIELD(rcc:8);
+ } rmd2;
+ struct {
+ unsigned res:32;
+ } rmd3;
+};
+
+
+#define PRINT_TMD(T) printf( \
+ "TMD0 : TBADR=0x%08x\n" \
+ "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
+ "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
+ " BPE=%d, BCNT=%d\n" \
+ "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
+ "LCA=%d, RTR=%d,\n" \
+ " TDR=%d, TRC=%d\n", \
+ (T)->tmd0.tbadr, \
+ (T)->tmd1.own, (T)->tmd1.err, (T)->tmd1.nofcs, \
+ (T)->tmd1.ltint, (T)->tmd1.one, (T)->tmd1.def, \
+ (T)->tmd1.stp, (T)->tmd1.enp, (T)->tmd1.bpe, \
+ 4096-(T)->tmd1.bcnt, \
+ (T)->tmd2.buff, (T)->tmd2.uflo, (T)->tmd2.exdef,\
+ (T)->tmd2.lcol, (T)->tmd2.lcar, (T)->tmd2.rtry, \
+ (T)->tmd2.tdr, (T)->tmd2.trc)
+
+#define PRINT_RMD(R) printf( \
+ "RMD0 : RBADR=0x%08x\n" \
+ "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
+ "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
+ "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
+ "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
+ (R)->rmd0.rbadr, \
+ (R)->rmd1.own, (R)->rmd1.err, (R)->rmd1.fram, \
+ (R)->rmd1.oflo, (R)->rmd1.crc, (R)->rmd1.buff, \
+ (R)->rmd1.stp, (R)->rmd1.enp, (R)->rmd1.bpe, \
+ (R)->rmd1.pam, (R)->rmd1.lafm, (R)->rmd1.bam, \
+ (R)->rmd1.ones, 4096-(R)->rmd1.bcnt, \
+ (R)->rmd2.rcc, (R)->rmd2.rpc, (R)->rmd2.mcnt, \
+ (R)->rmd2.zeros)
+
+static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd, target_phys_addr_t addr)
+{
+ if (!BCR_SWSTYLE(s)) {
+ uint16_t xda[4];
+ cpu_physical_memory_read(addr,
+ (void *)&xda[0], sizeof(xda));
+ ((uint32_t *)tmd)[0] = (xda[0]&0xffff) |
+ ((xda[1]&0x00ff) << 16);
+ ((uint32_t *)tmd)[1] = (xda[2]&0xffff)|
+ ((xda[1] & 0xff00) << 16);
+ ((uint32_t *)tmd)[2] =
+ (xda[3] & 0xffff) << 16;
+ ((uint32_t *)tmd)[3] = 0;
+ }
+ else
+ if (BCR_SWSTYLE(s) != 3)
+ cpu_physical_memory_read(addr, (void *)tmd, 16);
+ else {
+ uint32_t xda[4];
+ cpu_physical_memory_read(addr,
+ (void *)&xda[0], sizeof(xda));
+ ((uint32_t *)tmd)[0] = xda[2];
+ ((uint32_t *)tmd)[1] = xda[1];
+ ((uint32_t *)tmd)[2] = xda[0];
+ ((uint32_t *)tmd)[3] = xda[3];
+ }
+}
+
+static inline void pcnet_tmd_store(PCNetState *s, struct pcnet_TMD *tmd, target_phys_addr_t addr)
+{
+ if (!BCR_SWSTYLE(s)) {
+ uint16_t xda[4];
+ xda[0] = ((uint32_t *)tmd)[0] & 0xffff;
+ xda[1] = ((((uint32_t *)tmd)[0]>>16)&0x00ff) |
+ ((((uint32_t *)tmd)[1]>>16)&0xff00);
+ xda[2] = ((uint32_t *)tmd)[1] & 0xffff;
+ xda[3] = ((uint32_t *)tmd)[2] >> 16;
+ cpu_physical_memory_write(addr,
+ (void *)&xda[0], sizeof(xda));
+ }
+ else {
+ if (BCR_SWSTYLE(s) != 3)
+ cpu_physical_memory_write(addr, (void *)tmd, 16);
+ else {
+ uint32_t xda[4];
+ xda[0] = ((uint32_t *)tmd)[2];
+ xda[1] = ((uint32_t *)tmd)[1];
+ xda[2] = ((uint32_t *)tmd)[0];
+ xda[3] = ((uint32_t *)tmd)[3];
+ cpu_physical_memory_write(addr,
+ (void *)&xda[0], sizeof(xda));
+ }
+ }
+}
+
+static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd, target_phys_addr_t addr)
+{
+ if (!BCR_SWSTYLE(s)) {
+ uint16_t rda[4];
+ cpu_physical_memory_read(addr,
+ (void *)&rda[0], sizeof(rda));
+ ((uint32_t *)rmd)[0] = (rda[0]&0xffff)|
+ ((rda[1] & 0x00ff) << 16);
+ ((uint32_t *)rmd)[1] = (rda[2]&0xffff)|
+ ((rda[1] & 0xff00) << 16);
+ ((uint32_t *)rmd)[2] = rda[3] & 0xffff;
+ ((uint32_t *)rmd)[3] = 0;
+ }
+ else
+ if (BCR_SWSTYLE(s) != 3)
+ cpu_physical_memory_read(addr, (void *)rmd, 16);
+ else {
+ uint32_t rda[4];
+ cpu_physical_memory_read(addr,
+ (void *)&rda[0], sizeof(rda));
+ ((uint32_t *)rmd)[0] = rda[2];
+ ((uint32_t *)rmd)[1] = rda[1];
+ ((uint32_t *)rmd)[2] = rda[0];
+ ((uint32_t *)rmd)[3] = rda[3];
+ }
+}
+
+static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd, target_phys_addr_t addr)
+{
+ if (!BCR_SWSTYLE(s)) {
+ uint16_t rda[4]; \
+ rda[0] = ((uint32_t *)rmd)[0] & 0xffff; \
+ rda[1] = ((((uint32_t *)rmd)[0]>>16)&0xff)|\
+ ((((uint32_t *)rmd)[1]>>16)&0xff00);\
+ rda[2] = ((uint32_t *)rmd)[1] & 0xffff; \
+ rda[3] = ((uint32_t *)rmd)[2] & 0xffff; \
+ cpu_physical_memory_write(addr, \
+ (void *)&rda[0], sizeof(rda)); \
+ }
+ else {
+ if (BCR_SWSTYLE(s) != 3)
+ cpu_physical_memory_write(addr, (void *)rmd, 16);
+ else {
+ uint32_t rda[4];
+ rda[0] = ((uint32_t *)rmd)[2];
+ rda[1] = ((uint32_t *)rmd)[1];
+ rda[2] = ((uint32_t *)rmd)[0];
+ rda[3] = ((uint32_t *)rmd)[3];
+ cpu_physical_memory_write(addr,
+ (void *)&rda[0], sizeof(rda));
+ }
+ }
+}
+
+
+#define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR)
+
+#define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR)
+
+#define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR)
+
+#define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR)
+
+#if 1
+
+#define CHECK_RMD(ADDR,RES) do { \
+ struct pcnet_RMD rmd; \
+ RMDLOAD(&rmd,(ADDR)); \
+ (RES) |= (rmd.rmd1.ones != 15) \
+ || (rmd.rmd2.zeros != 0); \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do { \
+ struct pcnet_TMD tmd; \
+ TMDLOAD(&tmd,(ADDR)); \
+ (RES) |= (tmd.tmd1.ones != 15); \
+} while (0)
+
+#else
+
+#define CHECK_RMD(ADDR,RES) do { \
+ switch (BCR_SWSTYLE(s)) { \
+ case 0x00: \
+ do { \
+ uint16_t rda[4]; \
+ cpu_physical_memory_read((ADDR), \
+ (void *)&rda[0], sizeof(rda)); \
+ (RES) |= (rda[2] & 0xf000)!=0xf000; \
+ (RES) |= (rda[3] & 0xf000)!=0x0000; \
+ } while (0); \
+ break; \
+ case 0x01: \
+ case 0x02: \
+ do { \
+ uint32_t rda[4]; \
+ cpu_physical_memory_read((ADDR), \
+ (void *)&rda[0], sizeof(rda)); \
+ (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+ (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \
+ } while (0); \
+ break; \
+ case 0x03: \
+ do { \
+ uint32_t rda[4]; \
+ cpu_physical_memory_read((ADDR), \
+ (void *)&rda[0], sizeof(rda)); \
+ (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \
+ (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+ } while (0); \
+ break; \
+ } \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do { \
+ switch (BCR_SWSTYLE(s)) { \
+ case 0x00: \
+ do { \
+ uint16_t xda[4]; \
+ cpu_physical_memory_read((ADDR), \
+ (void *)&xda[0], sizeof(xda)); \
+ (RES) |= (xda[2] & 0xf000)!=0xf000;\
+ } while (0); \
+ break; \
+ case 0x01: \
+ case 0x02: \
+ case 0x03: \
+ do { \
+ uint32_t xda[4]; \
+ cpu_physical_memory_read((ADDR), \
+ (void *)&xda[0], sizeof(xda)); \
+ (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \
+ } while (0); \
+ break; \
+ } \
+} while (0)
+
+#endif
+
+#define PRINT_PKTHDR(BUF) do { \
+ struct qemu_ether_header *hdr = (void *)(BUF); \
+ printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+ "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+ "type=0x%04x (bcast=%d)\n", \
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
+ hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
+ hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
+ be16_to_cpu(hdr->ether_type), \
+ !!ETHER_IS_MULTICAST(hdr->ether_dhost)); \
+} while (0)
+
+#define MULTICAST_FILTER_LEN 8
+
+static inline uint32_t lnc_mchash(const uint8_t *ether_addr)
+{
+#define LNC_POLYNOMIAL 0xEDB88320UL
+ uint32_t crc = 0xFFFFFFFF;
+ int idx, bit;
+ uint8_t data;
+
+ for (idx = 0; idx < 6; idx++) {
+ for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) {
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
+ data >>= 1;
+ }
+ }
+ return crc;
+#undef LNC_POLYNOMIAL
+}
+
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+static inline int padr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+ struct qemu_ether_header *hdr = (void *)buf;
+ uint8_t padr[6] = {
+ s->csr[12] & 0xff, s->csr[12] >> 8,
+ s->csr[13] & 0xff, s->csr[13] >> 8,
+ s->csr[14] & 0xff, s->csr[14] >> 8
+ };
+ int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6);
+#ifdef PCNET_DEBUG_MATCH
+ printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
+ "padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
+ padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]);
+ printf("padr_match result=%d\n", result);
+#endif
+ return result;
+}
+
+static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size)
+{
+ static uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct qemu_ether_header *hdr = (void *)buf;
+ int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6);
+#ifdef PCNET_DEBUG_MATCH
+ printf("padr_bcast result=%d\n", result);
+#endif
+ return result;
+}
+
+static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+ struct qemu_ether_header *hdr = (void *)buf;
+ if ((*(hdr->ether_dhost)&0x01) &&
+ ((uint64_t *)&s->csr[8])[0] != 0LL) {
+ uint8_t ladr[8] = {
+ s->csr[8] & 0xff, s->csr[8] >> 8,
+ s->csr[9] & 0xff, s->csr[9] >> 8,
+ s->csr[10] & 0xff, s->csr[10] >> 8,
+ s->csr[11] & 0xff, s->csr[11] >> 8
+ };
+ int index = lnc_mchash(hdr->ether_dhost) >> 26;
+ return !!(ladr[index >> 3] & (1 << (index & 7)));
+ }
+ return 0;
+}
+
+static inline target_phys_addr_t pcnet_rdra_addr(PCNetState *s, int idx)
+{
+ while (idx < 1) idx += CSR_RCVRL(s);
+ return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8));
+}
+
+static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
+ ticks_per_sec, 33000000L);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+static void pcnet_poll(PCNetState *s);
+static void pcnet_poll_timer(void *opaque);
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap);
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value);
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val);
+static uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap);
+
+static void pcnet_s_reset(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_s_reset\n");
+#endif
+
+ s->lnkst = 0x40;
+ s->rdra = 0;
+ s->tdra = 0;
+ s->rap = 0;
+
+ s->bcr[BCR_BSBC] &= ~0x0080;
+
+ s->csr[0] = 0x0004;
+ s->csr[3] = 0x0000;
+ s->csr[4] = 0x0115;
+ s->csr[5] = 0x0000;
+ s->csr[6] = 0x0000;
+ s->csr[8] = 0;
+ s->csr[9] = 0;
+ s->csr[10] = 0;
+ s->csr[11] = 0;
+ s->csr[12] = le16_to_cpu(((uint16_t *)&s->prom[0])[0]);
+ s->csr[13] = le16_to_cpu(((uint16_t *)&s->prom[0])[1]);
+ s->csr[14] = le16_to_cpu(((uint16_t *)&s->prom[0])[2]);
+ s->csr[15] &= 0x21c4;
+ s->csr[72] = 1;
+ s->csr[74] = 1;
+ s->csr[76] = 1;
+ s->csr[78] = 1;
+ s->csr[80] = 0x1410;
+ s->csr[88] = 0x1003;
+ s->csr[89] = 0x0262;
+ s->csr[94] = 0x0000;
+ s->csr[100] = 0x0200;
+ s->csr[103] = 0x0105;
+ s->csr[103] = 0x0105;
+ s->csr[112] = 0x0000;
+ s->csr[114] = 0x0000;
+ s->csr[122] = 0x0000;
+ s->csr[124] = 0x0000;
+
+ s->tx_busy = 0;
+}
+
+static void pcnet_update_irq(PCNetState *s)
+{
+ int isr = 0;
+ s->csr[0] &= ~0x0080;
+
+#if 1
+ if (((s->csr[0] & ~s->csr[3]) & 0x5f00) ||
+ (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) ||
+ (((s->csr[5]>>1) & s->csr[5]) & 0x0048))
+#else
+ if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ ||
+ (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ ||
+ (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ ||
+ (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ ||
+ (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ ||
+ (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ ||
+ (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ ||
+ (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ ||
+ (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ ||
+ (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ ||
+ (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ ||
+ (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */)
+#endif
+ {
+
+ isr = CSR_INEA(s);
+ s->csr[0] |= 0x0080;
+ }
+
+ if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */
+ s->csr[4] &= ~0x0080;
+ s->csr[4] |= 0x0040;
+ s->csr[0] |= 0x0080;
+ isr = 1;
+#ifdef PCNET_DEBUG
+ printf("pcnet user int\n");
+#endif
+ }
+
+#if 1
+ if (((s->csr[5]>>1) & s->csr[5]) & 0x0500)
+#else
+ if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ ||
+ (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ )
+#endif
+ {
+ isr = 1;
+ s->csr[0] |= 0x0080;
+ }
+
+ if (isr != s->isr) {
+#ifdef PCNET_DEBUG
+ printf("pcnet: INTA=%d\n", isr);
+#endif
+ }
+ pci_set_irq(&s->dev, 0, isr);
+ s->isr = isr;
+}
+
+static void pcnet_init(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_init init_addr=0x%08x\n", PHYSADDR(s,CSR_IADR(s)));
+#endif
+
+#define PCNET_INIT() do { \
+ cpu_physical_memory_read(PHYSADDR(s,CSR_IADR(s)), \
+ (uint8_t *)&initblk, sizeof(initblk)); \
+ s->csr[15] = le16_to_cpu(initblk.mode); \
+ CSR_RCVRL(s) = (initblk.rlen < 9) ? (1 << initblk.rlen) : 512; \
+ CSR_XMTRL(s) = (initblk.tlen < 9) ? (1 << initblk.tlen) : 512; \
+ s->csr[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \
+ s->csr[ 8] = le16_to_cpu(initblk.ladrf1); \
+ s->csr[ 9] = le16_to_cpu(initblk.ladrf2); \
+ s->csr[10] = le16_to_cpu(initblk.ladrf3); \
+ s->csr[11] = le16_to_cpu(initblk.ladrf4); \
+ s->csr[12] = le16_to_cpu(initblk.padr1); \
+ s->csr[13] = le16_to_cpu(initblk.padr2); \
+ s->csr[14] = le16_to_cpu(initblk.padr3); \
+ s->rdra = PHYSADDR(s,initblk.rdra); \
+ s->tdra = PHYSADDR(s,initblk.tdra); \
+} while (0)
+
+ if (BCR_SSIZE32(s)) {
+ struct pcnet_initblk32 initblk;
+ PCNET_INIT();
+#ifdef PCNET_DEBUG
+ printf("initblk.rlen=0x%02x, initblk.tlen=0x%02x\n",
+ initblk.rlen, initblk.tlen);
+#endif
+ } else {
+ struct pcnet_initblk16 initblk;
+ PCNET_INIT();
+#ifdef PCNET_DEBUG
+ printf("initblk.rlen=0x%02x, initblk.tlen=0x%02x\n",
+ initblk.rlen, initblk.tlen);
+#endif
+ }
+
+#undef PCNET_INIT
+
+ CSR_RCVRC(s) = CSR_RCVRL(s);
+ CSR_XMTRC(s) = CSR_XMTRL(s);
+
+#ifdef PCNET_DEBUG
+ printf("pcnet ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]\n",
+ BCR_SSIZE32(s),
+ s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s));
+#endif
+
+ s->csr[0] |= 0x0101;
+ s->csr[0] &= ~0x0004; /* clear STOP bit */
+}
+
+static void pcnet_start(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_start\n");
+#endif
+
+ if (!CSR_DTX(s))
+ s->csr[0] |= 0x0010; /* set TXON */
+
+ if (!CSR_DRX(s))
+ s->csr[0] |= 0x0020; /* set RXON */
+
+ s->csr[0] &= ~0x0004; /* clear STOP bit */
+ s->csr[0] |= 0x0002;
+}
+
+static void pcnet_stop(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_stop\n");
+#endif
+ s->csr[0] &= ~0x7feb;
+ s->csr[0] |= 0x0014;
+ s->csr[4] &= ~0x02c2;
+ s->csr[5] &= ~0x0011;
+ pcnet_poll_timer(s);
+}
+
+static void pcnet_rdte_poll(PCNetState *s)
+{
+ s->csr[28] = s->csr[29] = 0;
+ if (s->rdra) {
+ int bad = 0;
+#if 1
+ target_phys_addr_t crda = pcnet_rdra_addr(s, CSR_RCVRC(s));
+ target_phys_addr_t nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s));
+ target_phys_addr_t nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s));
+#else
+ target_phys_addr_t crda = s->rdra +
+ (CSR_RCVRL(s) - CSR_RCVRC(s)) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1;
+ target_phys_addr_t nrda = s->rdra +
+ (CSR_RCVRL(s) - nrdc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1;
+ target_phys_addr_t nnrd = s->rdra +
+ (CSR_RCVRL(s) - nnrc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+#endif
+
+ CHECK_RMD(PHYSADDR(s,crda), bad);
+ if (!bad) {
+ CHECK_RMD(PHYSADDR(s,nrda), bad);
+ if (bad || (nrda == crda)) nrda = 0;
+ CHECK_RMD(PHYSADDR(s,nnrd), bad);
+ if (bad || (nnrd == crda)) nnrd = 0;
+
+ s->csr[28] = crda & 0xffff;
+ s->csr[29] = crda >> 16;
+ s->csr[26] = nrda & 0xffff;
+ s->csr[27] = nrda >> 16;
+ s->csr[36] = nnrd & 0xffff;
+ s->csr[37] = nnrd >> 16;
+#ifdef PCNET_DEBUG
+ if (bad) {
+ printf("pcnet: BAD RMD RECORDS AFTER 0x%08x\n",
+ PHYSADDR(s,crda));
+ }
+ } else {
+ printf("pcnet: BAD RMD RDA=0x%08x\n", PHYSADDR(s,crda));
+#endif
+ }
+ }
+
+ if (CSR_CRDA(s)) {
+ struct pcnet_RMD rmd;
+ RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s)));
+ CSR_CRBC(s) = rmd.rmd1.bcnt;
+ CSR_CRST(s) = ((uint32_t *)&rmd)[1] >> 16;
+#ifdef PCNET_DEBUG_RMD_X
+ printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMD1=0x%08x RMD2=0x%08x\n",
+ PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s),
+ ((uint32_t *)&rmd)[1], ((uint32_t *)&rmd)[2]);
+ PRINT_RMD(&rmd);
+#endif
+ } else {
+ CSR_CRBC(s) = CSR_CRST(s) = 0;
+ }
+
+ if (CSR_NRDA(s)) {
+ struct pcnet_RMD rmd;
+ RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s)));
+ CSR_NRBC(s) = rmd.rmd1.bcnt;
+ CSR_NRST(s) = ((uint32_t *)&rmd)[1] >> 16;
+ } else {
+ CSR_NRBC(s) = CSR_NRST(s) = 0;
+ }
+
+}
+
+static int pcnet_tdte_poll(PCNetState *s)
+{
+ s->csr[34] = s->csr[35] = 0;
+ if (s->tdra) {
+ target_phys_addr_t cxda = s->tdra +
+ (CSR_XMTRL(s) - CSR_XMTRC(s)) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ int bad = 0;
+ CHECK_TMD(PHYSADDR(s, cxda),bad);
+ if (!bad) {
+ if (CSR_CXDA(s) != cxda) {
+ s->csr[60] = s->csr[34];
+ s->csr[61] = s->csr[35];
+ s->csr[62] = CSR_CXBC(s);
+ s->csr[63] = CSR_CXST(s);
+ }
+ s->csr[34] = cxda & 0xffff;
+ s->csr[35] = cxda >> 16;
+#ifdef PCNET_DEBUG
+ } else {
+ printf("pcnet: BAD TMD XDA=0x%08x\n", PHYSADDR(s,cxda));
+#endif
+ }
+ }
+
+ if (CSR_CXDA(s)) {
+ struct pcnet_TMD tmd;
+
+ TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+ CSR_CXBC(s) = tmd.tmd1.bcnt;
+ CSR_CXST(s) = ((uint32_t *)&tmd)[1] >> 16;
+ } else {
+ CSR_CXBC(s) = CSR_CXST(s) = 0;
+ }
+
+ return !!(CSR_CXST(s) & 0x8000);
+}
+
+static int pcnet_can_receive(void *opaque)
+{
+ PCNetState *s = opaque;
+ if (CSR_STOP(s) || CSR_SPND(s))
+ return 0;
+
+ if (s->recv_pos > 0)
+ return 0;
+
+ return sizeof(s->buffer)-16;
+}
+
+#define MIN_BUF_SIZE 60
+
+static void pcnet_receive(void *opaque, const uint8_t *buf, int size)
+{
+ PCNetState *s = opaque;
+ int is_padr = 0, is_bcast = 0, is_ladr = 0;
+ uint8_t buf1[60];
+
+ if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size)
+ return;
+
+#ifdef PCNET_DEBUG
+ printf("pcnet_receive size=%d\n", size);
+#endif
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ if (CSR_PROM(s)
+ || (is_padr=padr_match(s, buf, size))
+ || (is_bcast=padr_bcast(s, buf, size))
+ || (is_ladr=ladr_match(s, buf, size))) {
+
+ pcnet_rdte_poll(s);
+
+ if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
+ struct pcnet_RMD rmd;
+ int rcvrc = CSR_RCVRC(s)-1,i;
+ target_phys_addr_t nrda;
+ for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
+ if (rcvrc <= 1)
+ rcvrc = CSR_RCVRL(s);
+ nrda = s->rdra +
+ (CSR_RCVRL(s) - rcvrc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ RMDLOAD(&rmd, PHYSADDR(s,nrda));
+ if (rmd.rmd1.own) {
+#ifdef PCNET_DEBUG_RMD
+ printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
+ rcvrc, CSR_RCVRC(s));
+#endif
+ CSR_RCVRC(s) = rcvrc;
+ pcnet_rdte_poll(s);
+ break;
+ }
+ }
+ }
+
+ if (!(CSR_CRST(s) & 0x8000)) {
+#ifdef PCNET_DEBUG_RMD
+ printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
+#endif
+ s->csr[0] |= 0x1000; /* Set MISS flag */
+ CSR_MISSC(s)++;
+ } else {
+ uint8_t *src = &s->buffer[8];
+ target_phys_addr_t crda = CSR_CRDA(s);
+ struct pcnet_RMD rmd;
+ int pktcount = 0;
+
+ memcpy(src, buf, size);
+
+#if 1
+ /* no need to compute the CRC */
+ src[size] = 0;
+ src[size + 1] = 0;
+ src[size + 2] = 0;
+ src[size + 3] = 0;
+ size += 4;
+#else
+ /* XXX: avoid CRC generation */
+ if (!CSR_ASTRP_RCV(s)) {
+ uint32_t fcs = ~0;
+ uint8_t *p = src;
+
+ while (size < 46) {
+ src[size++] = 0;
+ }
+
+ while (p != &src[size]) {
+ CRC(fcs, *p++);
+ }
+ ((uint32_t *)&src[size])[0] = htonl(fcs);
+ size += 4; /* FCS at end of packet */
+ } else size += 4;
+#endif
+
+#ifdef PCNET_DEBUG_MATCH
+ PRINT_PKTHDR(buf);
+#endif
+
+ RMDLOAD(&rmd, PHYSADDR(s,crda));
+ /*if (!CSR_LAPPEN(s))*/
+ rmd.rmd1.stp = 1;
+
+#define PCNET_RECV_STORE() do { \
+ int count = MIN(4096 - rmd.rmd1.bcnt,size); \
+ target_phys_addr_t rbadr = PHYSADDR(s, rmd.rmd0.rbadr); \
+ cpu_physical_memory_write(rbadr, src, count); \
+ src += count; size -= count; \
+ rmd.rmd2.mcnt = count; rmd.rmd1.own = 0; \
+ RMDSTORE(&rmd, PHYSADDR(s,crda)); \
+ pktcount++; \
+} while (0)
+
+ PCNET_RECV_STORE();
+ if ((size > 0) && CSR_NRDA(s)) {
+ target_phys_addr_t nrda = CSR_NRDA(s);
+ RMDLOAD(&rmd, PHYSADDR(s,nrda));
+ if (rmd.rmd1.own) {
+ crda = nrda;
+ PCNET_RECV_STORE();
+ if ((size > 0) && (nrda=CSR_NNRD(s))) {
+ RMDLOAD(&rmd, PHYSADDR(s,nrda));
+ if (rmd.rmd1.own) {
+ crda = nrda;
+ PCNET_RECV_STORE();
+ }
+ }
+ }
+ }
+
+#undef PCNET_RECV_STORE
+
+ RMDLOAD(&rmd, PHYSADDR(s,crda));
+ if (size == 0) {
+ rmd.rmd1.enp = 1;
+ rmd.rmd1.pam = !CSR_PROM(s) && is_padr;
+ rmd.rmd1.lafm = !CSR_PROM(s) && is_ladr;
+ rmd.rmd1.bam = !CSR_PROM(s) && is_bcast;
+ } else {
+ rmd.rmd1.oflo = 1;
+ rmd.rmd1.buff = 1;
+ rmd.rmd1.err = 1;
+ }
+ RMDSTORE(&rmd, PHYSADDR(s,crda));
+ s->csr[0] |= 0x0400;
+
+#ifdef PCNET_DEBUG
+ printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
+ CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
+#endif
+#ifdef PCNET_DEBUG_RMD
+ PRINT_RMD(&rmd);
+#endif
+
+ while (pktcount--) {
+ if (CSR_RCVRC(s) <= 1)
+ CSR_RCVRC(s) = CSR_RCVRL(s);
+ else
+ CSR_RCVRC(s)--;
+ }
+
+ pcnet_rdte_poll(s);
+
+ }
+ }
+
+ pcnet_poll(s);
+ pcnet_update_irq(s);
+}
+
+static void pcnet_transmit(PCNetState *s)
+{
+ target_phys_addr_t xmit_cxda = 0;
+ int count = CSR_XMTRL(s)-1;
+ s->xmit_pos = -1;
+
+ if (!CSR_TXON(s)) {
+ s->csr[0] &= ~0x0008;
+ return;
+ }
+
+ s->tx_busy = 1;
+
+ txagain:
+ if (pcnet_tdte_poll(s)) {
+ struct pcnet_TMD tmd;
+
+ TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+#ifdef PCNET_DEBUG_TMD
+ printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
+ PRINT_TMD(&tmd);
+#endif
+ if (tmd.tmd1.stp) {
+ s->xmit_pos = 0;
+ if (!tmd.tmd1.enp) {
+ cpu_physical_memory_read(PHYSADDR(s, tmd.tmd0.tbadr),
+ s->buffer, 4096 - tmd.tmd1.bcnt);
+ s->xmit_pos += 4096 - tmd.tmd1.bcnt;
+ }
+ xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
+ }
+ if (tmd.tmd1.enp && (s->xmit_pos >= 0)) {
+ cpu_physical_memory_read(PHYSADDR(s, tmd.tmd0.tbadr),
+ s->buffer + s->xmit_pos, 4096 - tmd.tmd1.bcnt);
+ s->xmit_pos += 4096 - tmd.tmd1.bcnt;
+#ifdef PCNET_DEBUG
+ printf("pcnet_transmit size=%d\n", s->xmit_pos);
+#endif
+ if (CSR_LOOP(s))
+ pcnet_receive(s, s->buffer, s->xmit_pos);
+ else
+ qemu_send_packet(s->vc, s->buffer, s->xmit_pos);
+
+ s->csr[0] &= ~0x0008; /* clear TDMD */
+ s->csr[4] |= 0x0004; /* set TXSTRT */
+ s->xmit_pos = -1;
+ }
+
+ tmd.tmd1.own = 0;
+ TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+ if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && tmd.tmd1.ltint))
+ s->csr[0] |= 0x0200; /* set TINT */
+
+ if (CSR_XMTRC(s)<=1)
+ CSR_XMTRC(s) = CSR_XMTRL(s);
+ else
+ CSR_XMTRC(s)--;
+ if (count--)
+ goto txagain;
+
+ } else
+ if (s->xmit_pos >= 0) {
+ struct pcnet_TMD tmd;
+ TMDLOAD(&tmd, PHYSADDR(s,xmit_cxda));
+ tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
+ tmd.tmd1.own = 0;
+ TMDSTORE(&tmd, PHYSADDR(s,xmit_cxda));
+ s->csr[0] |= 0x0200; /* set TINT */
+ if (!CSR_DXSUFLO(s)) {
+ s->csr[0] &= ~0x0010;
+ } else
+ if (count--)
+ goto txagain;
+ }
+
+ s->tx_busy = 0;
+}
+
+static void pcnet_poll(PCNetState *s)
+{
+ if (CSR_RXON(s)) {
+ pcnet_rdte_poll(s);
+ }
+
+ if (CSR_TDMD(s) ||
+ (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s)))
+ {
+ /* prevent recursion */
+ if (s->tx_busy)
+ return;
+
+ pcnet_transmit(s);
+ }
+}
+
+static void pcnet_poll_timer(void *opaque)
+{
+ PCNetState *s = opaque;
+
+ qemu_del_timer(s->poll_timer);
+
+ if (CSR_TDMD(s)) {
+ pcnet_transmit(s);
+ }
+
+ pcnet_update_irq(s);
+
+ if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) {
+ uint64_t now = qemu_get_clock(vm_clock) * 33;
+ if (!s->timer || !now)
+ s->timer = now;
+ else {
+ uint64_t t = now - s->timer + CSR_POLL(s);
+ if (t > 0xffffLL) {
+ pcnet_poll(s);
+ CSR_POLL(s) = CSR_PINT(s);
+ } else
+ CSR_POLL(s) = t;
+ }
+ qemu_mod_timer(s->poll_timer,
+ pcnet_get_next_poll_time(s,qemu_get_clock(vm_clock)));
+ }
+}
+
+
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value)
+{
+ uint16_t val = new_value;
+#ifdef PCNET_DEBUG_CSR
+ printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+ switch (rap) {
+ case 0:
+ s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */
+
+ s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048);
+
+ val = (val & 0x007f) | (s->csr[0] & 0x7f00);
+
+ /* IFF STOP, STRT and INIT are set, clear STRT and INIT */
+ if ((val&7) == 7)
+ val &= ~3;
+
+ if (!CSR_STOP(s) && (val & 4))
+ pcnet_stop(s);
+
+ if (!CSR_INIT(s) && (val & 1))
+ pcnet_init(s);
+
+ if (!CSR_STRT(s) && (val & 2))
+ pcnet_start(s);
+
+ if (CSR_TDMD(s))
+ pcnet_transmit(s);
+
+ return;
+ case 1:
+ case 2:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 18: /* CRBAL */
+ case 19: /* CRBAU */
+ case 20: /* CXBAL */
+ case 21: /* CXBAU */
+ case 22: /* NRBAU */
+ case 23: /* NRBAU */
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40: /* CRBC */
+ case 41:
+ case 42: /* CXBC */
+ case 43:
+ case 44:
+ case 45:
+ case 46: /* POLL */
+ case 47: /* POLLINT */
+ case 72:
+ case 74:
+ case 76: /* RCVRL */
+ case 78: /* XMTRL */
+ case 112:
+ if (CSR_STOP(s) || CSR_SPND(s))
+ break;
+ return;
+ case 3:
+ break;
+ case 4:
+ s->csr[4] &= ~(val & 0x026a);
+ val &= ~0x026a; val |= s->csr[4] & 0x026a;
+ break;
+ case 5:
+ s->csr[5] &= ~(val & 0x0a90);
+ val &= ~0x0a90; val |= s->csr[5] & 0x0a90;
+ break;
+ case 16:
+ pcnet_csr_writew(s,1,val);
+ return;
+ case 17:
+ pcnet_csr_writew(s,2,val);
+ return;
+ case 58:
+ pcnet_bcr_writew(s,BCR_SWS,val);
+ break;
+ default:
+ return;
+ }
+ s->csr[rap] = val;
+}
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap)
+{
+ uint32_t val;
+ switch (rap) {
+ case 0:
+ pcnet_update_irq(s);
+ val = s->csr[0];
+ val |= (val & 0x7800) ? 0x8000 : 0;
+ break;
+ case 16:
+ return pcnet_csr_readw(s,1);
+ case 17:
+ return pcnet_csr_readw(s,2);
+ case 58:
+ return pcnet_bcr_readw(s,BCR_SWS);
+ case 88:
+ val = s->csr[89];
+ val <<= 16;
+ val |= s->csr[88];
+ break;
+ default:
+ val = s->csr[rap];
+ }
+#ifdef PCNET_DEBUG_CSR
+ printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+ return val;
+}
+
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val)
+{
+ rap &= 127;
+#ifdef PCNET_DEBUG_BCR
+ printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+ switch (rap) {
+ case BCR_SWS:
+ if (!(CSR_STOP(s) || CSR_SPND(s)))
+ return;
+ val &= ~0x0300;
+ switch (val & 0x00ff) {
+ case 0:
+ val |= 0x0200;
+ break;
+ case 1:
+ val |= 0x0100;
+ break;
+ case 2:
+ case 3:
+ val |= 0x0300;
+ break;
+ default:
+ printf("Bad SWSTYLE=0x%02x\n", val & 0xff);
+ val = 0x0200;
+ break;
+ }
+#ifdef PCNET_DEBUG
+ printf("BCR_SWS=0x%04x\n", val);
+#endif
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ case BCR_MC:
+ case BCR_FDC:
+ case BCR_BSBC:
+ case BCR_EECAS:
+ case BCR_PLAT:
+ s->bcr[rap] = val;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
+{
+ uint32_t val;
+ rap &= 127;
+ switch (rap) {
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ val = s->bcr[rap] & ~0x8000;
+ val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
+ break;
+ default:
+ val = rap < 32 ? s->bcr[rap] : 0;
+ break;
+ }
+#ifdef PCNET_DEBUG_BCR
+ printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+ return val;
+}
+
+static void pcnet_h_reset(PCNetState *s)
+{
+ int i;
+ uint16_t checksum;
+
+ /* Initialize the PROM */
+
+ memcpy(s->prom, s->nd->macaddr, 6);
+ s->prom[12] = s->prom[13] = 0x00;
+ s->prom[14] = s->prom[15] = 0x57;
+
+ for (i = 0,checksum = 0; i < 16; i++)
+ checksum += s->prom[i];
+ *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum);
+
+
+ s->bcr[BCR_MSRDA] = 0x0005;
+ s->bcr[BCR_MSWRA] = 0x0005;
+ s->bcr[BCR_MC ] = 0x0002;
+ s->bcr[BCR_LNKST] = 0x00c0;
+ s->bcr[BCR_LED1 ] = 0x0084;
+ s->bcr[BCR_LED2 ] = 0x0088;
+ s->bcr[BCR_LED3 ] = 0x0090;
+ s->bcr[BCR_FDC ] = 0x0000;
+ s->bcr[BCR_BSBC ] = 0x9001;
+ s->bcr[BCR_EECAS] = 0x0002;
+ s->bcr[BCR_SWS ] = 0x0200;
+ s->bcr[BCR_PLAT ] = 0xff06;
+
+ pcnet_s_reset(s);
+}
+
+static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+#ifdef PCNET_DEBUG
+ printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+ /* Check APROMWE bit to enable write access */
+ if (pcnet_bcr_readw(s,2) & 0x80)
+ s->prom[addr & 15] = val;
+}
+
+static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = s->prom[addr &= 15];
+#ifdef PCNET_DEBUG
+ printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+ pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val);
+#endif
+ if (!BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ pcnet_csr_writew(s, s->rap, val);
+ break;
+ case 0x02:
+ s->rap = val & 0x7f;
+ break;
+ case 0x06:
+ pcnet_bcr_writew(s, s->rap, val);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+}
+
+static uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = -1;
+ pcnet_poll_timer(s);
+ if (!BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ val = pcnet_csr_readw(s, s->rap);
+ break;
+ case 0x02:
+ val = s->rap;
+ break;
+ case 0x04:
+ pcnet_s_reset(s);
+ val = 0;
+ break;
+ case 0x06:
+ val = pcnet_bcr_readw(s, s->rap);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff);
+#endif
+ return val;
+}
+
+static void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+ pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ if (BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ pcnet_csr_writew(s, s->rap, val & 0xffff);
+ break;
+ case 0x04:
+ s->rap = val & 0x7f;
+ break;
+ case 0x0c:
+ pcnet_bcr_writew(s, s->rap, val & 0xffff);
+ break;
+ }
+ } else
+ if ((addr & 0x0f) == 0) {
+ /* switch device to dword i/o mode */
+ pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080);
+#ifdef PCNET_DEBUG_IO
+ printf("device switched into dword i/o mode\n");
+#endif
+ }
+ pcnet_update_irq(s);
+}
+
+static uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = -1;
+ pcnet_poll_timer(s);
+ if (BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ val = pcnet_csr_readw(s, s->rap);
+ break;
+ case 0x04:
+ val = s->rap;
+ break;
+ case 0x08:
+ pcnet_s_reset(s);
+ val = 0;
+ break;
+ case 0x0c:
+ val = pcnet_bcr_readw(s, s->rap);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ return val;
+}
+
+static void pcnet_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCNetState *d = (PCNetState *)pci_dev;
+
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_map addr=0x%04x size=0x%04x\n", addr, size);
+#endif
+
+ register_ioport_write(addr, 16, 1, pcnet_aprom_writeb, d);
+ register_ioport_read(addr, 16, 1, pcnet_aprom_readb, d);
+
+ register_ioport_write(addr + 0x10, 0x10, 2, pcnet_ioport_writew, d);
+ register_ioport_read(addr + 0x10, 0x10, 2, pcnet_ioport_readw, d);
+ register_ioport_write(addr + 0x10, 0x10, 4, pcnet_ioport_writel, d);
+ register_ioport_read(addr + 0x10, 0x10, 4, pcnet_ioport_readl, d);
+}
+
+static void pcnet_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writeb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+ if (!(addr & 0x10))
+ pcnet_aprom_writeb(d, addr & 0x0f, val);
+}
+
+static uint32_t pcnet_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val = -1;
+ if (!(addr & 0x10))
+ val = pcnet_aprom_readb(d, addr & 0x0f);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readb addr=0x%08x val=0x%02x\n", addr, val & 0xff);
+#endif
+ return val;
+}
+
+static void pcnet_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writew addr=0x%08x val=0x%04x\n", addr, val);
+#endif
+ if (addr & 0x10)
+ pcnet_ioport_writew(d, addr & 0x0f, val);
+ else {
+ addr &= 0x0f;
+ pcnet_aprom_writeb(d, addr, val & 0xff);
+ pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+ }
+}
+
+static uint32_t pcnet_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val = -1;
+ if (addr & 0x10)
+ val = pcnet_ioport_readw(d, addr & 0x0f);
+ else {
+ addr &= 0x0f;
+ val = pcnet_aprom_readb(d, addr+1);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr);
+ }
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readw addr=0x%08x val = 0x%04x\n", addr, val & 0xffff);
+#endif
+ return val;
+}
+
+static void pcnet_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writel addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ if (addr & 0x10)
+ pcnet_ioport_writel(d, addr & 0x0f, val);
+ else {
+ addr &= 0x0f;
+ pcnet_aprom_writeb(d, addr, val & 0xff);
+ pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+ pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16);
+ pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24);
+ }
+}
+
+static uint32_t pcnet_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val;
+ if (addr & 0x10)
+ val = pcnet_ioport_readl(d, addr & 0x0f);
+ else {
+ addr &= 0x0f;
+ val = pcnet_aprom_readb(d, addr+3);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr+2);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr+1);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr);
+ }
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readl addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ return val;
+}
+
+
+static CPUWriteMemoryFunc *pcnet_mmio_write[] = {
+ (CPUWriteMemoryFunc *)&pcnet_mmio_writeb,
+ (CPUWriteMemoryFunc *)&pcnet_mmio_writew,
+ (CPUWriteMemoryFunc *)&pcnet_mmio_writel
+};
+
+static CPUReadMemoryFunc *pcnet_mmio_read[] = {
+ (CPUReadMemoryFunc *)&pcnet_mmio_readb,
+ (CPUReadMemoryFunc *)&pcnet_mmio_readw,
+ (CPUReadMemoryFunc *)&pcnet_mmio_readl
+};
+
+static void pcnet_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCNetState *d = (PCNetState *)pci_dev;
+
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_map addr=0x%08x 0x%08x\n", addr, size);
+#endif
+
+ cpu_register_physical_memory(addr, PCNET_PNPMMIO_SIZE, d->mmio_io_addr);
+}
+
+void pci_pcnet_init(PCIBus *bus, NICInfo *nd)
+{
+ PCNetState *d;
+ uint8_t *pci_conf;
+
+#if 0
+ printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n",
+ sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD));
+#endif
+
+ d = (PCNetState *)pci_register_device(bus, "PCNet", sizeof(PCNetState),
+ -1, NULL, NULL);
+
+ pci_conf = d->dev.config;
+
+ *(uint16_t *)&pci_conf[0x00] = cpu_to_le16(0x1022);
+ *(uint16_t *)&pci_conf[0x02] = cpu_to_le16(0x2000);
+ *(uint16_t *)&pci_conf[0x04] = cpu_to_le16(0x0007);
+ *(uint16_t *)&pci_conf[0x06] = cpu_to_le16(0x0280);
+ pci_conf[0x08] = 0x10;
+ pci_conf[0x09] = 0x00;
+ pci_conf[0x0a] = 0x00; // ethernet network controller
+ pci_conf[0x0b] = 0x02;
+ pci_conf[0x0e] = 0x00; // header_type
+
+ *(uint32_t *)&pci_conf[0x10] = cpu_to_le32(0x00000001);
+ *(uint32_t *)&pci_conf[0x14] = cpu_to_le32(0x00000000);
+
+ pci_conf[0x3d] = 1; // interrupt pin 0
+ pci_conf[0x3e] = 0x06;
+ pci_conf[0x3f] = 0xff;
+
+ /* Handler for memory-mapped I/O */
+ d->mmio_io_addr =
+ cpu_register_io_memory(0, pcnet_mmio_read, pcnet_mmio_write, d);
+
+ pci_register_io_region((PCIDevice *)d, 0, PCNET_IOPORT_SIZE,
+ PCI_ADDRESS_SPACE_IO, pcnet_ioport_map);
+
+ pci_register_io_region((PCIDevice *)d, 1, PCNET_PNPMMIO_SIZE,
+ PCI_ADDRESS_SPACE_MEM, pcnet_mmio_map);
+
+ d->poll_timer = qemu_new_timer(vm_clock, pcnet_poll_timer, d);
+
+ d->nd = nd;
+
+ d->vc = qemu_new_vlan_client(nd->vlan, pcnet_receive,
+ pcnet_can_receive, d);
+
+ snprintf(d->vc->info_str, sizeof(d->vc->info_str),
+ "pcnet macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ d->nd->macaddr[0],
+ d->nd->macaddr[1],
+ d->nd->macaddr[2],
+ d->nd->macaddr[3],
+ d->nd->macaddr[4],
+ d->nd->macaddr[5]);
+
+ pcnet_h_reset(d);
+}
diff --git a/hw/pcspk.c b/hw/pcspk.c
new file mode 100644
index 0000000..0d52b31
--- /dev/null
+++ b/hw/pcspk.c
@@ -0,0 +1,147 @@
+/*
+ * QEMU PC speaker emulation
+ *
+ * Copyright (c) 2006 Joachim Henke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+
+#define PCSPK_BUF_LEN 1792
+#define PCSPK_SAMPLE_RATE 32000
+#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
+#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
+
+typedef struct {
+ uint8_t sample_buf[PCSPK_BUF_LEN];
+ QEMUSoundCard card;
+ SWVoiceOut *voice;
+ PITState *pit;
+ unsigned int pit_count;
+ unsigned int samples;
+ unsigned int play_pos;
+ int data_on;
+ int dummy_refresh_clock;
+} PCSpkState;
+
+static const char *s_spk = "pcspk";
+static PCSpkState pcspk_state;
+
+static inline void generate_samples(PCSpkState *s)
+{
+ unsigned int i;
+
+ if (s->pit_count) {
+ const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
+ const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
+
+ /* multiple of wavelength for gapless looping */
+ s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1;
+ for (i = 0; i < s->samples; ++i)
+ s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
+ } else {
+ s->samples = PCSPK_BUF_LEN;
+ for (i = 0; i < PCSPK_BUF_LEN; ++i)
+ s->sample_buf[i] = 128; /* silence */
+ }
+}
+
+static void pcspk_callback(void *opaque, int free)
+{
+ PCSpkState *s = opaque;
+ unsigned int n;
+
+ if (pit_get_mode(s->pit, 2) != 3)
+ return;
+
+ n = pit_get_initial_count(s->pit, 2);
+ /* avoid frequencies that are not reproducible with sample rate */
+ if (n < PCSPK_MIN_COUNT)
+ n = 0;
+
+ if (s->pit_count != n) {
+ s->pit_count = n;
+ s->play_pos = 0;
+ generate_samples(s);
+ }
+
+ while (free > 0) {
+ n = audio_MIN(s->samples - s->play_pos, (unsigned int)free);
+ n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
+ if (!n)
+ break;
+ s->play_pos = (s->play_pos + n) % s->samples;
+ free -= n;
+ }
+}
+
+int pcspk_audio_init(AudioState *audio)
+{
+ PCSpkState *s = &pcspk_state;
+ audsettings_t as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+
+ if (!audio) {
+ AUD_log(s_spk, "No audio state\n");
+ return -1;
+ }
+ AUD_register_card(audio, s_spk, &s->card);
+
+ s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
+ if (!s->voice) {
+ AUD_log(s_spk, "Could not open voice\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)
+{
+ PCSpkState *s = opaque;
+ int out;
+
+ s->dummy_refresh_clock ^= (1 << 4);
+ out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5;
+
+ return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out;
+}
+
+static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCSpkState *s = opaque;
+ const int gate = val & 1;
+
+ s->data_on = (val >> 1) & 1;
+ pit_set_gate(s->pit, 2, gate);
+ if (s->voice) {
+ if (gate) /* restart */
+ s->play_pos = 0;
+ AUD_set_active_out(s->voice, gate & s->data_on);
+ }
+}
+
+void pcspk_init(PITState *pit)
+{
+ PCSpkState *s = &pcspk_state;
+
+ s->pit = pit;
+ register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s);
+ register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s);
+}
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
new file mode 100644
index 0000000..ee2f63a
--- /dev/null
+++ b/hw/pflash_cfi02.c
@@ -0,0 +1,624 @@
+/*
+ * CFI parallel flash with AMD command set emulation
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
+ * Supported commands/modes are:
+ * - flash read
+ * - flash write
+ * - flash ID read
+ * - sector erase
+ * - chip erase
+ * - unlock bypass command
+ * - CFI queries
+ *
+ * It does not support flash interleaving.
+ * It does not implement boot blocs with reduced size
+ * It does not implement software data protection as found in many real chips
+ * It does not implement erase suspend/resume commands
+ * It does not implement multiple sectors erase
+ */
+
+#include "vl.h"
+
+//#define PFLASH_DEBUG
+#ifdef PFLASH_DEBUG
+#define DPRINTF(fmt, args...) \
+do { \
+ printf("PFLASH: " fmt , ##args); \
+} while (0)
+#else
+#define DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+struct pflash_t {
+ BlockDriverState *bs;
+ target_ulong base;
+ target_ulong sector_len;
+ target_ulong total_len;
+ int width;
+ int wcycle; /* if 0, the flash is read normally */
+ int bypass;
+ int ro;
+ uint8_t cmd;
+ uint8_t status;
+ uint16_t ident[4];
+ uint8_t cfi_len;
+ uint8_t cfi_table[0x52];
+ QEMUTimer *timer;
+ ram_addr_t off;
+ int fl_mem;
+ void *storage;
+};
+
+static void pflash_timer (void *opaque)
+{
+ pflash_t *pfl = opaque;
+
+ DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+ /* Reset flash */
+ pfl->status ^= 0x80;
+ if (pfl->bypass) {
+ pfl->wcycle = 2;
+ } else {
+ cpu_register_physical_memory(pfl->base, pfl->total_len,
+ pfl->off | IO_MEM_ROMD | pfl->fl_mem);
+ pfl->wcycle = 0;
+ }
+ pfl->cmd = 0;
+}
+
+static uint32_t pflash_read (pflash_t *pfl, target_ulong offset, int width)
+{
+ target_ulong boff;
+ uint32_t ret;
+ uint8_t *p;
+
+ DPRINTF("%s: offset %08x\n", __func__, offset);
+ ret = -1;
+ offset -= pfl->base;
+ boff = offset & 0xFF;
+ if (pfl->width == 2)
+ boff = boff >> 1;
+ else if (pfl->width == 4)
+ boff = boff >> 2;
+ switch (pfl->cmd) {
+ default:
+ /* This should never happen : reset state & treat it as a read*/
+ DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ case 0x80:
+ /* We accept reads during second unlock sequence... */
+ case 0x00:
+ flash_read:
+ /* Flash area read */
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ ret = p[offset];
+// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
+ break;
+ case 2:
+#if defined(TARGET_WORDS_BIGENDIAN)
+ ret = p[offset] << 8;
+ ret |= p[offset + 1];
+#else
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+#endif
+// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
+ break;
+ case 4:
+#if defined(TARGET_WORDS_BIGENDIAN)
+ ret = p[offset] << 24;
+ ret |= p[offset + 1] << 16;
+ ret |= p[offset + 2] << 8;
+ ret |= p[offset + 3];
+#else
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 2] << 16;
+ ret |= p[offset + 3] << 24;
+#endif
+// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
+ break;
+ }
+ break;
+ case 0x90:
+ /* flash ID read */
+ switch (boff) {
+ case 0x00:
+ case 0x01:
+ ret = pfl->ident[boff & 0x01];
+ break;
+ case 0x02:
+ ret = 0x00; /* Pretend all sectors are unprotected */
+ break;
+ case 0x0E:
+ case 0x0F:
+ if (pfl->ident[2 + (boff & 0x01)] == (uint8_t)-1)
+ goto flash_read;
+ ret = pfl->ident[2 + (boff & 0x01)];
+ break;
+ default:
+ goto flash_read;
+ }
+ DPRINTF("%s: ID %d %x\n", __func__, boff, ret);
+ break;
+ case 0xA0:
+ case 0x10:
+ case 0x30:
+ /* Status register read */
+ ret = pfl->status;
+ DPRINTF("%s: status %x\n", __func__, ret);
+ /* Toggle bit 6 */
+ pfl->status ^= 0x40;
+ break;
+ case 0x98:
+ /* CFI query mode */
+ if (boff > pfl->cfi_len)
+ ret = 0;
+ else
+ ret = pfl->cfi_table[boff];
+ break;
+ }
+
+ return ret;
+}
+
+/* update flash content on disk */
+static void pflash_update(pflash_t *pfl, int offset,
+ int size)
+{
+ int offset_end;
+ if (pfl->bs) {
+ offset_end = offset + size;
+ /* round to sectors */
+ offset = offset >> 9;
+ offset_end = (offset_end + 511) >> 9;
+ bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
+ offset_end - offset);
+ }
+}
+
+static void pflash_write (pflash_t *pfl, target_ulong offset, uint32_t value,
+ int width)
+{
+ target_ulong boff;
+ uint8_t *p;
+ uint8_t cmd;
+
+ /* WARNING: when the memory area is in ROMD mode, the offset is a
+ ram offset, not a physical address */
+ if (pfl->wcycle == 0)
+ offset -= (target_ulong)(long)pfl->storage;
+ else
+ offset -= pfl->base;
+
+ cmd = value;
+ DPRINTF("%s: offset %08x %08x %d\n", __func__, offset, value, width);
+ if (pfl->cmd != 0xA0 && cmd == 0xF0) {
+ DPRINTF("%s: flash reset asked (%02x %02x)\n",
+ __func__, pfl->cmd, cmd);
+ goto reset_flash;
+ }
+ /* Set the device in I/O access mode */
+ cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem);
+ boff = offset & (pfl->sector_len - 1);
+ if (pfl->width == 2)
+ boff = boff >> 1;
+ else if (pfl->width == 4)
+ boff = boff >> 2;
+ switch (pfl->wcycle) {
+ case 0:
+ /* We're in read mode */
+ check_unlock0:
+ if (boff == 0x55 && cmd == 0x98) {
+ enter_CFI_mode:
+ /* Enter CFI query mode */
+ pfl->wcycle = 7;
+ pfl->cmd = 0x98;
+ return;
+ }
+ if (boff != 0x555 || cmd != 0xAA) {
+ DPRINTF("%s: unlock0 failed %04x %02x %04x\n",
+ __func__, boff, cmd, 0x555);
+ goto reset_flash;
+ }
+ DPRINTF("%s: unlock sequence started\n", __func__);
+ break;
+ case 1:
+ /* We started an unlock sequence */
+ check_unlock1:
+ if (boff != 0x2AA || cmd != 0x55) {
+ DPRINTF("%s: unlock1 failed %04x %02x\n", __func__, boff, cmd);
+ goto reset_flash;
+ }
+ DPRINTF("%s: unlock sequence done\n", __func__);
+ break;
+ case 2:
+ /* We finished an unlock sequence */
+ if (!pfl->bypass && boff != 0x555) {
+ DPRINTF("%s: command failed %04x %02x\n", __func__, boff, cmd);
+ goto reset_flash;
+ }
+ switch (cmd) {
+ case 0x20:
+ pfl->bypass = 1;
+ goto do_bypass;
+ case 0x80:
+ case 0x90:
+ case 0xA0:
+ pfl->cmd = cmd;
+ DPRINTF("%s: starting command %02x\n", __func__, cmd);
+ break;
+ default:
+ DPRINTF("%s: unknown command %02x\n", __func__, cmd);
+ goto reset_flash;
+ }
+ break;
+ case 3:
+ switch (pfl->cmd) {
+ case 0x80:
+ /* We need another unlock sequence */
+ goto check_unlock0;
+ case 0xA0:
+ DPRINTF("%s: write data offset %08x %08x %d\n",
+ __func__, offset, value, width);
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ p[offset] &= value;
+ pflash_update(pfl, offset, 1);
+ break;
+ case 2:
+#if defined(TARGET_WORDS_BIGENDIAN)
+ p[offset] &= value >> 8;
+ p[offset + 1] &= value;
+#else
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+#endif
+ pflash_update(pfl, offset, 2);
+ break;
+ case 4:
+#if defined(TARGET_WORDS_BIGENDIAN)
+ p[offset] &= value >> 24;
+ p[offset + 1] &= value >> 16;
+ p[offset + 2] &= value >> 8;
+ p[offset + 3] &= value;
+#else
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+ p[offset + 2] &= value >> 16;
+ p[offset + 3] &= value >> 24;
+#endif
+ pflash_update(pfl, offset, 4);
+ break;
+ }
+ pfl->status = 0x00 | ~(value & 0x80);
+ /* Let's pretend write is immediate */
+ if (pfl->bypass)
+ goto do_bypass;
+ goto reset_flash;
+ case 0x90:
+ if (pfl->bypass && cmd == 0x00) {
+ /* Unlock bypass reset */
+ goto reset_flash;
+ }
+ /* We can enter CFI query mode from autoselect mode */
+ if (boff == 0x55 && cmd == 0x98)
+ goto enter_CFI_mode;
+ /* No break here */
+ default:
+ DPRINTF("%s: invalid write for command %02x\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ case 4:
+ switch (pfl->cmd) {
+ case 0xA0:
+ /* Ignore writes while flash data write is occuring */
+ /* As we suppose write is immediate, this should never happen */
+ return;
+ case 0x80:
+ goto check_unlock1;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid command state %02x (wc 4)\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ break;
+ case 5:
+ switch (cmd) {
+ case 0x10:
+ if (boff != 0x555) {
+ DPRINTF("%s: chip erase: invalid address %04x\n",
+ __func__, offset);
+ goto reset_flash;
+ }
+ /* Chip erase */
+ DPRINTF("%s: start chip erase\n", __func__);
+ memset(pfl->storage, 0xFF, pfl->total_len);
+ pfl->status = 0x00;
+ pflash_update(pfl, 0, pfl->total_len);
+ /* Let's wait 5 seconds before chip erase is done */
+ qemu_mod_timer(pfl->timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec * 5));
+ break;
+ case 0x30:
+ /* Sector erase */
+ p = pfl->storage;
+ offset &= ~(pfl->sector_len - 1);
+ DPRINTF("%s: start sector erase at %08x\n", __func__, offset);
+ memset(p + offset, 0xFF, pfl->sector_len);
+ pflash_update(pfl, offset, pfl->sector_len);
+ pfl->status = 0x00;
+ /* Let's wait 1/2 second before sector erase is done */
+ qemu_mod_timer(pfl->timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 2));
+ break;
+ default:
+ DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
+ goto reset_flash;
+ }
+ pfl->cmd = cmd;
+ break;
+ case 6:
+ switch (pfl->cmd) {
+ case 0x10:
+ /* Ignore writes during chip erase */
+ return;
+ case 0x30:
+ /* Ignore writes during sector erase */
+ return;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid command state %02x (wc 6)\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ break;
+ case 7: /* Special value for CFI queries */
+ DPRINTF("%s: invalid write in CFI query mode\n", __func__);
+ goto reset_flash;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid write state (wc 7)\n", __func__);
+ goto reset_flash;
+ }
+ pfl->wcycle++;
+
+ return;
+
+ /* Reset flash */
+ reset_flash:
+ if (pfl->wcycle != 0) {
+ cpu_register_physical_memory(pfl->base, pfl->total_len,
+ pfl->off | IO_MEM_ROMD | pfl->fl_mem);
+ }
+ pfl->bypass = 0;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ return;
+
+ do_bypass:
+ pfl->wcycle = 2;
+ pfl->cmd = 0;
+ return;
+}
+
+
+static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr)
+{
+ return pflash_read(opaque, addr, 1);
+}
+
+static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 2);
+}
+
+static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 4);
+}
+
+static void pflash_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_write(opaque, addr, value, 1);
+}
+
+static void pflash_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 2);
+}
+
+static void pflash_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 4);
+}
+
+static CPUWriteMemoryFunc *pflash_write_ops[] = {
+ &pflash_writeb,
+ &pflash_writew,
+ &pflash_writel,
+};
+
+static CPUReadMemoryFunc *pflash_read_ops[] = {
+ &pflash_readb,
+ &pflash_readw,
+ &pflash_readl,
+};
+
+/* Count trailing zeroes of a 32 bits quantity */
+static int ctz32 (uint32_t n)
+{
+ int ret;
+
+ ret = 0;
+ if (!(n & 0xFFFF)) {
+ ret += 16;
+ n = n >> 16;
+ }
+ if (!(n & 0xFF)) {
+ ret += 8;
+ n = n >> 8;
+ }
+ if (!(n & 0xF)) {
+ ret += 4;
+ n = n >> 4;
+ }
+ if (!(n & 0x3)) {
+ ret += 2;
+ n = n >> 2;
+ }
+ if (!(n & 0x1)) {
+ ret++;
+ n = n >> 1;
+ }
+#if 0 /* This is not necessary as n is never 0 */
+ if (!n)
+ ret++;
+#endif
+
+ return ret;
+}
+
+pflash_t *pflash_register (target_ulong base, ram_addr_t off,
+ BlockDriverState *bs,
+ target_ulong sector_len, int nb_blocs, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3)
+{
+ pflash_t *pfl;
+ target_long total_len;
+
+ total_len = sector_len * nb_blocs;
+ /* XXX: to be fixed */
+ if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
+ total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
+ return NULL;
+ pfl = qemu_mallocz(sizeof(pflash_t));
+ if (pfl == NULL)
+ return NULL;
+ pfl->storage = phys_ram_base + off;
+ pfl->fl_mem = cpu_register_io_memory(0, pflash_read_ops, pflash_write_ops, pfl);
+ pfl->off = off;
+ cpu_register_physical_memory(base, total_len,
+ off | pfl->fl_mem | IO_MEM_ROMD);
+ pfl->bs = bs;
+ if (pfl->bs) {
+ /* read the initial flash content */
+ bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
+ }
+#if 0 /* XXX: there should be a bit to set up read-only,
+ * the same way the hardware does (with WP pin).
+ */
+ pfl->ro = 1;
+#else
+ pfl->ro = 0;
+#endif
+ pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
+ pfl->base = base;
+ pfl->sector_len = sector_len;
+ pfl->total_len = total_len;
+ pfl->width = width;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ pfl->status = 0;
+ pfl->ident[0] = id0;
+ pfl->ident[1] = id1;
+ pfl->ident[2] = id2;
+ pfl->ident[3] = id3;
+ /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
+ pfl->cfi_len = 0x52;
+ /* Standard "QRY" string */
+ pfl->cfi_table[0x10] = 'Q';
+ pfl->cfi_table[0x11] = 'R';
+ pfl->cfi_table[0x12] = 'Y';
+ /* Command set (AMD/Fujitsu) */
+ pfl->cfi_table[0x13] = 0x02;
+ pfl->cfi_table[0x14] = 0x00;
+ /* Primary extended table address (none) */
+ pfl->cfi_table[0x15] = 0x00;
+ pfl->cfi_table[0x16] = 0x00;
+ /* Alternate command set (none) */
+ pfl->cfi_table[0x17] = 0x00;
+ pfl->cfi_table[0x18] = 0x00;
+ /* Alternate extended table (none) */
+ pfl->cfi_table[0x19] = 0x00;
+ pfl->cfi_table[0x1A] = 0x00;
+ /* Vcc min */
+ pfl->cfi_table[0x1B] = 0x27;
+ /* Vcc max */
+ pfl->cfi_table[0x1C] = 0x36;
+ /* Vpp min (no Vpp pin) */
+ pfl->cfi_table[0x1D] = 0x00;
+ /* Vpp max (no Vpp pin) */
+ pfl->cfi_table[0x1E] = 0x00;
+ /* Reserved */
+ pfl->cfi_table[0x1F] = 0x07;
+ /* Timeout for min size buffer write (16 µs) */
+ pfl->cfi_table[0x20] = 0x04;
+ /* Typical timeout for block erase (512 ms) */
+ pfl->cfi_table[0x21] = 0x09;
+ /* Typical timeout for full chip erase (4096 ms) */
+ pfl->cfi_table[0x22] = 0x0C;
+ /* Reserved */
+ pfl->cfi_table[0x23] = 0x01;
+ /* Max timeout for buffer write */
+ pfl->cfi_table[0x24] = 0x04;
+ /* Max timeout for block erase */
+ pfl->cfi_table[0x25] = 0x0A;
+ /* Max timeout for chip erase */
+ pfl->cfi_table[0x26] = 0x0D;
+ /* Device size */
+ pfl->cfi_table[0x27] = ctz32(total_len) + 1;
+ /* Flash device interface (8 & 16 bits) */
+ pfl->cfi_table[0x28] = 0x02;
+ pfl->cfi_table[0x29] = 0x00;
+ /* Max number of bytes in multi-bytes write */
+ pfl->cfi_table[0x2A] = 0x05;
+ pfl->cfi_table[0x2B] = 0x00;
+ /* Number of erase block regions (uniform) */
+ pfl->cfi_table[0x2C] = 0x01;
+ /* Erase block region 1 */
+ pfl->cfi_table[0x2D] = nb_blocs - 1;
+ pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
+ pfl->cfi_table[0x2F] = sector_len >> 8;
+ pfl->cfi_table[0x30] = sector_len >> 16;
+
+ return pfl;
+}
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
new file mode 100644
index 0000000..1f7ad94
--- /dev/null
+++ b/hw/piix_pci.c
@@ -0,0 +1,419 @@
+/*
+ * QEMU i440FX/PIIX3 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+typedef uint32_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState I440FXState;
+
+static void i440fx_addr_writel(void* opaque, uint32_t addr, uint32_t val)
+{
+ I440FXState *s = opaque;
+ s->config_reg = val;
+}
+
+static uint32_t i440fx_addr_readl(void* opaque, uint32_t addr)
+{
+ I440FXState *s = opaque;
+ return s->config_reg;
+}
+
+static void piix3_set_irq(PCIDevice *pci_dev, void *pic, int irq_num, int level);
+
+PCIBus *i440fx_init(void)
+{
+ PCIBus *b;
+ PCIDevice *d;
+ I440FXState *s;
+
+ s = qemu_mallocz(sizeof(I440FXState));
+ b = pci_register_bus(piix3_set_irq, NULL, 0);
+ s->bus = b;
+
+ register_ioport_write(0xcf8, 4, 4, i440fx_addr_writel, s);
+ register_ioport_read(0xcf8, 4, 4, i440fx_addr_readl, s);
+
+ register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s);
+ register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s);
+ register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s);
+ register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s);
+ register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s);
+ register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s);
+
+ d = pci_register_device(b, "i440FX", sizeof(PCIDevice), 0,
+ NULL, NULL);
+
+ d->config[0x00] = 0x86; // vendor_id
+ d->config[0x01] = 0x80;
+ d->config[0x02] = 0x37; // device_id
+ d->config[0x03] = 0x12;
+ d->config[0x08] = 0x02; // revision
+ d->config[0x0a] = 0x00; // class_sub = host2pci
+ d->config[0x0b] = 0x06; // class_base = PCI_bridge
+ d->config[0x0e] = 0x00; // header_type
+ return b;
+}
+
+/* PIIX3 PCI to ISA bridge */
+
+static PCIDevice *piix3_dev;
+
+/* just used for simpler irq handling. */
+#define PCI_IRQ_WORDS ((PCI_DEVICES_MAX + 31) / 32)
+
+static uint32_t pci_irq_levels[4][PCI_IRQ_WORDS];
+
+/* return the global irq number corresponding to a given device irq
+ pin. We could also use the bus number to have a more precise
+ mapping. */
+static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot_addend;
+ slot_addend = (pci_dev->devfn >> 3) - 1;
+ return (irq_num + slot_addend) & 3;
+}
+
+static inline int get_pci_irq_level(int irq_num)
+{
+ int pic_level;
+#if (PCI_IRQ_WORDS == 2)
+ pic_level = ((pci_irq_levels[irq_num][0] |
+ pci_irq_levels[irq_num][1]) != 0);
+#else
+ {
+ int i;
+ pic_level = 0;
+ for(i = 0; i < PCI_IRQ_WORDS; i++) {
+ if (pci_irq_levels[irq_num][i]) {
+ pic_level = 1;
+ break;
+ }
+ }
+ }
+#endif
+ return pic_level;
+}
+
+static void piix3_set_irq(PCIDevice *pci_dev, void *pic, int irq_num, int level)
+{
+ int irq_index, shift, pic_irq, pic_level;
+ uint32_t *p;
+
+ irq_num = pci_slot_get_pirq(pci_dev, irq_num);
+ irq_index = pci_dev->irq_index;
+ p = &pci_irq_levels[irq_num][irq_index >> 5];
+ shift = (irq_index & 0x1f);
+ *p = (*p & ~(1 << shift)) | (level << shift);
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ /* XXX: optimize */
+ pic_irq = piix3_dev->config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ /* the pic level is the logical OR of all the PCI irqs mapped
+ to it */
+ pic_level = 0;
+ if (pic_irq == piix3_dev->config[0x60])
+ pic_level |= get_pci_irq_level(0);
+ if (pic_irq == piix3_dev->config[0x61])
+ pic_level |= get_pci_irq_level(1);
+ if (pic_irq == piix3_dev->config[0x62])
+ pic_level |= get_pci_irq_level(2);
+ if (pic_irq == piix3_dev->config[0x63])
+ pic_level |= get_pci_irq_level(3);
+ pic_set_irq(pic_irq, pic_level);
+ }
+}
+
+static void piix3_reset(PCIDevice *d)
+{
+ uint8_t *pci_conf = d->config;
+
+ pci_conf[0x04] = 0x07; // master, memory and I/O
+ pci_conf[0x05] = 0x00;
+ pci_conf[0x06] = 0x00;
+ pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+ pci_conf[0x4c] = 0x4d;
+ pci_conf[0x4e] = 0x03;
+ pci_conf[0x4f] = 0x00;
+ pci_conf[0x60] = 0x80;
+ pci_conf[0x69] = 0x02;
+ pci_conf[0x70] = 0x80;
+ pci_conf[0x76] = 0x0c;
+ pci_conf[0x77] = 0x0c;
+ pci_conf[0x78] = 0x02;
+ pci_conf[0x79] = 0x00;
+ pci_conf[0x80] = 0x00;
+ pci_conf[0x82] = 0x00;
+ pci_conf[0xa0] = 0x08;
+ pci_conf[0xa0] = 0x08;
+ pci_conf[0xa2] = 0x00;
+ pci_conf[0xa3] = 0x00;
+ pci_conf[0xa4] = 0x00;
+ pci_conf[0xa5] = 0x00;
+ pci_conf[0xa6] = 0x00;
+ pci_conf[0xa7] = 0x00;
+ pci_conf[0xa8] = 0x0f;
+ pci_conf[0xaa] = 0x00;
+ pci_conf[0xab] = 0x00;
+ pci_conf[0xac] = 0x00;
+ pci_conf[0xae] = 0x00;
+}
+
+int piix3_init(PCIBus *bus)
+{
+ PCIDevice *d;
+ uint8_t *pci_conf;
+
+ d = pci_register_device(bus, "PIIX3", sizeof(PCIDevice),
+ -1, NULL, NULL);
+ register_savevm("PIIX3", 0, 1, generic_pci_save, generic_pci_load, d);
+
+ piix3_dev = d;
+ pci_conf = d->config;
+
+ pci_conf[0x00] = 0x86; // Intel
+ pci_conf[0x01] = 0x80;
+ pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
+ pci_conf[0x03] = 0x70;
+ pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA
+ pci_conf[0x0b] = 0x06; // class_base = PCI_bridge
+ pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic
+
+ piix3_reset(d);
+ return d->devfn;
+}
+
+/***********************************************************/
+/* XXX: the following should be moved to the PC BIOS */
+
+static __attribute__((unused)) uint32_t isa_inb(uint32_t addr)
+{
+ return cpu_inb(NULL, addr);
+}
+
+static void isa_outb(uint32_t val, uint32_t addr)
+{
+ cpu_outb(NULL, addr, val);
+}
+
+static __attribute__((unused)) uint32_t isa_inw(uint32_t addr)
+{
+ return cpu_inw(NULL, addr);
+}
+
+static __attribute__((unused)) void isa_outw(uint32_t val, uint32_t addr)
+{
+ cpu_outw(NULL, addr, val);
+}
+
+static __attribute__((unused)) uint32_t isa_inl(uint32_t addr)
+{
+ return cpu_inl(NULL, addr);
+}
+
+static __attribute__((unused)) void isa_outl(uint32_t val, uint32_t addr)
+{
+ cpu_outl(NULL, addr, val);
+}
+
+static uint32_t pci_bios_io_addr;
+static uint32_t pci_bios_mem_addr;
+/* host irqs corresponding to PCI irqs A-D */
+static uint8_t pci_irqs[4] = { 11, 9, 11, 9 };
+
+static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ pci_data_write(s, addr, val, 4);
+}
+
+static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ pci_data_write(s, addr, val, 2);
+}
+
+static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ pci_data_write(s, addr, val, 1);
+}
+
+static __attribute__((unused)) uint32_t pci_config_readl(PCIDevice *d, uint32_t addr)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ return pci_data_read(s, addr, 4);
+}
+
+static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ return pci_data_read(s, addr, 2);
+}
+
+static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr)
+{
+ PCIBus *s = d->bus;
+ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8);
+ return pci_data_read(s, addr, 1);
+}
+
+static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr)
+{
+ PCIIORegion *r;
+ uint16_t cmd;
+ uint32_t ofs;
+
+ if ( region_num == PCI_ROM_SLOT ) {
+ ofs = 0x30;
+ }else{
+ ofs = 0x10 + region_num * 4;
+ }
+
+ pci_config_writel(d, ofs, addr);
+ r = &d->io_regions[region_num];
+
+ /* enable memory mappings */
+ cmd = pci_config_readw(d, PCI_COMMAND);
+ if ( region_num == PCI_ROM_SLOT )
+ cmd |= 2;
+ else if (r->type & PCI_ADDRESS_SPACE_IO)
+ cmd |= 1;
+ else
+ cmd |= 2;
+ pci_config_writew(d, PCI_COMMAND, cmd);
+}
+
+static void pci_bios_init_device(PCIDevice *d)
+{
+ int class;
+ PCIIORegion *r;
+ uint32_t *paddr;
+ int i, pin, pic_irq, vendor_id, device_id;
+
+ class = pci_config_readw(d, PCI_CLASS_DEVICE);
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ switch(class) {
+ case 0x0101:
+ if (vendor_id == 0x8086 && device_id == 0x7010) {
+ /* PIIX3 IDE */
+ pci_config_writew(d, 0x40, 0x8000); // enable IDE0
+ pci_config_writew(d, 0x42, 0x8000); // enable IDE1
+ goto default_map;
+ } else {
+ /* IDE: we map it as in ISA mode */
+ pci_set_io_region_addr(d, 0, 0x1f0);
+ pci_set_io_region_addr(d, 1, 0x3f4);
+ pci_set_io_region_addr(d, 2, 0x170);
+ pci_set_io_region_addr(d, 3, 0x374);
+ }
+ break;
+ case 0x0300:
+ if (vendor_id != 0x1234)
+ goto default_map;
+ /* VGA: map frame buffer to default Bochs VBE address */
+ pci_set_io_region_addr(d, 0, 0xE0000000);
+ break;
+ case 0x0800:
+ /* PIC */
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ if (vendor_id == 0x1014) {
+ /* IBM */
+ if (device_id == 0x0046 || device_id == 0xFFFF) {
+ /* MPIC & MPIC2 */
+ pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000);
+ }
+ }
+ break;
+ case 0xff00:
+ if (vendor_id == 0x0106b &&
+ (device_id == 0x0017 || device_id == 0x0022)) {
+ /* macio bridge */
+ pci_set_io_region_addr(d, 0, 0x80800000);
+ }
+ break;
+ default:
+ default_map:
+ /* default memory mappings */
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (r->size) {
+ if (r->type & PCI_ADDRESS_SPACE_IO)
+ paddr = &pci_bios_io_addr;
+ else
+ paddr = &pci_bios_mem_addr;
+ *paddr = (*paddr + r->size - 1) & ~(r->size - 1);
+ pci_set_io_region_addr(d, i, *paddr);
+ *paddr += r->size;
+ }
+ }
+ break;
+ }
+
+ /* map the interrupt */
+ pin = pci_config_readb(d, PCI_INTERRUPT_PIN);
+ if (pin != 0) {
+ pin = pci_slot_get_pirq(d, pin - 1);
+ pic_irq = pci_irqs[pin];
+ pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq);
+ }
+}
+
+/*
+ * This function initializes the PCI devices as a normal PCI BIOS
+ * would do. It is provided just in case the BIOS has no support for
+ * PCI.
+ */
+void pci_bios_init(void)
+{
+ int i, irq;
+ uint8_t elcr[2];
+
+ pci_bios_io_addr = 0xc000;
+ pci_bios_mem_addr = 0xf0000000;
+
+ /* activate IRQ mappings */
+ elcr[0] = 0x00;
+ elcr[1] = 0x00;
+ for(i = 0; i < 4; i++) {
+ irq = pci_irqs[i];
+ /* set to trigger level */
+ elcr[irq >> 3] |= (1 << (irq & 7));
+ /* activate irq remapping in PIIX */
+ pci_config_writeb(piix3_dev, 0x60 + i, irq);
+ }
+ isa_outb(elcr[0], 0x4d0);
+ isa_outb(elcr[1], 0x4d1);
+
+ pci_for_each_device(pci_bios_init_device);
+}
+
diff --git a/hw/pl011.c b/hw/pl011.c
new file mode 100644
index 0000000..657f03b
--- /dev/null
+++ b/hw/pl011.c
@@ -0,0 +1,251 @@
+/*
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+typedef struct {
+ uint32_t base;
+ uint32_t readbuff;
+ uint32_t flags;
+ uint32_t lcr;
+ uint32_t cr;
+ uint32_t dmacr;
+ uint32_t int_enabled;
+ uint32_t int_level;
+ uint32_t read_fifo[16];
+ uint32_t ilpr;
+ uint32_t ibrd;
+ uint32_t fbrd;
+ uint32_t ifl;
+ int read_pos;
+ int read_count;
+ int read_trigger;
+ CharDriverState *chr;
+ void *pic;
+ int irq;
+} pl011_state;
+
+#define PL011_INT_TX 0x20
+#define PL011_INT_RX 0x10
+
+#define PL011_FLAG_TXFE 0x80
+#define PL011_FLAG_RXFF 0x40
+#define PL011_FLAG_TXFF 0x20
+#define PL011_FLAG_RXFE 0x10
+
+static const unsigned char pl011_id[] =
+{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl011_update(pl011_state *s)
+{
+ uint32_t flags;
+
+ flags = s->int_level & s->int_enabled;
+ pic_set_irq_new(s->pic, s->irq, flags != 0);
+}
+
+static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ uint32_t c;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl011_id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ s->flags &= ~PL011_FLAG_RXFF;
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == 16)
+ s->read_pos = 0;
+ }
+ if (s->read_count == 0) {
+ s->flags |= PL011_FLAG_RXFE;
+ }
+ if (s->read_count == s->read_trigger - 1)
+ s->int_level &= ~ PL011_INT_RX;
+ pl011_update(s);
+ return c;
+ case 1: /* UARTCR */
+ return 0;
+ case 6: /* UARTFR */
+ return s->flags;
+ case 8: /* UARTILPR */
+ return s->ilpr;
+ case 9: /* UARTIBRD */
+ return s->ibrd;
+ case 10: /* UARTFBRD */
+ return s->fbrd;
+ case 11: /* UARTLCR_H */
+ return s->lcr;
+ case 12: /* UARTCR */
+ return s->cr;
+ case 13: /* UARTIFLS */
+ return s->ifl;
+ case 14: /* UARTIMSC */
+ return s->int_enabled;
+ case 15: /* UARTRIS */
+ return s->int_level;
+ case 16: /* UARTMIS */
+ return s->int_level & s->int_enabled;
+ case 18: /* UARTDMACR */
+ return s->dmacr;
+ default:
+ cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl011_set_read_trigger(pl011_state *s)
+{
+#if 0
+ /* The docs say the RX interrupt is triggered when the FIFO exceeds
+ the threshold. However linux only reads the FIFO in response to an
+ interrupt. Triggering the interrupt when the FIFO is non-empty seems
+ to make things work. */
+ if (s->lcr & 0x10)
+ s->read_trigger = (s->ifl >> 1) & 0x1c;
+ else
+#endif
+ s->read_trigger = 1;
+}
+
+static void pl011_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ unsigned char ch;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ /* ??? Check if transmitter is enabled. */
+ ch = value;
+ if (s->chr)
+ qemu_chr_write(s->chr, &ch, 1);
+ s->int_level |= PL011_INT_TX;
+ pl011_update(s);
+ break;
+ case 1: /* UARTCR */
+ s->cr = value;
+ break;
+ case 8: /* UARTUARTILPR */
+ s->ilpr = value;
+ break;
+ case 9: /* UARTIBRD */
+ s->ibrd = value;
+ break;
+ case 10: /* UARTFBRD */
+ s->fbrd = value;
+ break;
+ case 11: /* UARTLCR_H */
+ s->lcr = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 12: /* UARTCR */
+ /* ??? Need to implement the enable and loopback bits. */
+ s->cr = value;
+ break;
+ case 13: /* UARTIFS */
+ s->ifl = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 14: /* UARTIMSC */
+ s->int_enabled = value;
+ pl011_update(s);
+ break;
+ case 17: /* UARTICR */
+ s->int_level &= ~value;
+ pl011_update(s);
+ break;
+ case 18: /* UARTDMACR */
+ s->dmacr = value;
+ if (value & 3)
+ cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
+ break;
+ default:
+ cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
+ }
+}
+
+static int pl011_can_recieve(void *opaque)
+{
+ pl011_state *s = (pl011_state *)opaque;
+
+ if (s->lcr & 0x10)
+ return s->read_count < 16;
+ else
+ return s->read_count < 1;
+}
+
+static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= 16)
+ slot -= 16;
+ s->read_fifo[slot] = *buf;
+ s->read_count++;
+ s->flags &= ~PL011_FLAG_RXFE;
+ if (s->cr & 0x10 || s->read_count == 16) {
+ s->flags |= PL011_FLAG_RXFF;
+ }
+ if (s->read_count == s->read_trigger) {
+ s->int_level |= PL011_INT_RX;
+ pl011_update(s);
+ }
+}
+
+static void pl011_event(void *opaque, int event)
+{
+ /* ??? Should probably implement break. */
+}
+
+static CPUReadMemoryFunc *pl011_readfn[] = {
+ pl011_read,
+ pl011_read,
+ pl011_read
+};
+
+static CPUWriteMemoryFunc *pl011_writefn[] = {
+ pl011_write,
+ pl011_write,
+ pl011_write
+};
+
+void pl011_init(uint32_t base, void *pic, int irq,
+ CharDriverState *chr)
+{
+ int iomemtype;
+ pl011_state *s;
+
+ s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
+ iomemtype = cpu_register_io_memory(0, pl011_readfn,
+ pl011_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ s->chr = chr;
+ s->read_trigger = 1;
+ s->ifl = 0x12;
+ s->cr = 0x300;
+ s->flags = 0x90;
+ if (chr){
+ qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s);
+ qemu_chr_add_event_handler(chr, pl011_event);
+ }
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/pl050.c b/hw/pl050.c
new file mode 100644
index 0000000..20451e5
--- /dev/null
+++ b/hw/pl050.c
@@ -0,0 +1,127 @@
+/*
+ * Arm PrimeCell PL050 Keyboard / Mouse Interface
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+typedef struct {
+ void *dev;
+ uint32_t base;
+ uint32_t cr;
+ uint32_t clk;
+ uint32_t last;
+ void *pic;
+ int pending;
+ int irq;
+ int is_mouse;
+} pl050_state;
+
+static const unsigned char pl050_id[] =
+{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl050_update(void *opaque, int level)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ int raise;
+
+ s->pending = level;
+ raise = (s->pending && (s->cr & 0x10) != 0)
+ || (s->cr & 0x08) != 0;
+ pic_set_irq_new(s->pic, s->irq, raise);
+}
+
+static uint32_t pl050_read(void *opaque, target_phys_addr_t offset)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000)
+ return pl050_id[(offset - 0xfe0) >> 2];
+
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ return s->cr;
+ case 1: /* KMISTAT */
+ /* KMIC and KMID bits not implemented. */
+ if (s->pending) {
+ return 0x10;
+ } else {
+ return 0;
+ }
+ case 2: /* KMIDATA */
+ if (s->pending)
+ s->last = ps2_read_data(s->dev);
+ return s->last;
+ case 3: /* KMICLKDIV */
+ return s->clk;
+ case 4: /* KMIIR */
+ return s->pending | 2;
+ default:
+ cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl050_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ s->cr = value;
+ pl050_update(s, s->pending);
+ /* ??? Need to implement the enable/disable bit. */
+ break;
+ case 2: /* KMIDATA */
+ /* ??? This should toggle the TX interrupt line. */
+ /* ??? This means kbd/mouse can block each other. */
+ if (s->is_mouse) {
+ ps2_write_mouse(s->dev, value);
+ } else {
+ ps2_write_keyboard(s->dev, value);
+ }
+ break;
+ case 3: /* KMICLKDIV */
+ s->clk = value;
+ return;
+ default:
+ cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", offset);
+ }
+}
+static CPUReadMemoryFunc *pl050_readfn[] = {
+ pl050_read,
+ pl050_read,
+ pl050_read
+};
+
+static CPUWriteMemoryFunc *pl050_writefn[] = {
+ pl050_write,
+ pl050_write,
+ pl050_write
+};
+
+void pl050_init(uint32_t base, void *pic, int irq, int is_mouse)
+{
+ int iomemtype;
+ pl050_state *s;
+
+ s = (pl050_state *)qemu_mallocz(sizeof(pl050_state));
+ iomemtype = cpu_register_io_memory(0, pl050_readfn,
+ pl050_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ s->is_mouse = is_mouse;
+ if (is_mouse)
+ s->dev = ps2_mouse_init(pl050_update, s);
+ else
+ s->dev = ps2_kbd_init(pl050_update, s);
+ /* ??? Save/restore. */
+}
+
diff --git a/hw/pl080.c b/hw/pl080.c
new file mode 100644
index 0000000..49996ca
--- /dev/null
+++ b/hw/pl080.c
@@ -0,0 +1,328 @@
+/*
+ * Arm PrimeCell PL080 DMA controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define PL080_NUM_CHANNELS 8
+#define PL080_CONF_E 0x1
+#define PL080_CONF_M1 0x2
+#define PL080_CONF_M2 0x4
+
+#define PL080_CCONF_H 0x40000
+#define PL080_CCONF_A 0x20000
+#define PL080_CCONF_L 0x10000
+#define PL080_CCONF_ITC 0x08000
+#define PL080_CCONF_IE 0x04000
+#define PL080_CCONF_E 0x00001
+
+#define PL080_CCTRL_I 0x80000000
+#define PL080_CCTRL_DI 0x08000000
+#define PL080_CCTRL_SI 0x04000000
+#define PL080_CCTRL_D 0x02000000
+#define PL080_CCTRL_S 0x01000000
+
+typedef struct {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t lli;
+ uint32_t ctrl;
+ uint32_t conf;
+} pl080_channel;
+
+typedef struct {
+ uint32_t base;
+ uint8_t tc_int;
+ uint8_t tc_mask;
+ uint8_t err_int;
+ uint8_t err_mask;
+ uint32_t conf;
+ uint32_t sync;
+ uint32_t req_single;
+ uint32_t req_burst;
+ pl080_channel chan[PL080_NUM_CHANNELS];
+ /* Flag to avoid recursive DMA invocations. */
+ int running;
+ void *pic;
+ int irq;
+} pl080_state;
+
+static const unsigned char pl080_id[] =
+{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl080_update(pl080_state *s)
+{
+ if ((s->tc_int & s->tc_mask)
+ || (s->err_int & s->err_mask))
+ pic_set_irq_new(s->pic, s->irq, 1);
+ else
+ pic_set_irq_new(s->pic, s->irq, 1);
+}
+
+static void pl080_run(pl080_state *s)
+{
+ int c;
+ int flow;
+ pl080_channel *ch;
+ int swidth;
+ int dwidth;
+ int xsize;
+ int n;
+ int src_id;
+ int dest_id;
+ int size;
+ char buff[4];
+ uint32_t req;
+
+ s->tc_mask = 0;
+ for (c = 0; c < PL080_NUM_CHANNELS; c++) {
+ if (s->chan[c].conf & PL080_CCONF_ITC)
+ s->tc_mask |= 1 << c;
+ if (s->chan[c].conf & PL080_CCONF_IE)
+ s->err_mask |= 1 << c;
+ }
+
+ if ((s->conf & PL080_CONF_E) == 0)
+ return;
+
+cpu_abort(cpu_single_env, "DMA active\n");
+ /* If we are already in the middle of a DMA operation then indicate that
+ there may be new DMA requests and return immediately. */
+ if (s->running) {
+ s->running++;
+ return;
+ }
+ s->running = 1;
+ while (s->running) {
+ for (c = 0; c < PL080_NUM_CHANNELS; c++) {
+ ch = &s->chan[c];
+again:
+ /* Test if thiws channel has any pending DMA requests. */
+ if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
+ != PL080_CCONF_E)
+ continue;
+ flow = (ch->conf >> 11) & 7;
+ if (flow >= 4) {
+ cpu_abort(cpu_single_env,
+ "pl080_run: Peripheral flow control not implemented\n");
+ }
+ src_id = (ch->conf >> 1) & 0x1f;
+ dest_id = (ch->conf >> 6) & 0x1f;
+ size = ch->ctrl & 0xfff;
+ req = s->req_single | s->req_burst;
+ switch (flow) {
+ case 0:
+ break;
+ case 1:
+ if ((req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ case 2:
+ if ((req & (1u << src_id)) == 0)
+ size = 0;
+ break;
+ case 3:
+ if ((req & (1u << src_id)) == 0
+ || (req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ }
+ if (!size)
+ continue;
+
+ /* Transfer one element. */
+ /* ??? Should transfer multiple elements for a burst request. */
+ /* ??? Unclear what the proper behavior is when source and
+ destination widths are different. */
+ swidth = 1 << ((ch->ctrl >> 18) & 7);
+ dwidth = 1 << ((ch->ctrl >> 21) & 7);
+ for (n = 0; n < dwidth; n+= swidth) {
+ cpu_physical_memory_read(ch->src, buff + n, swidth);
+ if (ch->ctrl & PL080_CCTRL_SI)
+ ch->src += swidth;
+ }
+ xsize = (dwidth < swidth) ? swidth : dwidth;
+ /* ??? This may pad the value incorrectly for dwidth < 32. */
+ for (n = 0; n < xsize; n += dwidth) {
+ cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
+ if (ch->ctrl & PL080_CCTRL_DI)
+ ch->dest += swidth;
+ }
+
+ size--;
+ ch->ctrl = (ch->ctrl & 0xfffff000) | size;
+ if (size == 0) {
+ /* Transfer complete. */
+ if (ch->lli) {
+ ch->src = ldl_phys(ch->lli);
+ ch->dest = ldl_phys(ch->lli + 4);
+ ch->ctrl = ldl_phys(ch->lli + 12);
+ ch->lli = ldl_phys(ch->lli + 8);
+ } else {
+ ch->conf &= ~PL080_CCONF_E;
+ }
+ if (ch->ctrl & PL080_CCTRL_I) {
+ s->tc_int |= 1 << c;
+ }
+ }
+ goto again;
+ }
+ if (--s->running)
+ s->running = 1;
+ }
+}
+
+static uint32_t pl080_read(void *opaque, target_phys_addr_t offset)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ uint32_t i;
+ uint32_t mask;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl080_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ return s->chan[i].src;
+ case 1: /* DestAddr */
+ return s->chan[i].dest;
+ case 2: /* LLI */
+ return s->chan[i].lli;
+ case 3: /* Control */
+ return s->chan[i].ctrl;
+ case 4: /* Configuration */
+ return s->chan[i].conf;
+ default:
+ goto bad_offset;
+ }
+ }
+ switch (offset >> 2) {
+ case 0: /* IntStatus */
+ return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
+ case 1: /* IntTCStatus */
+ return (s->tc_int & s->tc_mask);
+ case 3: /* IntErrorStatus */
+ return (s->err_int & s->err_mask);
+ case 5: /* RawIntTCStatus */
+ return s->tc_int;
+ case 6: /* RawIntErrorStatus */
+ return s->err_int;
+ case 7: /* EnbldChns */
+ mask = 0;
+ for (i = 0; i < PL080_NUM_CHANNELS; i++) {
+ if (s->chan[i].conf & PL080_CCONF_E)
+ mask |= 1 << i;
+ }
+ return mask;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ return 0;
+ case 12: /* Configuration */
+ return s->conf;
+ case 13: /* Sync */
+ return s->sync;
+ default:
+ bad_offset:
+ cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl080_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ int i;
+
+ offset -= s->base;
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ s->chan[i].src = value;
+ break;
+ case 1: /* DestAddr */
+ s->chan[i].dest = value;
+ break;
+ case 2: /* LLI */
+ s->chan[i].lli = value;
+ break;
+ case 3: /* Control */
+ s->chan[i].ctrl = value;
+ break;
+ case 4: /* Configuration */
+ s->chan[i].conf = value;
+ pl080_run(s);
+ break;
+ }
+ }
+ switch (offset >> 2) {
+ case 2: /* IntTCClear */
+ s->tc_int &= ~value;
+ break;
+ case 4: /* IntErrorClear */
+ s->err_int &= ~value;
+ break;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n");
+ break;
+ case 12: /* Configuration */
+ s->conf = value;
+ if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
+ cpu_abort(cpu_single_env,
+ "pl080_write: Big-endian DMA not implemented\n");
+ }
+ pl080_run(s);
+ break;
+ case 13: /* Sync */
+ s->sync = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset);
+ }
+ pl080_update(s);
+}
+
+static CPUReadMemoryFunc *pl080_readfn[] = {
+ pl080_read,
+ pl080_read,
+ pl080_read
+};
+
+static CPUWriteMemoryFunc *pl080_writefn[] = {
+ pl080_write,
+ pl080_write,
+ pl080_write
+};
+
+void *pl080_init(uint32_t base, void *pic, int irq)
+{
+ int iomemtype;
+ pl080_state *s;
+
+ s = (pl080_state *)qemu_mallocz(sizeof(pl080_state));
+ iomemtype = cpu_register_io_memory(0, pl080_readfn,
+ pl080_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ /* ??? Save/restore. */
+ return s;
+}
+
diff --git a/hw/pl110.c b/hw/pl110.c
new file mode 100644
index 0000000..ecebe35
--- /dev/null
+++ b/hw/pl110.c
@@ -0,0 +1,420 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU LGPL
+ */
+
+#include "vl.h"
+
+#define PL110_CR_EN 0x001
+#define PL110_CR_BEBO 0x200
+#define PL110_CR_BEPO 0x400
+#define PL110_CR_PWR 0x800
+
+enum pl110_bppmode
+{
+ BPP_1,
+ BPP_2,
+ BPP_4,
+ BPP_8,
+ BPP_16,
+ BPP_32
+};
+
+typedef struct {
+ uint32_t base;
+ DisplayState *ds;
+ /* The Versatile/PB uses a slightly modified PL110 controller. */
+ int versatile;
+ void *pic;
+ uint32_t timing[4];
+ uint32_t cr;
+ uint32_t upbase;
+ uint32_t lpbase;
+ uint32_t int_status;
+ uint32_t int_mask;
+ int cols;
+ int rows;
+ enum pl110_bppmode bpp;
+ int invalidate;
+ uint32_t pallette[256];
+ uint32_t raw_pallette[128];
+ int irq;
+} pl110_state;
+
+static const unsigned char pl110_id[] =
+{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
+ has a different ID. However Linux only looks for the normal ID. */
+#if 0
+static const unsigned char pl110_versatile_id[] =
+{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#else
+#define pl110_versatile_id pl110_id
+#endif
+
+static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
+}
+
+static inline uint32_t rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+}
+
+static inline uint32_t rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+}
+
+static inline uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
+{
+ return (r << 16) | (g << 8) | b;
+}
+
+static inline uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
+{
+ return (r << 16) | (g << 8) | b;
+}
+
+typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int);
+
+#define BITS 8
+#include "pl110_template.h"
+#define BITS 15
+#include "pl110_template.h"
+#define BITS 16
+#include "pl110_template.h"
+#define BITS 24
+#include "pl110_template.h"
+#define BITS 32
+#include "pl110_template.h"
+
+static int pl110_enabled(pl110_state *s)
+{
+ return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
+}
+
+static void pl110_update_display(void *opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ drawfn* fntable;
+ drawfn fn;
+ uint32_t *pallette;
+ uint32_t addr;
+ uint32_t base;
+ int dest_width;
+ int src_width;
+ uint8_t *dest;
+ uint8_t *src;
+ int first, last = 0;
+ int dirty, new_dirty;
+ int i;
+
+ if (!pl110_enabled(s))
+ return;
+
+ switch (s->ds->depth) {
+ case 0:
+ return;
+ case 8:
+ fntable = pl110_draw_fn_8;
+ dest_width = 1;
+ break;
+ case 15:
+ fntable = pl110_draw_fn_15;
+ dest_width = 2;
+ break;
+ case 16:
+ fntable = pl110_draw_fn_16;
+ dest_width = 2;
+ break;
+ case 24:
+ fntable = pl110_draw_fn_24;
+ dest_width = 3;
+ break;
+ case 32:
+ fntable = pl110_draw_fn_32;
+ dest_width = 4;
+ break;
+ default:
+ fprintf(stderr, "pl110: Bad color depth\n");
+ exit(1);
+ }
+ if (s->cr & PL110_CR_BEBO)
+ fn = fntable[s->bpp + 6];
+ else if (s->cr & PL110_CR_BEPO)
+ fn = fntable[s->bpp + 12];
+ else
+ fn = fntable[s->bpp];
+
+ src_width = s->cols;
+ switch (s->bpp) {
+ case BPP_1:
+ src_width >>= 3;
+ break;
+ case BPP_2:
+ src_width >>= 2;
+ break;
+ case BPP_4:
+ src_width >>= 1;
+ break;
+ case BPP_8:
+ break;
+ case BPP_16:
+ src_width <<= 1;
+ break;
+ case BPP_32:
+ src_width <<= 2;
+ break;
+ }
+ dest_width *= s->cols;
+ pallette = s->pallette;
+ base = s->upbase;
+ /* HACK: Arm aliases physical memory at 0x80000000. */
+ if (base > 0x80000000)
+ base -= 0x80000000;
+ src = phys_ram_base + base;
+ dest = s->ds->data;
+ first = -1;
+ addr = base;
+
+ dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
+ for (i = 0; i < s->rows; i++) {
+ new_dirty = 0;
+ if ((addr & TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) {
+ uint32_t tmp;
+ for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) {
+ new_dirty |= cpu_physical_memory_get_dirty(addr + tmp,
+ VGA_DIRTY_FLAG);
+ }
+ }
+
+ if (dirty || new_dirty || s->invalidate) {
+ fn(pallette, dest, src, s->cols);
+ if (first == -1)
+ first = i;
+ last = i;
+ }
+ dirty = new_dirty;
+ addr += src_width;
+ dest += dest_width;
+ src += src_width;
+ }
+ if (first < 0)
+ return;
+
+ s->invalidate = 0;
+ cpu_physical_memory_reset_dirty(base + first * src_width,
+ base + (last + 1) * src_width,
+ VGA_DIRTY_FLAG);
+ dpy_update(s->ds, 0, first, s->cols, last - first + 1);
+}
+
+static void pl110_invalidate_display(void * opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ s->invalidate = 1;
+}
+
+static void pl110_update_pallette(pl110_state *s, int n)
+{
+ int i;
+ uint32_t raw;
+ unsigned int r, g, b;
+
+ raw = s->raw_pallette[n];
+ n <<= 1;
+ for (i = 0; i < 2; i++) {
+ r = (raw & 0x1f) << 3;
+ raw >>= 5;
+ g = (raw & 0x1f) << 3;
+ raw >>= 5;
+ b = (raw & 0x1f) << 3;
+ /* The I bit is ignored. */
+ raw >>= 6;
+ switch (s->ds->depth) {
+ case 8:
+ s->pallette[n] = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ s->pallette[n] = rgb_to_pixel15(r, g, b);
+ break;
+ case 16:
+ s->pallette[n] = rgb_to_pixel16(r, g, b);
+ break;
+ case 24:
+ case 32:
+ s->pallette[n] = rgb_to_pixel32(r, g, b);
+ break;
+ }
+ n++;
+ }
+}
+
+static void pl110_resize(pl110_state *s, int width, int height)
+{
+ if (width != s->cols || height != s->rows) {
+ if (pl110_enabled(s)) {
+ dpy_resize(s->ds, width, height);
+ }
+ }
+ s->cols = width;
+ s->rows = height;
+}
+
+/* Update interrupts. */
+static void pl110_update(pl110_state *s)
+{
+ /* TODO: Implement interrupts. */
+}
+
+static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
+{
+ pl110_state *s = (pl110_state *)opaque;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ if (s->versatile)
+ return pl110_versatile_id[(offset - 0xfe0) >> 2];
+ else
+ return pl110_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x400) {
+ return s->raw_pallette[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ return s->timing[0];
+ case 1: /* LCDTiming1 */
+ return s->timing[1];
+ case 2: /* LCDTiming2 */
+ return s->timing[2];
+ case 3: /* LCDTiming3 */
+ return s->timing[3];
+ case 4: /* LCDUPBASE */
+ return s->upbase;
+ case 5: /* LCDLPBASE */
+ return s->lpbase;
+ case 6: /* LCDIMSC */
+ return s->int_mask;
+ case 7: /* LCDControl */
+ return s->cr;
+ case 8: /* LCDRIS */
+ return s->int_status;
+ case 9: /* LCDMIS */
+ return s->int_status & s->int_mask;
+ case 11: /* LCDUPCURR */
+ /* TODO: Implement vertical refresh. */
+ return s->upbase;
+ case 12: /* LCDLPCURR */
+ return s->lpbase;
+ default:
+ cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl110_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ int n;
+
+ /* For simplicity invalidate the display whenever a control register
+ is writen to. */
+ s->invalidate = 1;
+ offset -= s->base;
+ if (offset >= 0x200 && offset < 0x400) {
+ /* Pallette. */
+ n = (offset - 0x200) >> 2;
+ s->raw_pallette[(offset - 0x200) >> 2] = val;
+ pl110_update_pallette(s, n);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ s->timing[0] = val;
+ n = ((val & 0xfc) + 4) * 4;
+ pl110_resize(s, n, s->rows);
+ break;
+ case 1: /* LCDTiming1 */
+ s->timing[1] = val;
+ n = (val & 0x3ff) + 1;
+ pl110_resize(s, s->cols, n);
+ break;
+ case 2: /* LCDTiming2 */
+ s->timing[2] = val;
+ break;
+ case 3: /* LCDTiming3 */
+ s->timing[3] = val;
+ break;
+ case 4: /* LCDUPBASE */
+ s->upbase = val;
+ break;
+ case 5: /* LCDLPBASE */
+ s->lpbase = val;
+ break;
+ case 6: /* LCDIMSC */
+ if (s->versatile)
+ goto control;
+ imsc:
+ s->int_mask = val;
+ pl110_update(s);
+ break;
+ case 7: /* LCDControl */
+ if (s->versatile)
+ goto imsc;
+ control:
+ s->cr = val;
+ s->bpp = (val >> 1) & 7;
+ if (pl110_enabled(s)) {
+ dpy_resize(s->ds, s->cols, s->rows);
+ }
+ break;
+ case 10: /* LCDICR */
+ s->int_status &= ~val;
+ pl110_update(s);
+ break;
+ default:
+ cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *pl110_readfn[] = {
+ pl110_read,
+ pl110_read,
+ pl110_read
+};
+
+static CPUWriteMemoryFunc *pl110_writefn[] = {
+ pl110_write,
+ pl110_write,
+ pl110_write
+};
+
+void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq,
+ int versatile)
+{
+ pl110_state *s;
+ int iomemtype;
+
+ s = (pl110_state *)qemu_mallocz(sizeof(pl110_state));
+ iomemtype = cpu_register_io_memory(0, pl110_readfn,
+ pl110_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+ s->ds = ds;
+ s->versatile = versatile;
+ s->pic = pic;
+ s->irq = irq;
+ graphic_console_init(ds, pl110_update_display, pl110_invalidate_display,
+ NULL, s);
+ /* ??? Save/restore. */
+ return s;
+}
diff --git a/hw/pl110_template.h b/hw/pl110_template.h
new file mode 100644
index 0000000..db05035
--- /dev/null
+++ b/hw/pl110_template.h
@@ -0,0 +1,252 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU LGPL
+ *
+ * Framebuffer format conversion routines.
+ */
+
+#ifndef ORDER
+
+#if BITS == 8
+#define COPY_PIXEL(to, from) *(to++) = from
+#elif BITS == 15 || BITS == 16
+#define COPY_PIXEL(to, from) *(uint16_t *)to = from; to += 2;
+#elif BITS == 24
+#define COPY_PIXEL(to, from) \
+ *(to++) = from; *(to++) = (from) >> 8; *(to++) = (from) >> 16
+#elif BITS == 32
+#define COPY_PIXEL(to, from) *(uint32_t *)to = from; to += 4;
+#else
+#error unknown bit depth
+#endif
+
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
+#include "pl110_template.h"
+#define ORDER 2
+#include "pl110_template.h"
+
+static drawfn glue(pl110_draw_fn_,BITS)[18] =
+{
+ glue(pl110_draw_line1_lblp,BITS),
+ glue(pl110_draw_line2_lblp,BITS),
+ glue(pl110_draw_line4_lblp,BITS),
+ glue(pl110_draw_line8_lblp,BITS),
+ glue(pl110_draw_line16_lblp,BITS),
+ glue(pl110_draw_line32_lblp,BITS),
+
+ glue(pl110_draw_line1_bbbp,BITS),
+ glue(pl110_draw_line2_bbbp,BITS),
+ glue(pl110_draw_line4_bbbp,BITS),
+ glue(pl110_draw_line8_bbbp,BITS),
+ glue(pl110_draw_line16_bbbp,BITS),
+ glue(pl110_draw_line32_bbbp,BITS),
+
+ glue(pl110_draw_line1_lbbp,BITS),
+ glue(pl110_draw_line2_lbbp,BITS),
+ glue(pl110_draw_line4_lbbp,BITS),
+ glue(pl110_draw_line8_lbbp,BITS),
+ glue(pl110_draw_line16_lbbp,BITS),
+ glue(pl110_draw_line32_lbbp,BITS)
+};
+
+#undef BITS
+#undef COPY_PIXEL
+
+#else
+
+#if ORDER == 0
+#define NAME glue(lblp, BITS)
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#elif ORDER == 1
+#define NAME glue(bbbp, BITS)
+#ifndef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#else
+#define SWAP_PIXELS 1
+#define NAME glue(lbbp, BITS)
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#endif
+
+#define FN_2(x, y) FN(x, y) FN(x+1, y)
+#define FN_4(x, y) FN_2(x, y) FN_2(x+1, y)
+#define FN_8(y) FN_4(0, y) FN_4(4, y)
+
+static void glue(pl110_draw_line1_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 7 - (x))) & 1]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x) + y)) & 1]);
+#endif
+#ifdef SWAP_WORDS
+ FN_8(24)
+ FN_8(16)
+ FN_8(8)
+ FN_8(0)
+#else
+ FN_8(0)
+ FN_8(8)
+ FN_8(16)
+ FN_8(24)
+#endif
+#undef FN
+ width -= 32;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line2_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 6 - (x)*2)) & 3]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*2 + y)) & 3]);
+#endif
+#ifdef SWAP_WORDS
+ FN_4(0, 24)
+ FN_4(0, 16)
+ FN_4(0, 8)
+ FN_4(0, 0)
+#else
+ FN_4(0, 0)
+ FN_4(0, 8)
+ FN_4(0, 16)
+ FN_4(0, 24)
+#endif
+#undef FN
+ width -= 16;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line4_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 4 - (x)*4)) & 0xf]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*4 + y)) & 0xf]);
+#endif
+#ifdef SWAP_WORDS
+ FN_2(0, 24)
+ FN_2(0, 16)
+ FN_2(0, 8)
+ FN_2(0, 0)
+#else
+ FN_2(0, 0)
+ FN_2(0, 8)
+ FN_2(0, 16)
+ FN_2(0, 24)
+#endif
+#undef FN
+ width -= 8;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line8_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#define FN(x) COPY_PIXEL(d, pallette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+ FN(24)
+ FN(16)
+ FN(8)
+ FN(0)
+#else
+ FN(0)
+ FN(8)
+ FN(16)
+ FN(24)
+#endif
+#undef FN
+ width -= 4;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line16_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+#if 0
+ r = data & 0x1f;
+ data >>= 5;
+ g = data & 0x3f;
+ data >>= 6;
+ b = data & 0x1f;
+ data >>= 5;
+#else
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line32_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ r = data & 0xff;
+ g = (data >> 8) & 0xff;
+ b = (data >> 16) & 0xff;
+#else
+ r = (data >> 24) & 0xff;
+ g = (data >> 16) & 0xff;
+ b = (data >> 8) & 0xff;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ width--;
+ src += 4;
+ }
+}
+
+#undef SWAP_PIXELS
+#undef NAME
+#undef SWAP_WORDS
+#undef ORDER
+
+#endif
diff --git a/hw/pl190.c b/hw/pl190.c
new file mode 100644
index 0000000..55c7180
--- /dev/null
+++ b/hw/pl190.c
@@ -0,0 +1,252 @@
+/*
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+/* The number of virtual priority levels. 16 user vectors plus the
+ unvectored IRQ. Chained interrupts would require an additional level
+ if implemented. */
+
+#define PL190_NUM_PRIO 17
+
+typedef struct {
+ arm_pic_handler handler;
+ uint32_t base;
+ DisplayState *ds;
+ uint32_t level;
+ uint32_t soft_level;
+ uint32_t irq_enable;
+ uint32_t fiq_select;
+ uint32_t default_addr;
+ uint8_t vect_control[16];
+ uint32_t vect_addr[PL190_NUM_PRIO];
+ /* Mask containing interrupts with higher priority than this one. */
+ uint32_t prio_mask[PL190_NUM_PRIO + 1];
+ int protected;
+ /* Current priority level. */
+ int priority;
+ int prev_prio[PL190_NUM_PRIO];
+ void *parent;
+ int irq;
+ int fiq;
+} pl190_state;
+
+static const unsigned char pl190_id[] =
+{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
+
+static inline uint32_t pl190_irq_level(pl190_state *s)
+{
+ return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
+}
+
+/* Update interrupts. */
+static void pl190_update(pl190_state *s)
+{
+ uint32_t level = pl190_irq_level(s);
+ int set;
+
+ set = (level & s->prio_mask[s->priority]) != 0;
+ pic_set_irq_new(s->parent, s->irq, set);
+ set = ((s->level | s->soft_level) & s->fiq_select) != 0;
+ pic_set_irq_new(s->parent, s->fiq, set);
+}
+
+static void pl190_set_irq(void *opaque, int irq, int level)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ pl190_update(s);
+}
+
+static void pl190_update_vectors(pl190_state *s)
+{
+ uint32_t mask;
+ int i;
+ int n;
+
+ mask = 0;
+ for (i = 0; i < 16; i++)
+ {
+ s->prio_mask[i] = mask;
+ if (s->vect_control[i] & 0x20)
+ {
+ n = s->vect_control[i] & 0x1f;
+ mask |= 1 << n;
+ }
+ }
+ s->prio_mask[16] = mask;
+ pl190_update(s);
+}
+
+static uint32_t pl190_read(void *opaque, target_phys_addr_t offset)
+{
+ pl190_state *s = (pl190_state *)opaque;
+ int i;
+
+ offset -= s->base;
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl190_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x140) {
+ return s->vect_addr[(offset - 0x100) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ return s->vect_control[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* IRQSTATUS */
+ return pl190_irq_level(s);
+ case 1: /* FIQSATUS */
+ return (s->level | s->soft_level) & s->fiq_select;
+ case 2: /* RAWINTR */
+ return s->level | s->soft_level;
+ case 3: /* INTSELECT */
+ return s->fiq_select;
+ case 4: /* INTENABLE */
+ return s->irq_enable;
+ case 6: /* SOFTINT */
+ return s->soft_level;
+ case 8: /* PROTECTION */
+ return s->protected;
+ case 12: /* VECTADDR */
+ /* Read vector address at the start of an ISR. Increases the
+ current priority level to that of the current interrupt. */
+ for (i = 0; i < s->priority; i++)
+ {
+ if ((s->level | s->soft_level) & s->prio_mask[i])
+ break;
+ }
+ /* Reading this value with no pending interrupts is undefined.
+ We return the default address. */
+ if (i == PL190_NUM_PRIO)
+ return s->vect_addr[16];
+ if (i < s->priority)
+ {
+ s->prev_prio[i] = s->priority;
+ s->priority = i;
+ pl190_update(s);
+ }
+ return s->vect_addr[s->priority];
+ case 13: /* DEFVECTADDR */
+ return s->vect_addr[16];
+ default:
+ cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ offset -= s->base;
+ if (offset >= 0x100 && offset < 0x140) {
+ s->vect_addr[(offset - 0x100) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ s->vect_control[(offset - 0x200) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* SELECT */
+ /* This is a readonly register, but linux tries to write to it
+ anyway. Ignore the write. */
+ break;
+ case 3: /* INTSELECT */
+ s->fiq_select = val;
+ break;
+ case 4: /* INTENABLE */
+ s->irq_enable |= val;
+ break;
+ case 5: /* INTENCLEAR */
+ s->irq_enable &= ~val;
+ break;
+ case 6: /* SOFTINT */
+ s->soft_level |= val;
+ break;
+ case 7: /* SOFTINTCLEAR */
+ s->soft_level &= ~val;
+ break;
+ case 8: /* PROTECTION */
+ /* TODO: Protection (supervisor only access) is not implemented. */
+ s->protected = val & 1;
+ break;
+ case 12: /* VECTADDR */
+ /* Restore the previous priority level. The value written is
+ ignored. */
+ if (s->priority < PL190_NUM_PRIO)
+ s->priority = s->prev_prio[s->priority];
+ break;
+ case 13: /* DEFVECTADDR */
+ s->default_addr = val;
+ break;
+ case 0xc0: /* ITCR */
+ if (val)
+ cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n");
+ break;
+ default:
+ cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", offset);
+ return;
+ }
+ pl190_update(s);
+}
+
+static CPUReadMemoryFunc *pl190_readfn[] = {
+ pl190_read,
+ pl190_read,
+ pl190_read
+};
+
+static CPUWriteMemoryFunc *pl190_writefn[] = {
+ pl190_write,
+ pl190_write,
+ pl190_write
+};
+
+void pl190_reset(pl190_state *s)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ s->vect_addr[i] = 0;
+ s->vect_control[i] = 0;
+ }
+ s->vect_addr[16] = 0;
+ s->prio_mask[17] = 0xffffffff;
+ s->priority = PL190_NUM_PRIO;
+ pl190_update_vectors(s);
+}
+
+void *pl190_init(uint32_t base, void *parent, int irq, int fiq)
+{
+ pl190_state *s;
+ int iomemtype;
+
+ s = (pl190_state *)qemu_mallocz(sizeof(pl190_state));
+ iomemtype = cpu_register_io_memory(0, pl190_readfn,
+ pl190_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->handler = pl190_set_irq;
+ s->base = base;
+ s->parent = parent;
+ s->irq = irq;
+ s->fiq = fiq;
+ pl190_reset(s);
+ /* ??? Save/restore. */
+ return s;
+}
diff --git a/hw/ppc.c b/hw/ppc.c
new file mode 100644
index 0000000..3743ad7
--- /dev/null
+++ b/hw/ppc.c
@@ -0,0 +1,428 @@
+/*
+ * QEMU generic PPC hardware System Emulator
+ *
+ * Copyright (c) 2003-2004 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "m48t59.h"
+
+/*****************************************************************************/
+/* PPC time base and decrementer emulation */
+//#define DEBUG_TB
+
+struct ppc_tb_t {
+ /* Time base management */
+ int64_t tb_offset; /* Compensation */
+ uint32_t tb_freq; /* TB frequency */
+ /* Decrementer management */
+ uint64_t decr_next; /* Tick for next decr interrupt */
+ struct QEMUTimer *decr_timer;
+};
+
+static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
+{
+ /* TB time in tb periods */
+ return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
+ tb_env->tb_freq, ticks_per_sec);
+}
+
+uint32_t cpu_ppc_load_tbl (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env);
+#ifdef DEBUG_TB
+ {
+ static int last_time;
+ int now;
+ now = time(NULL);
+ if (last_time != now) {
+ last_time = now;
+ printf("%s: tb=0x%016lx %d %08lx\n",
+ __func__, tb, now, tb_env->tb_offset);
+ }
+ }
+#endif
+
+ return tb & 0xFFFFFFFF;
+}
+
+uint32_t cpu_ppc_load_tbu (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env);
+#ifdef DEBUG_TB
+ printf("%s: tb=0x%016lx\n", __func__, tb);
+#endif
+ return tb >> 32;
+}
+
+static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
+{
+ tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
+ - qemu_get_clock(vm_clock);
+#ifdef DEBUG_TB
+ printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
+#endif
+}
+
+void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ cpu_ppc_store_tb(tb_env,
+ ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
+}
+
+void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ cpu_ppc_store_tb(tb_env,
+ ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
+}
+
+uint32_t cpu_ppc_load_decr (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint32_t decr;
+ int64_t diff;
+
+ diff = tb_env->decr_next - qemu_get_clock(vm_clock);
+ if (diff >= 0)
+ decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec);
+ else
+ decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec);
+#if defined(DEBUG_TB)
+ printf("%s: 0x%08x\n", __func__, decr);
+#endif
+ return decr;
+}
+
+/* When decrementer expires,
+ * all we need to do is generate or queue a CPU exception
+ */
+static inline void cpu_ppc_decr_excp (CPUState *env)
+{
+ /* Raise it */
+#ifdef DEBUG_TB
+ printf("raise decrementer exception\n");
+#endif
+ cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+}
+
+static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
+ uint32_t value, int is_excp)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t now, next;
+
+#ifdef DEBUG_TB
+ printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
+#endif
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
+ if (is_excp)
+ next += tb_env->decr_next - now;
+ if (next == now)
+ next++;
+ tb_env->decr_next = next;
+ /* Adjust timer */
+ qemu_mod_timer(tb_env->decr_timer, next);
+ /* If we set a negative value and the decrementer was positive,
+ * raise an exception.
+ */
+ if ((value & 0x80000000) && !(decr & 0x80000000))
+ cpu_ppc_decr_excp(env);
+}
+
+void cpu_ppc_store_decr (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
+}
+
+static void cpu_ppc_decr_cb (void *opaque)
+{
+ _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
+}
+
+/* Set up (once) timebase frequency (in Hz) */
+ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
+{
+ ppc_tb_t *tb_env;
+
+ tb_env = qemu_mallocz(sizeof(ppc_tb_t));
+ if (tb_env == NULL)
+ return NULL;
+ env->tb_env = tb_env;
+ if (tb_env->tb_freq == 0 || 1) {
+ tb_env->tb_freq = freq;
+ /* Create new timer */
+ tb_env->decr_timer =
+ qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
+ /* There is a bug in 2.4 kernels:
+ * if a decrementer exception is pending when it enables msr_ee,
+ * it's not ready to handle it...
+ */
+ _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
+ }
+
+ return tb_env;
+}
+
+#if 0
+/*****************************************************************************/
+/* Handle system reset (for now, just stop emulation) */
+void cpu_ppc_reset (CPUState *env)
+{
+ printf("Reset asked... Stop emulation\n");
+ abort();
+}
+#endif
+
+static void PPC_io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ cpu_outb(NULL, addr & 0xffff, value);
+}
+
+static uint32_t PPC_io_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inb(NULL, addr & 0xffff);
+ return ret;
+}
+
+static void PPC_io_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap16(value);
+#endif
+ cpu_outw(NULL, addr & 0xffff, value);
+}
+
+static uint32_t PPC_io_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inw(NULL, addr & 0xffff);
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap16(ret);
+#endif
+ return ret;
+}
+
+static void PPC_io_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+ cpu_outl(NULL, addr & 0xffff, value);
+}
+
+static uint32_t PPC_io_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = cpu_inl(NULL, addr & 0xffff);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap32(ret);
+#endif
+ return ret;
+}
+
+CPUWriteMemoryFunc *PPC_io_write[] = {
+ &PPC_io_writeb,
+ &PPC_io_writew,
+ &PPC_io_writel,
+};
+
+CPUReadMemoryFunc *PPC_io_read[] = {
+ &PPC_io_readb,
+ &PPC_io_readw,
+ &PPC_io_readl,
+};
+
+/*****************************************************************************/
+/* Debug port */
+void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ addr &= 0xF;
+ switch (addr) {
+ case 0:
+ printf("%c", val);
+ break;
+ case 1:
+ printf("\n");
+ fflush(stdout);
+ break;
+ case 2:
+ printf("Set loglevel to %04x\n", val);
+ cpu_set_log(val | 0x100);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* NVRAM helpers */
+void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value)
+{
+ m48t59_write(nvram, addr, value);
+}
+
+uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr)
+{
+ return m48t59_read(nvram, addr);
+}
+
+void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
+{
+ m48t59_write(nvram, addr, value >> 8);
+ m48t59_write(nvram, addr + 1, value & 0xFF);
+}
+
+uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr)
+{
+ uint16_t tmp;
+
+ tmp = m48t59_read(nvram, addr) << 8;
+ tmp |= m48t59_read(nvram, addr + 1);
+ return tmp;
+}
+
+void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
+{
+ m48t59_write(nvram, addr, value >> 24);
+ m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF);
+ m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF);
+ m48t59_write(nvram, addr + 3, value & 0xFF);
+}
+
+uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr)
+{
+ uint32_t tmp;
+
+ tmp = m48t59_read(nvram, addr) << 24;
+ tmp |= m48t59_read(nvram, addr + 1) << 16;
+ tmp |= m48t59_read(nvram, addr + 2) << 8;
+ tmp |= m48t59_read(nvram, addr + 3);
+ return tmp;
+}
+
+void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
+ const unsigned char *str, uint32_t max)
+{
+ int i;
+
+ for (i = 0; i < max && str[i] != '\0'; i++) {
+ m48t59_write(nvram, addr + i, str[i]);
+ }
+ m48t59_write(nvram, addr + max - 1, '\0');
+}
+
+int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max)
+{
+ int i;
+
+ memset(dst, 0, max);
+ for (i = 0; i < max; i++) {
+ dst[i] = NVRAM_get_byte(nvram, addr + i);
+ if (dst[i] == '\0')
+ break;
+ }
+
+ return i;
+}
+
+static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
+{
+ uint16_t tmp;
+ uint16_t pd, pd1, pd2;
+
+ tmp = prev >> 8;
+ pd = prev ^ value;
+ pd1 = pd & 0x000F;
+ pd2 = ((pd >> 4) & 0x000F) ^ pd1;
+ tmp ^= (pd1 << 3) | (pd1 << 8);
+ tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
+
+ return tmp;
+}
+
+uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count)
+{
+ uint32_t i;
+ uint16_t crc = 0xFFFF;
+ int odd;
+
+ odd = count & 1;
+ count &= ~1;
+ for (i = 0; i != count; i++) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
+ }
+ if (odd) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
+ }
+
+ return crc;
+}
+
+#define CMDLINE_ADDR 0x017ff000
+
+int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
+ const unsigned char *arch,
+ uint32_t RAM_size, int boot_device,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth)
+{
+ uint16_t crc;
+
+ /* Set parameters for Open Hack'Ware BIOS */
+ NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
+ NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */
+ NVRAM_set_word(nvram, 0x14, NVRAM_size);
+ NVRAM_set_string(nvram, 0x20, arch, 16);
+ NVRAM_set_lword(nvram, 0x30, RAM_size);
+ NVRAM_set_byte(nvram, 0x34, boot_device);
+ NVRAM_set_lword(nvram, 0x38, kernel_image);
+ NVRAM_set_lword(nvram, 0x3C, kernel_size);
+ if (cmdline) {
+ /* XXX: put the cmdline in NVRAM too ? */
+ strcpy(phys_ram_base + CMDLINE_ADDR, cmdline);
+ NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR);
+ NVRAM_set_lword(nvram, 0x44, strlen(cmdline));
+ } else {
+ NVRAM_set_lword(nvram, 0x40, 0);
+ NVRAM_set_lword(nvram, 0x44, 0);
+ }
+ NVRAM_set_lword(nvram, 0x48, initrd_image);
+ NVRAM_set_lword(nvram, 0x4C, initrd_size);
+ NVRAM_set_lword(nvram, 0x50, NVRAM_image);
+
+ NVRAM_set_word(nvram, 0x54, width);
+ NVRAM_set_word(nvram, 0x56, height);
+ NVRAM_set_word(nvram, 0x58, depth);
+ crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
+ NVRAM_set_word(nvram, 0xFC, crc);
+
+ return 0;
+}
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
new file mode 100644
index 0000000..42d5995
--- /dev/null
+++ b/hw/ppc_chrp.c
@@ -0,0 +1,566 @@
+/*
+ * QEMU PPC CHRP/PMAC hardware System Emulator
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define BIOS_FILENAME "ppc_rom.bin"
+#define VGABIOS_FILENAME "video.x"
+#define NVRAM_SIZE 0x2000
+
+#define KERNEL_LOAD_ADDR 0x01000000
+#define INITRD_LOAD_ADDR 0x01800000
+
+/* MacIO devices (mapped inside the MacIO address space): CUDA, DBDMA,
+ NVRAM */
+
+static int dbdma_mem_index;
+static int cuda_mem_index;
+static int ide0_mem_index = -1;
+static int ide1_mem_index = -1;
+static int openpic_mem_index = -1;
+static int heathrow_pic_mem_index = -1;
+static int macio_nvram_mem_index = -1;
+
+/* DBDMA: currently no op - should suffice right now */
+
+static void dbdma_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ printf("%s: 0x%08x <= 0x%08x\n", __func__, addr, value);
+}
+
+static void dbdma_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static void dbdma_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static uint32_t dbdma_readb (void *opaque, target_phys_addr_t addr)
+{
+ printf("%s: 0x%08x => 0x00000000\n", __func__, addr);
+ return 0;
+}
+
+static uint32_t dbdma_readw (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static CPUWriteMemoryFunc *dbdma_write[] = {
+ &dbdma_writeb,
+ &dbdma_writew,
+ &dbdma_writel,
+};
+
+static CPUReadMemoryFunc *dbdma_read[] = {
+ &dbdma_readb,
+ &dbdma_readw,
+ &dbdma_readl,
+};
+
+/* macio style NVRAM device */
+typedef struct MacIONVRAMState {
+ uint8_t data[0x2000];
+} MacIONVRAMState;
+
+static void macio_nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ MacIONVRAMState *s = opaque;
+ addr = (addr >> 4) & 0x1fff;
+ s->data[addr] = value;
+ // printf("macio_nvram_writeb %04x = %02x\n", addr, value);
+}
+
+static uint32_t macio_nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+ MacIONVRAMState *s = opaque;
+ uint32_t value;
+
+ addr = (addr >> 4) & 0x1fff;
+ value = s->data[addr];
+ // printf("macio_nvram_readb %04x = %02x\n", addr, value);
+ return value;
+}
+
+static CPUWriteMemoryFunc *macio_nvram_write[] = {
+ &macio_nvram_writeb,
+ &macio_nvram_writeb,
+ &macio_nvram_writeb,
+};
+
+static CPUReadMemoryFunc *macio_nvram_read[] = {
+ &macio_nvram_readb,
+ &macio_nvram_readb,
+ &macio_nvram_readb,
+};
+
+static MacIONVRAMState *macio_nvram_init(void)
+{
+ MacIONVRAMState *s;
+ s = qemu_mallocz(sizeof(MacIONVRAMState));
+ if (!s)
+ return NULL;
+ macio_nvram_mem_index = cpu_register_io_memory(0, macio_nvram_read,
+ macio_nvram_write, s);
+ return s;
+}
+
+static void macio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ if (heathrow_pic_mem_index >= 0) {
+ cpu_register_physical_memory(addr + 0x00000, 0x1000,
+ heathrow_pic_mem_index);
+ }
+ cpu_register_physical_memory(addr + 0x08000, 0x1000, dbdma_mem_index);
+ cpu_register_physical_memory(addr + 0x16000, 0x2000, cuda_mem_index);
+ if (ide0_mem_index >= 0)
+ cpu_register_physical_memory(addr + 0x1f000, 0x1000, ide0_mem_index);
+ if (ide1_mem_index >= 0)
+ cpu_register_physical_memory(addr + 0x20000, 0x1000, ide1_mem_index);
+ if (openpic_mem_index >= 0) {
+ cpu_register_physical_memory(addr + 0x40000, 0x40000,
+ openpic_mem_index);
+ }
+ if (macio_nvram_mem_index >= 0)
+ cpu_register_physical_memory(addr + 0x60000, 0x20000, macio_nvram_mem_index);
+}
+
+static void macio_init(PCIBus *bus, int device_id)
+{
+ PCIDevice *d;
+
+ d = pci_register_device(bus, "macio", sizeof(PCIDevice),
+ -1, NULL, NULL);
+ /* Note: this code is strongly inspirated from the corresponding code
+ in PearPC */
+ d->config[0x00] = 0x6b; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = device_id;
+ d->config[0x03] = device_id >> 8;
+
+ d->config[0x0a] = 0x00; // class_sub = pci2pci
+ d->config[0x0b] = 0xff; // class_base = bridge
+ d->config[0x0e] = 0x00; // header_type
+
+ d->config[0x3d] = 0x01; // interrupt on pin 1
+
+ dbdma_mem_index = cpu_register_io_memory(0, dbdma_read, dbdma_write, NULL);
+
+ pci_register_io_region(d, 0, 0x80000,
+ PCI_ADDRESS_SPACE_MEM, macio_map);
+}
+
+/* UniN device */
+static void unin_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static uint32_t unin_readl (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static CPUWriteMemoryFunc *unin_write[] = {
+ &unin_writel,
+ &unin_writel,
+ &unin_writel,
+};
+
+static CPUReadMemoryFunc *unin_read[] = {
+ &unin_readl,
+ &unin_readl,
+ &unin_readl,
+};
+
+/* temporary frame buffer OSI calls for the video.x driver. The right
+ solution is to modify the driver to use VGA PCI I/Os */
+static int vga_osi_call(CPUState *env)
+{
+ static int vga_vbl_enabled;
+ int linesize;
+
+ // printf("osi_call R5=%d\n", env->gpr[5]);
+
+ /* same handler as PearPC, coming from the original MOL video
+ driver. */
+ switch(env->gpr[5]) {
+ case 4:
+ break;
+ case 28: /* set_vmode */
+ if (env->gpr[6] != 1 || env->gpr[7] != 0)
+ env->gpr[3] = 1;
+ else
+ env->gpr[3] = 0;
+ break;
+ case 29: /* get_vmode_info */
+ if (env->gpr[6] != 0) {
+ if (env->gpr[6] != 1 || env->gpr[7] != 0) {
+ env->gpr[3] = 1;
+ break;
+ }
+ }
+ env->gpr[3] = 0;
+ env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */
+ env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */
+ env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */
+ env->gpr[7] = 85 << 16; /* refresh rate */
+ env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */
+ linesize = ((graphic_depth + 7) >> 3) * graphic_width;
+ linesize = (linesize + 3) & ~3;
+ env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */
+ break;
+ case 31: /* set_video power */
+ env->gpr[3] = 0;
+ break;
+ case 39: /* video_ctrl */
+ if (env->gpr[6] == 0 || env->gpr[6] == 1)
+ vga_vbl_enabled = env->gpr[6];
+ env->gpr[3] = 0;
+ break;
+ case 47:
+ break;
+ case 59: /* set_color */
+ /* R6 = index, R7 = RGB */
+ env->gpr[3] = 0;
+ break;
+ case 64: /* get color */
+ /* R6 = index */
+ env->gpr[3] = 0;
+ break;
+ case 116: /* set hwcursor */
+ /* R6 = x, R7 = y, R8 = visible, R9 = data */
+ break;
+ default:
+ fprintf(stderr, "unsupported OSI call R5=%08x\n", env->gpr[5]);
+ break;
+ }
+ return 1; /* osi_call handled */
+}
+
+/* XXX: suppress that */
+static void pic_irq_request(void *opaque, int level)
+{
+}
+
+static uint8_t nvram_chksum(const uint8_t *buf, int n)
+{
+ int sum, i;
+ sum = 0;
+ for(i = 0; i < n; i++)
+ sum += buf[i];
+ return (sum & 0xff) + (sum >> 8);
+}
+
+/* set a free Mac OS NVRAM partition */
+void pmac_format_nvram_partition(uint8_t *buf, int len)
+{
+ char partition_name[12] = "wwwwwwwwwwww";
+
+ buf[0] = 0x7f; /* free partition magic */
+ buf[1] = 0; /* checksum */
+ buf[2] = len >> 8;
+ buf[3] = len;
+ memcpy(buf + 4, partition_name, 12);
+ buf[1] = nvram_chksum(buf, 16);
+}
+
+/* PowerPC CHRP hardware initialisation */
+static void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ int is_heathrow)
+{
+ CPUState *env;
+ char buf[1024];
+ SetIRQFunc *set_irq;
+ void *pic;
+ m48t59_t *nvram;
+ int PPC_io_memory, unin_memory;
+ int linux_boot, i;
+ unsigned long bios_offset, vga_bios_offset;
+ uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
+ ppc_def_t *def;
+ PCIBus *pci_bus;
+ const char *arch_name;
+ int vga_bios_size, bios_size;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ env = cpu_init();
+ register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+
+ /* Register CPU as a 74x/75x */
+ /* XXX: CPU model (or PVR) should be provided on command line */
+ // ppc_find_by_name("750gx", &def); // Linux boot OK
+ // ppc_find_by_name("750fx", &def); // Linux boot OK
+ /* Linux does not boot on 750cxe (and probably other 750cx based)
+ * because it assumes it has 8 IBAT & DBAT pairs as it only have 4.
+ */
+ // ppc_find_by_name("750cxe", &def);
+ // ppc_find_by_name("750p", &def);
+ // ppc_find_by_name("740p", &def);
+ ppc_find_by_name("750", &def);
+ // ppc_find_by_name("740", &def);
+ // ppc_find_by_name("G3", &def);
+ // ppc_find_by_name("604r", &def);
+ // ppc_find_by_name("604e", &def);
+ // ppc_find_by_name("604", &def);
+ if (def == NULL) {
+ cpu_abort(env, "Unable to find PowerPC CPU definition\n");
+ }
+ cpu_ppc_register(env, def);
+
+ /* Set time-base frequency to 100 Mhz */
+ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+
+ env->osi_call = vga_osi_call;
+
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ /* allocate and load BIOS */
+ bios_offset = ram_size + vga_ram_size;
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
+ bios_size = load_image(buf, phys_ram_base + bios_offset);
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf);
+ exit(1);
+ }
+ bios_size = (bios_size + 0xfff) & ~0xfff;
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+
+ /* allocate and load VGA BIOS */
+ vga_bios_offset = bios_offset + bios_size;
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);
+ vga_bios_size = load_image(buf, phys_ram_base + vga_bios_offset + 8);
+ if (vga_bios_size < 0) {
+ /* if no bios is present, we can still work */
+ fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n", buf);
+ vga_bios_size = 0;
+ } else {
+ /* set a specific header (XXX: find real Apple format for NDRV
+ drivers) */
+ phys_ram_base[vga_bios_offset] = 'N';
+ phys_ram_base[vga_bios_offset + 1] = 'D';
+ phys_ram_base[vga_bios_offset + 2] = 'R';
+ phys_ram_base[vga_bios_offset + 3] = 'V';
+ cpu_to_be32w((uint32_t *)(phys_ram_base + vga_bios_offset + 4),
+ vga_bios_size);
+ vga_bios_size += 8;
+ }
+ vga_bios_size = (vga_bios_size + 0xfff) & ~0xfff;
+
+ if (linux_boot) {
+ kernel_base = KERNEL_LOAD_ADDR;
+ /* now we can load the kernel */
+ kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image(initrd_filename,
+ phys_ram_base + initrd_base);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ boot_device = 'm';
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+
+ if (is_heathrow) {
+ isa_mem_base = 0x80000000;
+
+ /* Register 2 MB of ISA IO space */
+ PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL);
+ cpu_register_physical_memory(0xfe000000, 0x00200000, PPC_io_memory);
+
+ /* init basic PC hardware */
+ pic = heathrow_pic_init(&heathrow_pic_mem_index);
+ set_irq = heathrow_pic_set_irq;
+ pci_bus = pci_grackle_init(0xfec00000, pic);
+ vga_initialize(pci_bus, ds, phys_ram_base + ram_size,
+ ram_size, vga_ram_size,
+ vga_bios_offset, vga_bios_size);
+
+ /* XXX: suppress that */
+ isa_pic = pic_init(pic_irq_request, NULL);
+
+ /* XXX: use Mac Serial port */
+ serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
+
+ for(i = 0; i < nb_nics; i++) {
+ if (!nd_table[i].model)
+ nd_table[i].model = "ne2k_pci";
+ pci_nic_init(pci_bus, &nd_table[i]);
+ }
+
+ pci_cmd646_ide_init(pci_bus, &bs_table[0], 0);
+
+ /* cuda also initialize ADB */
+ cuda_mem_index = cuda_init(set_irq, pic, 0x12);
+
+ adb_kbd_init(&adb_bus);
+ adb_mouse_init(&adb_bus);
+
+ {
+ MacIONVRAMState *nvr;
+ nvr = macio_nvram_init();
+ pmac_format_nvram_partition(nvr->data, 0x2000);
+ }
+
+ macio_init(pci_bus, 0x0017);
+
+ nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE, 59);
+
+ arch_name = "HEATHROW";
+ } else {
+ isa_mem_base = 0x80000000;
+
+ /* Register 8 MB of ISA IO space */
+ PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL);
+ cpu_register_physical_memory(0xF2000000, 0x00800000, PPC_io_memory);
+
+ /* UniN init */
+ unin_memory = cpu_register_io_memory(0, unin_read, unin_write, NULL);
+ cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory);
+
+ pic = openpic_init(NULL, &openpic_mem_index, 1, &env);
+ set_irq = openpic_set_irq;
+ pci_bus = pci_pmac_init(pic);
+ /* init basic PC hardware */
+ vga_initialize(pci_bus, ds, phys_ram_base + ram_size,
+ ram_size, vga_ram_size,
+ vga_bios_offset, vga_bios_size);
+
+ /* XXX: suppress that */
+ isa_pic = pic_init(pic_irq_request, NULL);
+
+ /* XXX: use Mac Serial port */
+ serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
+
+ for(i = 0; i < nb_nics; i++) {
+ pci_ne2000_init(pci_bus, &nd_table[i]);
+ }
+
+#if 1
+ ide0_mem_index = pmac_ide_init(&bs_table[0], set_irq, pic, 0x13);
+ ide1_mem_index = pmac_ide_init(&bs_table[2], set_irq, pic, 0x14);
+#else
+ pci_cmd646_ide_init(pci_bus, &bs_table[0], 0);
+#endif
+ /* cuda also initialize ADB */
+ cuda_mem_index = cuda_init(set_irq, pic, 0x19);
+
+ adb_kbd_init(&adb_bus);
+ adb_mouse_init(&adb_bus);
+
+ macio_init(pci_bus, 0x0022);
+
+ nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE, 59);
+
+ arch_name = "MAC99";
+ }
+
+ if (usb_enabled) {
+ usb_ohci_init(pci_bus, 3, -1);
+ }
+
+ if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
+ graphic_depth = 15;
+
+ PPC_NVRAM_set_params(nvram, NVRAM_SIZE, arch_name, ram_size, boot_device,
+ kernel_base, kernel_size,
+ kernel_cmdline,
+ initrd_base, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth);
+ /* No PCI init: the BIOS will do it */
+
+ /* Special port to get debug messages from Open-Firmware */
+ register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL);
+}
+
+static void ppc_core99_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ ppc_chrp_init(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 0);
+}
+
+static void ppc_heathrow_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ ppc_chrp_init(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 1);
+}
+
+QEMUMachine core99_machine = {
+ "mac99",
+ "Mac99 based PowerMAC",
+ ppc_core99_init,
+};
+
+QEMUMachine heathrow_machine = {
+ "g3bw",
+ "Heathrow based PowerMAC",
+ ppc_heathrow_init,
+};
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
new file mode 100644
index 0000000..a4d7ddf
--- /dev/null
+++ b/hw/ppc_prep.c
@@ -0,0 +1,694 @@
+/*
+ * QEMU PPC PREP hardware System Emulator
+ *
+ * Copyright (c) 2003-2004 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define HARD_DEBUG_PPC_IO
+//#define DEBUG_PPC_IO
+
+#define BIOS_FILENAME "ppc_rom.bin"
+#define KERNEL_LOAD_ADDR 0x01000000
+#define INITRD_LOAD_ADDR 0x01800000
+
+extern int loglevel;
+extern FILE *logfile;
+
+#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO)
+#define DEBUG_PPC_IO
+#endif
+
+#if defined (HARD_DEBUG_PPC_IO)
+#define PPC_IO_DPRINTF(fmt, args...) \
+do { \
+ if (loglevel & CPU_LOG_IOPORT) { \
+ fprintf(logfile, "%s: " fmt, __func__ , ##args); \
+ } else { \
+ printf("%s : " fmt, __func__ , ##args); \
+ } \
+} while (0)
+#elif defined (DEBUG_PPC_IO)
+#define PPC_IO_DPRINTF(fmt, args...) \
+do { \
+ if (loglevel & CPU_LOG_IOPORT) { \
+ fprintf(logfile, "%s: " fmt, __func__ , ##args); \
+ } \
+} while (0)
+#else
+#define PPC_IO_DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+/* Constants for devices init */
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 13, 13 };
+
+#define NE2000_NB_MAX 6
+
+static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+//static PITState *pit;
+
+/* ISA IO ports bridge */
+#define PPC_IO_BASE 0x80000000
+
+/* Speaker port 0x61 */
+int speaker_data_on;
+int dummy_refresh_clock;
+
+static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+#if 0
+ speaker_data_on = (val >> 1) & 1;
+ pit_set_gate(pit, 2, val & 1);
+#endif
+}
+
+static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
+{
+#if 0
+ int out;
+ out = pit_get_out(pit, 2, qemu_get_clock(vm_clock));
+ dummy_refresh_clock ^= 1;
+ return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
+ (dummy_refresh_clock << 4);
+#endif
+ return 0;
+}
+
+static void pic_irq_request(void *opaque, int level)
+{
+ if (level)
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+}
+
+/* PCI intack register */
+/* Read-only register (?) */
+static void _PPC_intack_write (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ // printf("%s: 0x%08x => 0x%08x\n", __func__, addr, value);
+}
+
+static inline uint32_t _PPC_intack_read (target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ if (addr == 0xBFFFFFF0)
+ retval = pic_intack_read(isa_pic);
+ // printf("%s: 0x%08x <= %d\n", __func__, addr, retval);
+
+ return retval;
+}
+
+static uint32_t PPC_intack_readb (void *opaque, target_phys_addr_t addr)
+{
+ return _PPC_intack_read(addr);
+}
+
+static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ return bswap16(_PPC_intack_read(addr));
+#else
+ return _PPC_intack_read(addr);
+#endif
+}
+
+static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ return bswap32(_PPC_intack_read(addr));
+#else
+ return _PPC_intack_read(addr);
+#endif
+}
+
+static CPUWriteMemoryFunc *PPC_intack_write[] = {
+ &_PPC_intack_write,
+ &_PPC_intack_write,
+ &_PPC_intack_write,
+};
+
+static CPUReadMemoryFunc *PPC_intack_read[] = {
+ &PPC_intack_readb,
+ &PPC_intack_readw,
+ &PPC_intack_readl,
+};
+
+/* PowerPC control and status registers */
+#if 0 // Not used
+static struct {
+ /* IDs */
+ uint32_t veni_devi;
+ uint32_t revi;
+ /* Control and status */
+ uint32_t gcsr;
+ uint32_t xcfr;
+ uint32_t ct32;
+ uint32_t mcsr;
+ /* General purpose registers */
+ uint32_t gprg[6];
+ /* Exceptions */
+ uint32_t feen;
+ uint32_t fest;
+ uint32_t fema;
+ uint32_t fecl;
+ uint32_t eeen;
+ uint32_t eest;
+ uint32_t eecl;
+ uint32_t eeint;
+ uint32_t eemck0;
+ uint32_t eemck1;
+ /* Error diagnostic */
+} XCSR;
+
+static void PPC_XCSR_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
+}
+
+static void PPC_XCSR_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap16(value);
+#endif
+ printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
+}
+
+static void PPC_XCSR_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+ printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value);
+}
+
+static uint32_t PPC_XCSR_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
+
+ return retval;
+}
+
+static uint32_t PPC_XCSR_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap16(retval);
+#endif
+
+ return retval;
+}
+
+static uint32_t PPC_XCSR_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval);
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap32(retval);
+#endif
+
+ return retval;
+}
+
+static CPUWriteMemoryFunc *PPC_XCSR_write[] = {
+ &PPC_XCSR_writeb,
+ &PPC_XCSR_writew,
+ &PPC_XCSR_writel,
+};
+
+static CPUReadMemoryFunc *PPC_XCSR_read[] = {
+ &PPC_XCSR_readb,
+ &PPC_XCSR_readw,
+ &PPC_XCSR_readl,
+};
+#endif
+
+/* Fake super-io ports for PREP platform (Intel 82378ZB) */
+typedef struct sysctrl_t {
+ m48t59_t *nvram;
+ uint8_t state;
+ uint8_t syscontrol;
+ uint8_t fake_io[2];
+ int contiguous_map;
+ int endian;
+} sysctrl_t;
+
+enum {
+ STATE_HARDFILE = 0x01,
+};
+
+static sysctrl_t *sysctrl;
+
+static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val);
+ sysctrl->fake_io[addr - 0x0398] = val;
+}
+
+static uint32_t PREP_io_read (void *opaque, uint32_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE,
+ sysctrl->fake_io[addr - 0x0398]);
+ return sysctrl->fake_io[addr - 0x0398];
+}
+
+static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val);
+ switch (addr) {
+ case 0x0092:
+ /* Special port 92 */
+ /* Check soft reset asked */
+ if (val & 0x01) {
+ // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET);
+ }
+ /* Check LE mode */
+ if (val & 0x02) {
+ sysctrl->endian = 1;
+ } else {
+ sysctrl->endian = 0;
+ }
+ break;
+ case 0x0800:
+ /* Motorola CPU configuration register : read-only */
+ break;
+ case 0x0802:
+ /* Motorola base module feature register : read-only */
+ break;
+ case 0x0803:
+ /* Motorola base module status register : read-only */
+ break;
+ case 0x0808:
+ /* Hardfile light register */
+ if (val & 1)
+ sysctrl->state |= STATE_HARDFILE;
+ else
+ sysctrl->state &= ~STATE_HARDFILE;
+ break;
+ case 0x0810:
+ /* Password protect 1 register */
+ if (sysctrl->nvram != NULL)
+ m48t59_toggle_lock(sysctrl->nvram, 1);
+ break;
+ case 0x0812:
+ /* Password protect 2 register */
+ if (sysctrl->nvram != NULL)
+ m48t59_toggle_lock(sysctrl->nvram, 2);
+ break;
+ case 0x0814:
+ /* L2 invalidate register */
+ // tlb_flush(first_cpu, 1);
+ break;
+ case 0x081C:
+ /* system control register */
+ sysctrl->syscontrol = val & 0x0F;
+ break;
+ case 0x0850:
+ /* I/O map type register */
+ sysctrl->contiguous_map = val & 0x01;
+ break;
+ default:
+ printf("ERROR: unaffected IO port write: %04lx => %02x\n",
+ (long)addr, val);
+ break;
+ }
+}
+
+static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t retval = 0xFF;
+
+ switch (addr) {
+ case 0x0092:
+ /* Special port 92 */
+ retval = 0x00;
+ break;
+ case 0x0800:
+ /* Motorola CPU configuration register */
+ retval = 0xEF; /* MPC750 */
+ break;
+ case 0x0802:
+ /* Motorola Base module feature register */
+ retval = 0xAD; /* No ESCC, PMC slot neither ethernet */
+ break;
+ case 0x0803:
+ /* Motorola base module status register */
+ retval = 0xE0; /* Standard MPC750 */
+ break;
+ case 0x080C:
+ /* Equipment present register:
+ * no L2 cache
+ * no upgrade processor
+ * no cards in PCI slots
+ * SCSI fuse is bad
+ */
+ retval = 0x3C;
+ break;
+ case 0x0810:
+ /* Motorola base module extended feature register */
+ retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */
+ break;
+ case 0x0814:
+ /* L2 invalidate: don't care */
+ break;
+ case 0x0818:
+ /* Keylock */
+ retval = 0x00;
+ break;
+ case 0x081C:
+ /* system control register
+ * 7 - 6 / 1 - 0: L2 cache enable
+ */
+ retval = sysctrl->syscontrol;
+ break;
+ case 0x0823:
+ /* */
+ retval = 0x03; /* no L2 cache */
+ break;
+ case 0x0850:
+ /* I/O map type register */
+ retval = sysctrl->contiguous_map;
+ break;
+ default:
+ printf("ERROR: unaffected IO port: %04lx read\n", (long)addr);
+ break;
+ }
+ PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE, retval);
+
+ return retval;
+}
+
+static inline target_phys_addr_t prep_IO_address (sysctrl_t *sysctrl,
+ target_phys_addr_t addr)
+{
+ if (sysctrl->contiguous_map == 0) {
+ /* 64 KB contiguous space for IOs */
+ addr &= 0xFFFF;
+ } else {
+ /* 8 MB non-contiguous space for IOs */
+ addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7);
+ }
+
+ return addr;
+}
+
+static void PPC_prep_io_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+ cpu_outb(NULL, addr, value);
+}
+
+static uint32_t PPC_prep_io_readb (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inb(NULL, addr);
+
+ return ret;
+}
+
+static void PPC_prep_io_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap16(value);
+#endif
+ PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value);
+ cpu_outw(NULL, addr, value);
+}
+
+static uint32_t PPC_prep_io_readw (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inw(NULL, addr);
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap16(ret);
+#endif
+ PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret);
+
+ return ret;
+}
+
+static void PPC_prep_io_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+ PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value);
+ cpu_outl(NULL, addr, value);
+}
+
+static uint32_t PPC_prep_io_readl (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inl(NULL, addr);
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = bswap32(ret);
+#endif
+ PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret);
+
+ return ret;
+}
+
+CPUWriteMemoryFunc *PPC_prep_io_write[] = {
+ &PPC_prep_io_writeb,
+ &PPC_prep_io_writew,
+ &PPC_prep_io_writel,
+};
+
+CPUReadMemoryFunc *PPC_prep_io_read[] = {
+ &PPC_prep_io_readb,
+ &PPC_prep_io_readw,
+ &PPC_prep_io_readl,
+};
+
+#define NVRAM_SIZE 0x2000
+
+/* PowerPC PREP hardware initialisation */
+static void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ CPUState *env;
+ char buf[1024];
+ SetIRQFunc *set_irq;
+ m48t59_t *nvram;
+ int PPC_io_memory;
+ int linux_boot, i, nb_nics1, bios_size;
+ unsigned long bios_offset;
+ uint32_t kernel_base, kernel_size, initrd_base, initrd_size;
+ ppc_def_t *def;
+ PCIBus *pci_bus;
+
+ sysctrl = qemu_mallocz(sizeof(sysctrl_t));
+ if (sysctrl == NULL)
+ return;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+
+ env = cpu_init();
+ register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+
+ /* Register CPU as a 604 */
+ /* XXX: CPU model (or PVR) should be provided on command line */
+ // ppc_find_by_name("604r", &def);
+ // ppc_find_by_name("604e", &def);
+ ppc_find_by_name("604", &def);
+ if (def == NULL) {
+ cpu_abort(env, "Unable to find PowerPC CPU definition\n");
+ }
+ cpu_ppc_register(env, def);
+ /* Set time-base frequency to 100 Mhz */
+ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ /* allocate and load BIOS */
+ bios_offset = ram_size + vga_ram_size;
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
+ bios_size = load_image(buf, phys_ram_base + bios_offset);
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf);
+ exit(1);
+ }
+ bios_size = (bios_size + 0xfff) & ~0xfff;
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+
+ if (linux_boot) {
+ kernel_base = KERNEL_LOAD_ADDR;
+ /* now we can load the kernel */
+ kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image(initrd_filename,
+ phys_ram_base + initrd_base);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ boot_device = 'm';
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+
+ isa_mem_base = 0xc0000000;
+ pci_bus = pci_prep_init();
+ // pci_bus = i440fx_init();
+ /* Register 8 MB of ISA IO space (needed for non-contiguous map) */
+ PPC_io_memory = cpu_register_io_memory(0, PPC_prep_io_read,
+ PPC_prep_io_write, sysctrl);
+ cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory);
+
+ /* init basic PC hardware */
+ vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size,
+ vga_ram_size, 0, 0);
+ rtc_init(0x70, 8);
+ // openpic = openpic_init(0x00000000, 0xF0000000, 1);
+ isa_pic = pic_init(pic_irq_request, first_cpu);
+ // pit = pit_init(0x40, 0);
+
+ serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]);
+ nb_nics1 = nb_nics;
+ if (nb_nics1 > NE2000_NB_MAX)
+ nb_nics1 = NE2000_NB_MAX;
+ for(i = 0; i < nb_nics1; i++) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "ne2k_isa") == 0) {
+ isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+
+ for(i = 0; i < 2; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ bs_table[2 * i], bs_table[2 * i + 1]);
+ }
+ kbd_init();
+ DMA_init(1);
+ // AUD_init();
+ // SB16_init();
+
+ fdctrl_init(6, 2, 0, 0x3f0, fd_table);
+
+ /* Register speaker port */
+ register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL);
+ register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL);
+ /* Register fake IO ports for PREP */
+ register_ioport_read(0x398, 2, 1, &PREP_io_read, sysctrl);
+ register_ioport_write(0x398, 2, 1, &PREP_io_write, sysctrl);
+ /* System control ports */
+ register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl);
+ register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl);
+ register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl);
+ register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl);
+ /* PCI intack location */
+ PPC_io_memory = cpu_register_io_memory(0, PPC_intack_read,
+ PPC_intack_write, NULL);
+ cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory);
+ /* PowerPC control and status register group */
+#if 0
+ PPC_io_memory = cpu_register_io_memory(0, PPC_XCSR_read, PPC_XCSR_write, NULL);
+ cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory);
+#endif
+
+ if (usb_enabled) {
+ usb_ohci_init(pci_bus, 3, -1);
+ }
+
+ nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59);
+ if (nvram == NULL)
+ return;
+ sysctrl->nvram = nvram;
+
+ /* Initialise NVRAM */
+ PPC_NVRAM_set_params(nvram, NVRAM_SIZE, "PREP", ram_size, boot_device,
+ kernel_base, kernel_size,
+ kernel_cmdline,
+ initrd_base, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth);
+
+ /* Special port to get debug messages from Open-Firmware */
+ register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL);
+}
+
+QEMUMachine prep_machine = {
+ "prep",
+ "PowerPC PREP platform",
+ ppc_prep_init,
+};
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
new file mode 100644
index 0000000..a31b74c
--- /dev/null
+++ b/hw/prep_pci.c
@@ -0,0 +1,167 @@
+/*
+ * QEMU PREP PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+typedef uint32_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState PREPPCIState;
+
+static void pci_prep_addr_writel(void* opaque, uint32_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+ s->config_reg = val;
+}
+
+static uint32_t pci_prep_addr_readl(void* opaque, uint32_t addr)
+{
+ PREPPCIState *s = opaque;
+ return s->config_reg;
+}
+
+static inline uint32_t PPC_PCIIO_config(target_phys_addr_t addr)
+{
+ int i;
+
+ for(i = 0; i < 11; i++) {
+ if ((addr & (1 << (11 + i))) != 0)
+ break;
+ }
+ return (addr & 0x7ff) | (i << 11);
+}
+
+static void PPC_PCIIO_writeb (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 1);
+}
+
+static void PPC_PCIIO_writew (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 2);
+}
+
+static void PPC_PCIIO_writel (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 4);
+}
+
+static uint32_t PPC_PCIIO_readb (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 1);
+ return val;
+}
+
+static uint32_t PPC_PCIIO_readw (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 2);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t PPC_PCIIO_readl (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 4);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+static CPUWriteMemoryFunc *PPC_PCIIO_write[] = {
+ &PPC_PCIIO_writeb,
+ &PPC_PCIIO_writew,
+ &PPC_PCIIO_writel,
+};
+
+static CPUReadMemoryFunc *PPC_PCIIO_read[] = {
+ &PPC_PCIIO_readb,
+ &PPC_PCIIO_readw,
+ &PPC_PCIIO_readl,
+};
+
+static void prep_set_irq(PCIDevice *d, void *pic, int irq_num, int level)
+{
+ /* XXX: we do not simulate the hardware - we rely on the BIOS to
+ set correctly for irq line field */
+ pic_set_irq(d->config[PCI_INTERRUPT_LINE], level);
+}
+
+PCIBus *pci_prep_init(void)
+{
+ PREPPCIState *s;
+ PCIDevice *d;
+ int PPC_io_memory;
+
+ s = qemu_mallocz(sizeof(PREPPCIState));
+ s->bus = pci_register_bus(prep_set_irq, NULL, 0);
+
+ register_ioport_write(0xcf8, 4, 4, pci_prep_addr_writel, s);
+ register_ioport_read(0xcf8, 4, 4, pci_prep_addr_readl, s);
+
+ register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s);
+ register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s);
+ register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s);
+ register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s);
+ register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s);
+ register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s);
+
+ PPC_io_memory = cpu_register_io_memory(0, PPC_PCIIO_read,
+ PPC_PCIIO_write, s);
+ cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory);
+
+ /* PCI host bridge */
+ d = pci_register_device(s->bus, "PREP Host Bridge - Motorola Raven",
+ sizeof(PCIDevice), 0, NULL, NULL);
+ d->config[0x00] = 0x57; // vendor_id : Motorola
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x01; // device_id : Raven
+ d->config[0x03] = 0x48;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x0A] = 0x00; // class_sub = pci host
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x0E] = 0x00; // header_type
+ d->config[0x34] = 0x00; // capabilities_pointer
+
+ return s->bus;
+}
+
diff --git a/hw/ps2.c b/hw/ps2.c
new file mode 100644
index 0000000..8438a5e
--- /dev/null
+++ b/hw/ps2.c
@@ -0,0 +1,566 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_POLL 0xEB /* Poll */
+#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
+#define AUX_SET_WRAP 0xEE /* Set wrap mode */
+#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
+#define AUX_GET_TYPE 0xF2 /* Get type */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_SET_DEFAULT 0xF6
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE 0x40
+#define MOUSE_STATUS_ENABLED 0x20
+#define MOUSE_STATUS_SCALE21 0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[PS2_QUEUE_SIZE];
+ int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+ PS2Queue queue;
+ int32_t write_cmd;
+ void (*update_irq)(void *, int);
+ void *update_arg;
+} PS2State;
+
+typedef struct {
+ PS2State common;
+ int scan_enabled;
+ /* Qemu uses translated PC scancodes internally. To avoid multiple
+ conversions we do the translation (if any) in the PS/2 emulation
+ not the keyboard controller. */
+ int translate;
+} PS2KbdState;
+
+typedef struct {
+ PS2State common;
+ uint8_t mouse_status;
+ uint8_t mouse_resolution;
+ uint8_t mouse_sample_rate;
+ uint8_t mouse_wrap;
+ uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+ uint8_t mouse_detect_state;
+ int mouse_dx; /* current values, needed for 'poll' mode */
+ int mouse_dy;
+ int mouse_dz;
+ uint8_t mouse_buttons;
+} PS2MouseState;
+
+/* Table to convert from PC scancodes to raw scancodes. */
+static const unsigned char ps2_raw_keycode[128] = {
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+void ps2_queue(void *opaque, int b)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q = &s->queue;
+
+ if (q->count >= PS2_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == PS2_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ s->update_irq(s->update_arg, 1);
+}
+
+static void ps2_put_keycode(void *opaque, int keycode)
+{
+ PS2KbdState *s = opaque;
+ if (!s->translate && keycode < 0xe0)
+ {
+ if (keycode & 0x80)
+ ps2_queue(&s->common, 0xf0);
+ keycode = ps2_raw_keycode[keycode & 0x7f];
+ }
+ ps2_queue(&s->common, keycode);
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q;
+ int val, index;
+
+ q = &s->queue;
+ if (q->count == 0) {
+ /* NOTE: if no data left, we return the last keyboard one
+ (needed for EMM386) */
+ /* XXX: need a timer to do things correctly */
+ index = q->rptr - 1;
+ if (index < 0)
+ index = PS2_QUEUE_SIZE - 1;
+ val = q->data[index];
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == PS2_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ /* reading deasserts IRQ */
+ s->update_irq(s->update_arg, 0);
+ /* reassert IRQs if data left */
+ s->update_irq(s->update_arg, q->count != 0);
+ }
+ return val;
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+ s->scan_enabled = 1;
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ switch(val) {
+ case 0x00:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case 0x05:
+ ps2_queue(&s->common, KBD_REPLY_RESEND);
+ break;
+ case KBD_CMD_GET_ID:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ ps2_queue(&s->common, 0xab);
+ ps2_queue(&s->common, 0x83);
+ break;
+ case KBD_CMD_ECHO:
+ ps2_queue(&s->common, KBD_CMD_ECHO);
+ break;
+ case KBD_CMD_ENABLE:
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_SET_LEDS:
+ case KBD_CMD_SET_RATE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_DISABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 0;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_ENABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET:
+ ps2_reset_keyboard(s);
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ ps2_queue(&s->common, KBD_REPLY_POR);
+ break;
+ default:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ }
+ break;
+ case KBD_CMD_SET_LEDS:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case KBD_CMD_SET_RATE:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+/* Set the scancode translation mode.
+ 0 = raw scancodes.
+ 1 = translated scancodes (used by qemu internally). */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+ s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+ unsigned int b;
+ int dx1, dy1, dz1;
+
+ dx1 = s->mouse_dx;
+ dy1 = s->mouse_dy;
+ dz1 = s->mouse_dz;
+ /* XXX: increase range to 8 bits ? */
+ if (dx1 > 127)
+ dx1 = 127;
+ else if (dx1 < -127)
+ dx1 = -127;
+ if (dy1 > 127)
+ dy1 = 127;
+ else if (dy1 < -127)
+ dy1 = -127;
+ b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+ ps2_queue(&s->common, b);
+ ps2_queue(&s->common, dx1 & 0xff);
+ ps2_queue(&s->common, dy1 & 0xff);
+ /* extra byte for IMPS/2 or IMEX */
+ switch(s->mouse_type) {
+ default:
+ break;
+ case 3:
+ if (dz1 > 127)
+ dz1 = 127;
+ else if (dz1 < -127)
+ dz1 = -127;
+ ps2_queue(&s->common, dz1 & 0xff);
+ break;
+ case 4:
+ if (dz1 > 7)
+ dz1 = 7;
+ else if (dz1 < -7)
+ dz1 = -7;
+ b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+ ps2_queue(&s->common, b);
+ break;
+ }
+
+ /* update deltas */
+ s->mouse_dx -= dx1;
+ s->mouse_dy -= dy1;
+ s->mouse_dz -= dz1;
+}
+
+static void ps2_mouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ PS2MouseState *s = opaque;
+
+ /* check if deltas are recorded when disabled */
+ if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+ return;
+
+ s->mouse_dx += dx;
+ s->mouse_dy -= dy;
+ s->mouse_dz += dz;
+ /* XXX: SDL sometimes generates nul events: we delete them */
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+ s->mouse_buttons == buttons_state)
+ return;
+ s->mouse_buttons = buttons_state;
+
+ if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+ (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+ for(;;) {
+ /* if not remote, send event. Multiple events are sent if
+ too big deltas */
+ ps2_mouse_send_packet(s);
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+ break;
+ }
+ }
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+ PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+ printf("kbd: write mouse 0x%02x\n", val);
+#endif
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ /* mouse command */
+ if (s->mouse_wrap) {
+ if (val == AUX_RESET_WRAP) {
+ s->mouse_wrap = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ return;
+ } else if (val != AUX_RESET) {
+ ps2_queue(&s->common, val);
+ return;
+ }
+ }
+ switch(val) {
+ case AUX_SET_SCALE11:
+ s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_SCALE21:
+ s->mouse_status |= MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_STREAM:
+ s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_WRAP:
+ s->mouse_wrap = 1;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_REMOTE:
+ s->mouse_status |= MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_TYPE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ case AUX_SET_RES:
+ case AUX_SET_SAMPLE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_SCALE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_status);
+ ps2_queue(&s->common, s->mouse_resolution);
+ ps2_queue(&s->common, s->mouse_sample_rate);
+ break;
+ case AUX_POLL:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_mouse_send_packet(s);
+ break;
+ case AUX_ENABLE_DEV:
+ s->mouse_status |= MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_DISABLE_DEV:
+ s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_DEFAULT:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_RESET:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ s->mouse_type = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, 0xaa);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ default:
+ break;
+ }
+ break;
+ case AUX_SET_SAMPLE:
+ s->mouse_sample_rate = val;
+ /* detect IMPS/2 or IMEX */
+ switch(s->mouse_detect_state) {
+ default:
+ case 0:
+ if (val == 200)
+ s->mouse_detect_state = 1;
+ break;
+ case 1:
+ if (val == 100)
+ s->mouse_detect_state = 2;
+ else if (val == 200)
+ s->mouse_detect_state = 3;
+ else
+ s->mouse_detect_state = 0;
+ break;
+ case 2:
+ if (val == 80)
+ s->mouse_type = 3; /* IMPS/2 */
+ s->mouse_detect_state = 0;
+ break;
+ case 3:
+ if (val == 80)
+ s->mouse_type = 4; /* IMEX */
+ s->mouse_detect_state = 0;
+ break;
+ }
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case AUX_SET_RES:
+ s->mouse_resolution = val;
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+static void ps2_reset(void *opaque)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q;
+ s->write_cmd = -1;
+ q = &s->queue;
+ q->rptr = 0;
+ q->wptr = 0;
+ q->count = 0;
+}
+
+static void ps2_common_save (QEMUFile *f, PS2State *s)
+{
+ qemu_put_be32s (f, &s->write_cmd);
+ qemu_put_be32s (f, &s->queue.rptr);
+ qemu_put_be32s (f, &s->queue.wptr);
+ qemu_put_be32s (f, &s->queue.count);
+ qemu_put_buffer (f, s->queue.data, sizeof (s->queue.data));
+}
+
+static void ps2_common_load (QEMUFile *f, PS2State *s)
+{
+ qemu_get_be32s (f, &s->write_cmd);
+ qemu_get_be32s (f, &s->queue.rptr);
+ qemu_get_be32s (f, &s->queue.wptr);
+ qemu_get_be32s (f, &s->queue.count);
+ qemu_get_buffer (f, s->queue.data, sizeof (s->queue.data));
+}
+
+static void ps2_kbd_save(QEMUFile* f, void* opaque)
+{
+ PS2KbdState *s = (PS2KbdState*)opaque;
+
+ ps2_common_save (f, &s->common);
+ qemu_put_be32s(f, &s->scan_enabled);
+ qemu_put_be32s(f, &s->translate);
+}
+
+static void ps2_mouse_save(QEMUFile* f, void* opaque)
+{
+ PS2MouseState *s = (PS2MouseState*)opaque;
+
+ ps2_common_save (f, &s->common);
+ qemu_put_8s(f, &s->mouse_status);
+ qemu_put_8s(f, &s->mouse_resolution);
+ qemu_put_8s(f, &s->mouse_sample_rate);
+ qemu_put_8s(f, &s->mouse_wrap);
+ qemu_put_8s(f, &s->mouse_type);
+ qemu_put_8s(f, &s->mouse_detect_state);
+ qemu_put_be32s(f, &s->mouse_dx);
+ qemu_put_be32s(f, &s->mouse_dy);
+ qemu_put_be32s(f, &s->mouse_dz);
+ qemu_put_8s(f, &s->mouse_buttons);
+}
+
+static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id)
+{
+ PS2KbdState *s = (PS2KbdState*)opaque;
+
+ if (version_id != 2)
+ return -EINVAL;
+
+ ps2_common_load (f, &s->common);
+ qemu_get_be32s(f, &s->scan_enabled);
+ qemu_get_be32s(f, &s->translate);
+ return 0;
+}
+
+static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id)
+{
+ PS2MouseState *s = (PS2MouseState*)opaque;
+
+ if (version_id != 2)
+ return -EINVAL;
+
+ ps2_common_load (f, &s->common);
+ qemu_get_8s(f, &s->mouse_status);
+ qemu_get_8s(f, &s->mouse_resolution);
+ qemu_get_8s(f, &s->mouse_sample_rate);
+ qemu_get_8s(f, &s->mouse_wrap);
+ qemu_get_8s(f, &s->mouse_type);
+ qemu_get_8s(f, &s->mouse_detect_state);
+ qemu_get_be32s(f, &s->mouse_dx);
+ qemu_get_be32s(f, &s->mouse_dy);
+ qemu_get_be32s(f, &s->mouse_dz);
+ qemu_get_8s(f, &s->mouse_buttons);
+ return 0;
+}
+
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ ps2_reset(&s->common);
+ register_savevm("ps2kbd", 0, 2, ps2_kbd_save, ps2_kbd_load, s);
+ qemu_add_kbd_event_handler(ps2_put_keycode, s);
+ qemu_register_reset(ps2_reset, &s->common);
+ return s;
+}
+
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ ps2_reset(&s->common);
+ register_savevm("ps2mouse", 0, 2, ps2_mouse_save, ps2_mouse_load, s);
+ qemu_add_mouse_event_handler(ps2_mouse_event, s, 0);
+ qemu_register_reset(ps2_reset, &s->common);
+ return s;
+}
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
new file mode 100644
index 0000000..9c613a8
--- /dev/null
+++ b/hw/rtl8139.c
@@ -0,0 +1,3471 @@
+/**
+ * QEMU RTL8139 emulation
+ *
+ * Copyright (c) 2006 Igor Kovalenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver)
+ *
+ * 2006-Apr-28 Juergen Lock : EEPROM emulation changes for FreeBSD driver
+ * HW revision ID changes for FreeBSD driver
+ *
+ * 2006-Jul-01 Igor Kovalenko : Implemented loopback mode for FreeBSD driver
+ * Corrected packet transfer reassembly routine for 8139C+ mode
+ * Rearranged debugging print statements
+ * Implemented PCI timer interrupt (disabled by default)
+ * Implemented Tally Counters, increased VM load/save version
+ * Implemented IP/TCP/UDP checksum task offloading
+ *
+ * 2006-Jul-04 Igor Kovalenko : Implemented TCP segmentation offloading
+ * Fixed MTU=1500 for produced ethernet frames
+ *
+ * 2006-Jul-09 Igor Kovalenko : Fixed TCP header length calculation while processing
+ * segmentation offloading
+ * Removed slirp.h dependency
+ * Added rx/tx buffer reset when enabling rx/tx operation
+ */
+
+#include "vl.h"
+
+/* debug RTL8139 card */
+//#define DEBUG_RTL8139 1
+
+#define PCI_FREQUENCY 33000000L
+
+/* debug RTL8139 card C+ mode only */
+//#define DEBUG_RTL8139CP 1
+
+/* RTL8139 provides frame CRC with received packet, this feature seems to be
+ ignored by most drivers, disabled by default */
+//#define RTL8139_CALCULATE_RXCRC 1
+
+/* Uncomment to enable on-board timer interrupts */
+//#define RTL8139_ONBOARD_TIMER 1
+
+#if defined(RTL8139_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#define SET_MASKED(input, mask, curr) \
+ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
+
+/* arg % size for size which is a power of 2 */
+#define MOD2(input, size) \
+ ( ( input ) & ( size - 1 ) )
+
+#if defined (DEBUG_RTL8139)
+# define DEBUG_PRINT(x) do { printf x ; } while (0)
+#else
+# define DEBUG_PRINT(x)
+#endif
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
+ /* Dump Tally Conter control register(64bit). C+ mode only */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ PCIRevisionID = 0x5E,
+ TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+ /* C+ mode */
+ TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */
+ RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */
+ CpCmd = 0xE0, /* C+ Command register (C+ mode only) */
+ IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */
+ RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */
+ RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */
+ TxThresh = 0xEC, /* Early Tx threshold */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* C+ mode */
+enum CplusCmdBits {
+ CPlusRxVLAN = 0x0040, /* enable receive VLAN detagging */
+ CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
+ CPlusRxEnb = 0x0002,
+ CPlusTxEnb = 0x0001,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+
+/* Transmit Status of All Descriptors (TSAD) Register */
+enum TSAD_bits {
+ TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3
+ TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2
+ TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1
+ TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0
+ TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3
+ TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2
+ TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1
+ TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0
+ TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3
+ TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2
+ TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1
+ TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0
+ TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3
+ TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2
+ TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1
+ TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0
+};
+
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+/*
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+*/
+enum CSCRBits {
+ CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
+ CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/
+ CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
+ CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
+ CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
+ CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
+ CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/
+ CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
+ CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+#define RTL8139_PCI_REVID_8139 0x10
+#define RTL8139_PCI_REVID_8139CPLUS 0x20
+
+#define RTL8139_PCI_REVID RTL8139_PCI_REVID_8139CPLUS
+
+/* Size is 64 * 16bit words */
+#define EEPROM_9346_ADDR_BITS 6
+#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS)
+#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
+
+enum Chip9346Operation
+{
+ Chip9346_op_mask = 0xc0, /* 10 zzzzzz */
+ Chip9346_op_read = 0x80, /* 10 AAAAAA */
+ Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */
+ Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */
+ Chip9346_op_write_enable = 0x30, /* 00 11zzzz */
+ Chip9346_op_write_all = 0x10, /* 00 01zzzz */
+ Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
+};
+
+enum Chip9346Mode
+{
+ Chip9346_none = 0,
+ Chip9346_enter_command_mode,
+ Chip9346_read_command,
+ Chip9346_data_read, /* from output register */
+ Chip9346_data_write, /* to input register, then to contents at specified address */
+ Chip9346_data_write_all, /* to input register, then filling contents */
+};
+
+typedef struct EEprom9346
+{
+ uint16_t contents[EEPROM_9346_SIZE];
+ int mode;
+ uint32_t tick;
+ uint8_t address;
+ uint16_t input;
+ uint16_t output;
+
+ uint8_t eecs;
+ uint8_t eesk;
+ uint8_t eedi;
+ uint8_t eedo;
+} EEprom9346;
+
+typedef struct RTL8139TallyCounters
+{
+ /* Tally counters */
+ uint64_t TxOk;
+ uint64_t RxOk;
+ uint64_t TxERR;
+ uint32_t RxERR;
+ uint16_t MissPkt;
+ uint16_t FAE;
+ uint32_t Tx1Col;
+ uint32_t TxMCol;
+ uint64_t RxOkPhy;
+ uint64_t RxOkBrd;
+ uint32_t RxOkMul;
+ uint16_t TxAbt;
+ uint16_t TxUndrn;
+} RTL8139TallyCounters;
+
+/* Clears all tally counters */
+static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
+
+/* Writes tally counters to specified physical memory address */
+static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* counters);
+
+/* Loads values of tally counters from VM state file */
+static void RTL8139TallyCounters_load(QEMUFile* f, RTL8139TallyCounters *tally_counters);
+
+/* Saves values of tally counters to VM state file */
+static void RTL8139TallyCounters_save(QEMUFile* f, RTL8139TallyCounters *tally_counters);
+
+typedef struct RTL8139State {
+ uint8_t phys[8]; /* mac address */
+ uint8_t mult[8]; /* multicast mask array */
+
+ uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */
+ uint32_t TxAddr[4]; /* TxAddr0 */
+ uint32_t RxBuf; /* Receive buffer */
+ uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */
+ uint32_t RxBufPtr;
+ uint32_t RxBufAddr;
+
+ uint16_t IntrStatus;
+ uint16_t IntrMask;
+
+ uint32_t TxConfig;
+ uint32_t RxConfig;
+ uint32_t RxMissed;
+
+ uint16_t CSCR;
+
+ uint8_t Cfg9346;
+ uint8_t Config0;
+ uint8_t Config1;
+ uint8_t Config3;
+ uint8_t Config4;
+ uint8_t Config5;
+
+ uint8_t clock_enabled;
+ uint8_t bChipCmdState;
+
+ uint16_t MultiIntr;
+
+ uint16_t BasicModeCtrl;
+ uint16_t BasicModeStatus;
+ uint16_t NWayAdvert;
+ uint16_t NWayLPAR;
+ uint16_t NWayExpansion;
+
+ uint16_t CpCmd;
+ uint8_t TxThresh;
+
+ int irq;
+ PCIDevice *pci_dev;
+ VLANClientState *vc;
+ uint8_t macaddr[6];
+ int rtl8139_mmio_io_addr;
+
+ /* C ring mode */
+ uint32_t currTxDesc;
+
+ /* C+ mode */
+ uint32_t currCPlusRxDesc;
+ uint32_t currCPlusTxDesc;
+
+ uint32_t RxRingAddrLO;
+ uint32_t RxRingAddrHI;
+
+ EEprom9346 eeprom;
+
+ uint32_t TCTR;
+ uint32_t TimerInt;
+ int64_t TCTR_base;
+
+ /* Tally counters */
+ RTL8139TallyCounters tally_counters;
+
+ /* Non-persistent data */
+ uint8_t *cplus_txbuffer;
+ int cplus_txbuffer_len;
+ int cplus_txbuffer_offset;
+
+ /* PCI interrupt timer */
+ QEMUTimer *timer;
+
+} RTL8139State;
+
+void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
+{
+ DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command));
+
+ switch (command & Chip9346_op_mask)
+ {
+ case Chip9346_op_read:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->eedo = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_data_read;
+ DEBUG_PRINT(("RTL8139: eeprom read from address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output));
+ }
+ break;
+
+ case Chip9346_op_write:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_none; /* Chip9346_data_write */
+ DEBUG_PRINT(("RTL8139: eeprom begin write to address 0x%02x\n",
+ eeprom->address));
+ }
+ break;
+ default:
+ eeprom->mode = Chip9346_none;
+ switch (command & Chip9346_op_ext_mask)
+ {
+ case Chip9346_op_write_enable:
+ DEBUG_PRINT(("RTL8139: eeprom write enabled\n"));
+ break;
+ case Chip9346_op_write_all:
+ DEBUG_PRINT(("RTL8139: eeprom begin write all\n"));
+ break;
+ case Chip9346_op_write_disable:
+ DEBUG_PRINT(("RTL8139: eeprom write disabled\n"));
+ break;
+ }
+ break;
+ }
+}
+
+void prom9346_shift_clock(EEprom9346 *eeprom)
+{
+ int bit = eeprom->eedi?1:0;
+
+ ++ eeprom->tick;
+
+ DEBUG_PRINT(("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo));
+
+ switch (eeprom->mode)
+ {
+ case Chip9346_enter_command_mode:
+ if (bit)
+ {
+ eeprom->mode = Chip9346_read_command;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ DEBUG_PRINT(("eeprom: +++ synchronized, begin command read\n"));
+ }
+ break;
+
+ case Chip9346_read_command:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 8)
+ {
+ prom9346_decode_command(eeprom, eeprom->input & 0xff);
+ }
+ break;
+
+ case Chip9346_data_read:
+ eeprom->eedo = (eeprom->output & 0x8000)?1:0;
+ eeprom->output <<= 1;
+ if (eeprom->tick == 16)
+ {
+#if 1
+ // the FreeBSD drivers (rl and re) don't explicitly toggle
+ // CS between reads (or does setting Cfg9346 to 0 count too?),
+ // so we need to enter wait-for-command state here
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+
+ DEBUG_PRINT(("eeprom: +++ end of read, awaiting next command\n"));
+#else
+ // original behaviour
+ ++eeprom->address;
+ eeprom->address &= EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->tick = 0;
+
+ DEBUG_PRINT(("eeprom: +++ read next address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output));
+#endif
+ }
+ break;
+
+ case Chip9346_data_write:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16)
+ {
+ DEBUG_PRINT(("RTL8139: eeprom write to address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->input));
+
+ eeprom->contents[eeprom->address] = eeprom->input;
+ eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ case Chip9346_data_write_all:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16)
+ {
+ int i;
+ for (i = 0; i < EEPROM_9346_SIZE; i++)
+ {
+ eeprom->contents[i] = eeprom->input;
+ }
+ DEBUG_PRINT(("RTL8139: eeprom filled with data=0x%04x\n",
+ eeprom->input));
+
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+int prom9346_get_wire(RTL8139State *s)
+{
+ EEprom9346 *eeprom = &s->eeprom;
+ if (!eeprom->eecs)
+ return 0;
+
+ return eeprom->eedo;
+}
+
+void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi)
+{
+ EEprom9346 *eeprom = &s->eeprom;
+ uint8_t old_eecs = eeprom->eecs;
+ uint8_t old_eesk = eeprom->eesk;
+
+ eeprom->eecs = eecs;
+ eeprom->eesk = eesk;
+ eeprom->eedi = eedi;
+
+ DEBUG_PRINT(("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n",
+ eeprom->eecs, eeprom->eesk, eeprom->eedi, eeprom->eedo));
+
+ if (!old_eecs && eecs)
+ {
+ /* Synchronize start */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ eeprom->output = 0;
+ eeprom->mode = Chip9346_enter_command_mode;
+
+ DEBUG_PRINT(("=== eeprom: begin access, enter command mode\n"));
+ }
+
+ if (!eecs)
+ {
+ DEBUG_PRINT(("=== eeprom: end access\n"));
+ return;
+ }
+
+ if (!old_eesk && eesk)
+ {
+ /* SK front rules */
+ prom9346_shift_clock(eeprom);
+ }
+}
+
+static void rtl8139_update_irq(RTL8139State *s)
+{
+ int isr;
+ isr = (s->IntrStatus & s->IntrMask) & 0xffff;
+
+ DEBUG_PRINT(("RTL8139: Set IRQ line %d to %d (%04x %04x)\n",
+ s->irq, isr ? 1 : 0, s->IntrStatus, s->IntrMask));
+
+ if (s->irq == 16) {
+ /* PCI irq */
+ pci_set_irq(s->pci_dev, 0, (isr != 0));
+ } else {
+ /* ISA irq */
+ pic_set_irq(s->irq, (isr != 0));
+ }
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static int compute_mcast_idx(const uint8_t *ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return (crc >> 26);
+}
+
+static int rtl8139_RxWrap(RTL8139State *s)
+{
+ /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */
+ return (s->RxConfig & (1 << 7));
+}
+
+static int rtl8139_receiver_enabled(RTL8139State *s)
+{
+ return s->bChipCmdState & CmdRxEnb;
+}
+
+static int rtl8139_transmitter_enabled(RTL8139State *s)
+{
+ return s->bChipCmdState & CmdTxEnb;
+}
+
+static int rtl8139_cp_receiver_enabled(RTL8139State *s)
+{
+ return s->CpCmd & CPlusRxEnb;
+}
+
+static int rtl8139_cp_transmitter_enabled(RTL8139State *s)
+{
+ return s->CpCmd & CPlusTxEnb;
+}
+
+static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
+{
+ if (s->RxBufAddr + size > s->RxBufferSize)
+ {
+ int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize);
+
+ /* write packet data */
+ if (wrapped && s->RxBufferSize < 65536 && !rtl8139_RxWrap(s))
+ {
+ DEBUG_PRINT((">>> RTL8139: rx packet wrapped in buffer at %d\n", size-wrapped));
+
+ if (size > wrapped)
+ {
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr,
+ buf, size-wrapped );
+ }
+
+ /* reset buffer pointer */
+ s->RxBufAddr = 0;
+
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr,
+ buf + (size-wrapped), wrapped );
+
+ s->RxBufAddr = wrapped;
+
+ return;
+ }
+ }
+
+ /* non-wrapping path or overwrapping enabled */
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, buf, size );
+
+ s->RxBufAddr += size;
+}
+
+#define MIN_BUF_SIZE 60
+static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+ return low | ((target_phys_addr_t)high << 32);
+#else
+ return low;
+#endif
+}
+
+static int rtl8139_can_receive(void *opaque)
+{
+ RTL8139State *s = opaque;
+ int avail;
+
+ /* Recieve (drop) packets if card is disabled. */
+ if (!s->clock_enabled)
+ return 1;
+ if (!rtl8139_receiver_enabled(s))
+ return 1;
+
+ if (rtl8139_cp_receiver_enabled(s)) {
+ /* ??? Flow control not implemented in c+ mode.
+ This is a hack to work around slirp deficiencies anyway. */
+ return 1;
+ } else {
+ avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr,
+ s->RxBufferSize);
+ return (avail == 0 || avail >= 1514);
+ }
+}
+
+static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int do_interrupt)
+{
+ RTL8139State *s = opaque;
+
+ uint32_t packet_header = 0;
+
+ uint8_t buf1[60];
+ static const uint8_t broadcast_macaddr[6] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ DEBUG_PRINT((">>> RTL8139: received len=%d\n", size));
+
+ /* test if board clock is stopped */
+ if (!s->clock_enabled)
+ {
+ DEBUG_PRINT(("RTL8139: stopped ==========================\n"));
+ return;
+ }
+
+ /* first check if receiver is enabled */
+
+ if (!rtl8139_receiver_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: receiver disabled ================\n"));
+ return;
+ }
+
+ /* XXX: check this */
+ if (s->RxConfig & AcceptAllPhys) {
+ /* promiscuous: receive all */
+ DEBUG_PRINT((">>> RTL8139: packet received in promiscuous mode\n"));
+
+ } else {
+ if (!memcmp(buf, broadcast_macaddr, 6)) {
+ /* broadcast address */
+ if (!(s->RxConfig & AcceptBroadcast))
+ {
+ DEBUG_PRINT((">>> RTL8139: broadcast packet rejected\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return;
+ }
+
+ packet_header |= RxBroadcast;
+
+ DEBUG_PRINT((">>> RTL8139: broadcast packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkBrd;
+
+ } else if (buf[0] & 0x01) {
+ /* multicast */
+ if (!(s->RxConfig & AcceptMulticast))
+ {
+ DEBUG_PRINT((">>> RTL8139: multicast packet rejected\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return;
+ }
+
+ int mcast_idx = compute_mcast_idx(buf);
+
+ if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+ {
+ DEBUG_PRINT((">>> RTL8139: multicast address mismatch\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return;
+ }
+
+ packet_header |= RxMulticast;
+
+ DEBUG_PRINT((">>> RTL8139: multicast packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkMul;
+
+ } else if (s->phys[0] == buf[0] &&
+ s->phys[1] == buf[1] &&
+ s->phys[2] == buf[2] &&
+ s->phys[3] == buf[3] &&
+ s->phys[4] == buf[4] &&
+ s->phys[5] == buf[5]) {
+ /* match */
+ if (!(s->RxConfig & AcceptMyPhys))
+ {
+ DEBUG_PRINT((">>> RTL8139: rejecting physical address matching packet\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return;
+ }
+
+ packet_header |= RxPhysical;
+
+ DEBUG_PRINT((">>> RTL8139: physical address matching packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkPhy;
+
+ } else {
+
+ DEBUG_PRINT((">>> RTL8139: unknown packet\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return;
+ }
+ }
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ if (rtl8139_cp_receiver_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: in C+ Rx mode ================\n"));
+
+ /* begin C+ receiver mode */
+
+/* w0 ownership flag */
+#define CP_RX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_RX_EOR (1<<30)
+/* w0 bits 0...12 : buffer size */
+#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)
+/* w1 tag available flag */
+#define CP_RX_TAVA (1<<16)
+/* w1 bits 0...15 : VLAN tag */
+#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low 32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+ int descriptor = s->currCPlusRxDesc;
+ target_phys_addr_t cplus_rx_ring_desc;
+
+ cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);
+ cplus_rx_ring_desc += 16 * descriptor;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode reading RX descriptor %d from host memory at %08x %08x = %016" PRIx64 "\n",
+ descriptor, s->RxRingAddrHI, s->RxRingAddrLO, (uint64_t)cplus_rx_ring_desc));
+
+ uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;
+
+ cpu_physical_memory_read(cplus_rx_ring_desc, (uint8_t *)&val, 4);
+ rxdw0 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
+ rxdw1 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+8, (uint8_t *)&val, 4);
+ rxbufLO = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+12, (uint8_t *)&val, 4);
+ rxbufHI = le32_to_cpu(val);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",
+ descriptor,
+ rxdw0, rxdw1, rxbufLO, rxbufHI));
+
+ if (!(rxdw0 & CP_RX_OWN))
+ {
+ DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d is owned by host\n", descriptor));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+ ++s->tally_counters.MissPkt;
+
+ rtl8139_update_irq(s);
+ return;
+ }
+
+ uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
+
+ /* TODO: scatter the packet over available receive ring descriptors space */
+
+ if (size+4 > rx_space)
+ {
+ DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d size %d received %d + 4\n",
+ descriptor, rx_space, size));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+ ++s->tally_counters.MissPkt;
+
+ rtl8139_update_irq(s);
+ return;
+ }
+
+ target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
+
+ /* receive/copy to target memory */
+ cpu_physical_memory_write( rx_addr, buf, size );
+
+ if (s->CpCmd & CPlusRxChkSum)
+ {
+ /* do some packet checksumming */
+ }
+
+ /* write checksum */
+#if defined (RTL8139_CALCULATE_RXCRC)
+ val = cpu_to_le32(crc32(~0, buf, size));
+#else
+ val = 0;
+#endif
+ cpu_physical_memory_write( rx_addr+size, (uint8_t *)&val, 4);
+
+/* first segment of received packet flag */
+#define CP_RX_STATUS_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_RX_STATUS_LS (1<<28)
+/* multicast packet flag */
+#define CP_RX_STATUS_MAR (1<<26)
+/* physical-matching packet flag */
+#define CP_RX_STATUS_PAM (1<<25)
+/* broadcast packet flag */
+#define CP_RX_STATUS_BAR (1<<24)
+/* runt packet flag */
+#define CP_RX_STATUS_RUNT (1<<19)
+/* crc error flag */
+#define CP_RX_STATUS_CRC (1<<18)
+/* IP checksum error flag */
+#define CP_RX_STATUS_IPF (1<<15)
+/* UDP checksum error flag */
+#define CP_RX_STATUS_UDPF (1<<14)
+/* TCP checksum error flag */
+#define CP_RX_STATUS_TCPF (1<<13)
+
+ /* transfer ownership to target */
+ rxdw0 &= ~CP_RX_OWN;
+
+ /* set first segment bit */
+ rxdw0 |= CP_RX_STATUS_FS;
+
+ /* set last segment bit */
+ rxdw0 |= CP_RX_STATUS_LS;
+
+ /* set received packet type flags */
+ if (packet_header & RxBroadcast)
+ rxdw0 |= CP_RX_STATUS_BAR;
+ if (packet_header & RxMulticast)
+ rxdw0 |= CP_RX_STATUS_MAR;
+ if (packet_header & RxPhysical)
+ rxdw0 |= CP_RX_STATUS_PAM;
+
+ /* set received size */
+ rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
+ rxdw0 |= (size+4);
+
+ /* reset VLAN tag flag */
+ rxdw1 &= ~CP_RX_TAVA;
+
+ /* update ring data */
+ val = cpu_to_le32(rxdw0);
+ cpu_physical_memory_write(cplus_rx_ring_desc, (uint8_t *)&val, 4);
+ val = cpu_to_le32(rxdw1);
+ cpu_physical_memory_write(cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
+
+ /* update tally counter */
+ ++s->tally_counters.RxOk;
+
+ /* seek to next Rx descriptor */
+ if (rxdw0 & CP_RX_EOR)
+ {
+ s->currCPlusRxDesc = 0;
+ }
+ else
+ {
+ ++s->currCPlusRxDesc;
+ }
+
+ DEBUG_PRINT(("RTL8139: done C+ Rx mode ----------------\n"));
+
+ }
+ else
+ {
+ DEBUG_PRINT(("RTL8139: in ring Rx mode ================\n"));
+
+ /* begin ring receiver mode */
+ int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize);
+
+ /* if receiver buffer is empty then avail == 0 */
+
+ if (avail != 0 && size + 8 >= avail)
+ {
+ DEBUG_PRINT(("rx overflow: rx buffer length %d head 0x%04x read 0x%04x === available 0x%04x need 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+ rtl8139_update_irq(s);
+ return;
+ }
+
+ packet_header |= RxStatusOK;
+
+ packet_header |= (((size+4) << 16) & 0xffff0000);
+
+ /* write header */
+ uint32_t val = cpu_to_le32(packet_header);
+
+ rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+ rtl8139_write_buffer(s, buf, size);
+
+ /* write checksum */
+#if defined (RTL8139_CALCULATE_RXCRC)
+ val = cpu_to_le32(crc32(~0, buf, size));
+#else
+ val = 0;
+#endif
+
+ rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+ /* correct buffer write pointer */
+ s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
+
+ /* now we can signal we have received something */
+
+ DEBUG_PRINT((" received: rx buffer length %d head 0x%04x read 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr));
+ }
+
+ s->IntrStatus |= RxOK;
+
+ if (do_interrupt)
+ {
+ rtl8139_update_irq(s);
+ }
+}
+
+static void rtl8139_receive(void *opaque, const uint8_t *buf, int size)
+{
+ rtl8139_do_receive(opaque, buf, size, 1);
+}
+
+static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
+{
+ s->RxBufferSize = bufferSize;
+ s->RxBufPtr = 0;
+ s->RxBufAddr = 0;
+}
+
+static void rtl8139_reset(RTL8139State *s)
+{
+ int i;
+
+ /* restore MAC address */
+ memcpy(s->phys, s->macaddr, 6);
+
+ /* reset interrupt mask */
+ s->IntrStatus = 0;
+ s->IntrMask = 0;
+
+ rtl8139_update_irq(s);
+
+ /* prepare eeprom */
+ s->eeprom.contents[0] = 0x8129;
+#if 1
+ // PCI vendor and device ID should be mirrored here
+ s->eeprom.contents[1] = 0x10ec;
+ s->eeprom.contents[2] = 0x8139;
+#endif
+ memcpy(&s->eeprom.contents[7], s->macaddr, 6);
+
+ /* mark all status registers as owned by host */
+ for (i = 0; i < 4; ++i)
+ {
+ s->TxStatus[i] = TxHostOwns;
+ }
+
+ s->currTxDesc = 0;
+ s->currCPlusRxDesc = 0;
+ s->currCPlusTxDesc = 0;
+
+ s->RxRingAddrLO = 0;
+ s->RxRingAddrHI = 0;
+
+ s->RxBuf = 0;
+
+ rtl8139_reset_rxring(s, 8192);
+
+ /* ACK the reset */
+ s->TxConfig = 0;
+
+#if 0
+// s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139 HasHltClk
+ s->clock_enabled = 0;
+#else
+ s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
+ s->clock_enabled = 1;
+#endif
+
+ s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;
+
+ /* set initial state data */
+ s->Config0 = 0x0; /* No boot ROM */
+ s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
+ s->Config3 = 0x1; /* fast back-to-back compatible */
+ s->Config5 = 0x0;
+
+ s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
+
+ s->CpCmd = 0x0; /* reset C+ mode */
+
+// s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
+// s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
+ s->BasicModeCtrl = 0x1000; // autonegotiation
+
+ s->BasicModeStatus = 0x7809;
+ //s->BasicModeStatus |= 0x0040; /* UTP medium */
+ s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
+ s->BasicModeStatus |= 0x0004; /* link is up */
+
+ s->NWayAdvert = 0x05e1; /* all modes, full duplex */
+ s->NWayLPAR = 0x05e1; /* all modes, full duplex */
+ s->NWayExpansion = 0x0001; /* autonegotiation supported */
+
+ /* also reset timer and disable timer interrupt */
+ s->TCTR = 0;
+ s->TimerInt = 0;
+ s->TCTR_base = 0;
+
+ /* reset tally counters */
+ RTL8139TallyCounters_clear(&s->tally_counters);
+}
+
+void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
+{
+ counters->TxOk = 0;
+ counters->RxOk = 0;
+ counters->TxERR = 0;
+ counters->RxERR = 0;
+ counters->MissPkt = 0;
+ counters->FAE = 0;
+ counters->Tx1Col = 0;
+ counters->TxMCol = 0;
+ counters->RxOkPhy = 0;
+ counters->RxOkBrd = 0;
+ counters->RxOkMul = 0;
+ counters->TxAbt = 0;
+ counters->TxUndrn = 0;
+}
+
+static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* tally_counters)
+{
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+
+ val64 = cpu_to_le64(tally_counters->TxOk);
+ cpu_physical_memory_write(tc_addr + 0, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->RxOk);
+ cpu_physical_memory_write(tc_addr + 8, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->TxERR);
+ cpu_physical_memory_write(tc_addr + 16, (uint8_t *)&val64, 8);
+
+ val32 = cpu_to_le32(tally_counters->RxERR);
+ cpu_physical_memory_write(tc_addr + 24, (uint8_t *)&val32, 4);
+
+ val16 = cpu_to_le16(tally_counters->MissPkt);
+ cpu_physical_memory_write(tc_addr + 28, (uint8_t *)&val16, 2);
+
+ val16 = cpu_to_le16(tally_counters->FAE);
+ cpu_physical_memory_write(tc_addr + 30, (uint8_t *)&val16, 2);
+
+ val32 = cpu_to_le32(tally_counters->Tx1Col);
+ cpu_physical_memory_write(tc_addr + 32, (uint8_t *)&val32, 4);
+
+ val32 = cpu_to_le32(tally_counters->TxMCol);
+ cpu_physical_memory_write(tc_addr + 36, (uint8_t *)&val32, 4);
+
+ val64 = cpu_to_le64(tally_counters->RxOkPhy);
+ cpu_physical_memory_write(tc_addr + 40, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->RxOkBrd);
+ cpu_physical_memory_write(tc_addr + 48, (uint8_t *)&val64, 8);
+
+ val32 = cpu_to_le32(tally_counters->RxOkMul);
+ cpu_physical_memory_write(tc_addr + 56, (uint8_t *)&val32, 4);
+
+ val16 = cpu_to_le16(tally_counters->TxAbt);
+ cpu_physical_memory_write(tc_addr + 60, (uint8_t *)&val16, 2);
+
+ val16 = cpu_to_le16(tally_counters->TxUndrn);
+ cpu_physical_memory_write(tc_addr + 62, (uint8_t *)&val16, 2);
+}
+
+/* Loads values of tally counters from VM state file */
+static void RTL8139TallyCounters_load(QEMUFile* f, RTL8139TallyCounters *tally_counters)
+{
+ qemu_get_be64s(f, &tally_counters->TxOk);
+ qemu_get_be64s(f, &tally_counters->RxOk);
+ qemu_get_be64s(f, &tally_counters->TxERR);
+ qemu_get_be32s(f, &tally_counters->RxERR);
+ qemu_get_be16s(f, &tally_counters->MissPkt);
+ qemu_get_be16s(f, &tally_counters->FAE);
+ qemu_get_be32s(f, &tally_counters->Tx1Col);
+ qemu_get_be32s(f, &tally_counters->TxMCol);
+ qemu_get_be64s(f, &tally_counters->RxOkPhy);
+ qemu_get_be64s(f, &tally_counters->RxOkBrd);
+ qemu_get_be32s(f, &tally_counters->RxOkMul);
+ qemu_get_be16s(f, &tally_counters->TxAbt);
+ qemu_get_be16s(f, &tally_counters->TxUndrn);
+}
+
+/* Saves values of tally counters to VM state file */
+static void RTL8139TallyCounters_save(QEMUFile* f, RTL8139TallyCounters *tally_counters)
+{
+ qemu_put_be64s(f, &tally_counters->TxOk);
+ qemu_put_be64s(f, &tally_counters->RxOk);
+ qemu_put_be64s(f, &tally_counters->TxERR);
+ qemu_put_be32s(f, &tally_counters->RxERR);
+ qemu_put_be16s(f, &tally_counters->MissPkt);
+ qemu_put_be16s(f, &tally_counters->FAE);
+ qemu_put_be32s(f, &tally_counters->Tx1Col);
+ qemu_put_be32s(f, &tally_counters->TxMCol);
+ qemu_put_be64s(f, &tally_counters->RxOkPhy);
+ qemu_put_be64s(f, &tally_counters->RxOkBrd);
+ qemu_put_be32s(f, &tally_counters->RxOkMul);
+ qemu_put_be16s(f, &tally_counters->TxAbt);
+ qemu_put_be16s(f, &tally_counters->TxUndrn);
+}
+
+static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: ChipCmd write val=0x%08x\n", val));
+
+ if (val & CmdReset)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd reset\n"));
+ rtl8139_reset(s);
+ }
+ if (val & CmdRxEnb)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd enable receiver\n"));
+
+ s->currCPlusRxDesc = 0;
+ }
+ if (val & CmdTxEnb)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd enable transmitter\n"));
+
+ s->currCPlusTxDesc = 0;
+ }
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xe3, s->bChipCmdState);
+
+ /* Deassert reset pin before next read */
+ val &= ~CmdReset;
+
+ s->bChipCmdState = val;
+}
+
+static int rtl8139_RxBufferEmpty(RTL8139State *s)
+{
+ int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize);
+
+ if (unread != 0)
+ {
+ DEBUG_PRINT(("RTL8139: receiver buffer data available 0x%04x\n", unread));
+ return 0;
+ }
+
+ DEBUG_PRINT(("RTL8139: receiver buffer is empty\n"));
+
+ return 1;
+}
+
+static uint32_t rtl8139_ChipCmd_read(RTL8139State *s)
+{
+ uint32_t ret = s->bChipCmdState;
+
+ if (rtl8139_RxBufferEmpty(s))
+ ret |= RxBufEmpty;
+
+ DEBUG_PRINT(("RTL8139: ChipCmd read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xff84, s->CpCmd);
+
+ s->CpCmd = val;
+}
+
+static uint32_t rtl8139_CpCmd_read(RTL8139State *s)
+{
+ uint32_t ret = s->CpCmd;
+
+ DEBUG_PRINT(("RTL8139C+ command register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139C+ IntrMitigate register write(w) val=0x%04x\n", val));
+}
+
+static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s)
+{
+ uint32_t ret = 0;
+
+ DEBUG_PRINT(("RTL8139C+ IntrMitigate register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+int rtl8139_config_writeable(RTL8139State *s)
+{
+ if (s->Cfg9346 & Cfg9346_Unlock)
+ {
+ return 1;
+ }
+
+ DEBUG_PRINT(("RTL8139: Configuration registers are write-protected\n"));
+
+ return 0;
+}
+
+static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139: BasicModeCtrl register write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ uint32 mask = 0x4cff;
+
+ if (1 || !rtl8139_config_writeable(s))
+ {
+ /* Speed setting and autonegotiation enable bits are read-only */
+ mask |= 0x3000;
+ /* Duplex mode setting is read-only */
+ mask |= 0x0100;
+ }
+
+ val = SET_MASKED(val, mask, s->BasicModeCtrl);
+
+ s->BasicModeCtrl = val;
+}
+
+static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
+{
+ uint32_t ret = s->BasicModeCtrl;
+
+ DEBUG_PRINT(("RTL8139: BasicModeCtrl register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139: BasicModeStatus register write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xff3f, s->BasicModeStatus);
+
+ s->BasicModeStatus = val;
+}
+
+static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
+{
+ uint32_t ret = s->BasicModeStatus;
+
+ DEBUG_PRINT(("RTL8139: BasicModeStatus register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Cfg9346 write val=0x%02x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x31, s->Cfg9346);
+
+ uint32_t opmode = val & 0xc0;
+ uint32_t eeprom_val = val & 0xf;
+
+ if (opmode == 0x80) {
+ /* eeprom access */
+ int eecs = (eeprom_val & 0x08)?1:0;
+ int eesk = (eeprom_val & 0x04)?1:0;
+ int eedi = (eeprom_val & 0x02)?1:0;
+ prom9346_set_wire(s, eecs, eesk, eedi);
+ } else if (opmode == 0x40) {
+ /* Reset. */
+ val = 0;
+ rtl8139_reset(s);
+ }
+
+ s->Cfg9346 = val;
+}
+
+static uint32_t rtl8139_Cfg9346_read(RTL8139State *s)
+{
+ uint32_t ret = s->Cfg9346;
+
+ uint32_t opmode = ret & 0xc0;
+
+ if (opmode == 0x80)
+ {
+ /* eeprom access */
+ int eedo = prom9346_get_wire(s);
+ if (eedo)
+ {
+ ret |= 0x01;
+ }
+ else
+ {
+ ret &= ~0x01;
+ }
+ }
+
+ DEBUG_PRINT(("RTL8139: Cfg9346 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config0_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config0 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf8, s->Config0);
+
+ s->Config0 = val;
+}
+
+static uint32_t rtl8139_Config0_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config0;
+
+ DEBUG_PRINT(("RTL8139: Config0 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config1_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config1 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xC, s->Config1);
+
+ s->Config1 = val;
+}
+
+static uint32_t rtl8139_Config1_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config1;
+
+ DEBUG_PRINT(("RTL8139: Config1 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config3_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config3 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x8F, s->Config3);
+
+ s->Config3 = val;
+}
+
+static uint32_t rtl8139_Config3_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config3;
+
+ DEBUG_PRINT(("RTL8139: Config3 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config4_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config4 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x0a, s->Config4);
+
+ s->Config4 = val;
+}
+
+static uint32_t rtl8139_Config4_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config4;
+
+ DEBUG_PRINT(("RTL8139: Config4 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config5_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config5 write val=0x%02x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x80, s->Config5);
+
+ s->Config5 = val;
+}
+
+static uint32_t rtl8139_Config5_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config5;
+
+ DEBUG_PRINT(("RTL8139: Config5 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: transmitter disabled; no TxConfig write val=0x%08x\n", val));
+ return;
+ }
+
+ DEBUG_PRINT(("RTL8139: TxConfig write val=0x%08x\n", val));
+
+ val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
+
+ s->TxConfig = val;
+}
+
+static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139C TxConfig via write(b) val=0x%02x\n", val));
+
+ uint32_t tc = s->TxConfig;
+ tc &= 0xFFFFFF00;
+ tc |= (val & 0x000000FF);
+ rtl8139_TxConfig_write(s, tc);
+}
+
+static uint32_t rtl8139_TxConfig_read(RTL8139State *s)
+{
+ uint32_t ret = s->TxConfig;
+
+ DEBUG_PRINT(("RTL8139: TxConfig read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxConfig write val=0x%08x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);
+
+ s->RxConfig = val;
+
+ /* reset buffer size and read/write pointers */
+ rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));
+
+ DEBUG_PRINT(("RTL8139: RxConfig write reset buffer size to %d\n", s->RxBufferSize));
+}
+
+static uint32_t rtl8139_RxConfig_read(RTL8139State *s)
+{
+ uint32_t ret = s->RxConfig;
+
+ DEBUG_PRINT(("RTL8139: RxConfig read val=0x%08x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt)
+{
+ if (!size)
+ {
+ DEBUG_PRINT(("RTL8139: +++ empty ethernet frame\n"));
+ return;
+ }
+
+ if (TxLoopBack == (s->TxConfig & TxLoopBack))
+ {
+ DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n"));
+ rtl8139_do_receive(s, buf, size, do_interrupt);
+ }
+ else
+ {
+ qemu_send_packet(s->vc, buf, size);
+ }
+}
+
+static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: transmitter disabled\n",
+ descriptor));
+ return 0;
+ }
+
+ if (s->TxStatus[descriptor] & TxHostOwns)
+ {
+ DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: owned by host (%08x)\n",
+ descriptor, s->TxStatus[descriptor]));
+ return 0;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ transmitting from descriptor %d\n", descriptor));
+
+ int txsize = s->TxStatus[descriptor] & 0x1fff;
+ uint8_t txbuffer[0x2000];
+
+ DEBUG_PRINT(("RTL8139: +++ transmit reading %d bytes from host memory at 0x%08x\n",
+ txsize, s->TxAddr[descriptor]));
+
+ cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize);
+
+ /* Mark descriptor as transferred */
+ s->TxStatus[descriptor] |= TxHostOwns;
+ s->TxStatus[descriptor] |= TxStatOK;
+
+ rtl8139_transfer_frame(s, txbuffer, txsize, 0);
+
+ DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor));
+
+ /* update interrupt */
+ s->IntrStatus |= TxOK;
+ rtl8139_update_irq(s);
+
+ return 1;
+}
+
+/* structures and macros for task offloading */
+typedef struct ip_header
+{
+ uint8_t ip_ver_len; /* version and header length */
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
+ uint32_t ip_src,ip_dst; /* source and dest address */
+} ip_header;
+
+#define IP_HEADER_VERSION_4 4
+#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
+#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
+
+typedef struct tcp_header
+{
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
+ uint32_t th_seq; /* sequence number */
+ uint32_t th_ack; /* acknowledgement number */
+ uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
+} tcp_header;
+
+typedef struct udp_header
+{
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ uint16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
+} udp_header;
+
+typedef struct ip_pseudo_header
+{
+ uint32_t ip_src;
+ uint32_t ip_dst;
+ uint8_t zeros;
+ uint8_t ip_proto;
+ uint16_t ip_payload;
+} ip_pseudo_header;
+
+#define IP_PROTO_TCP 6
+#define IP_PROTO_UDP 17
+
+#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
+#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
+#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
+
+#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
+
+#define TCP_FLAG_FIN 0x01
+#define TCP_FLAG_PUSH 0x08
+
+/* produces ones' complement sum of data */
+static uint16_t ones_complement_sum(uint8_t *data, size_t len)
+{
+ uint32_t result = 0;
+
+ for (; len > 1; data+=2, len-=2)
+ {
+ result += *(uint16_t*)data;
+ }
+
+ /* add the remainder byte */
+ if (len)
+ {
+ uint8_t odd[2] = {*data, 0};
+ result += *(uint16_t*)odd;
+ }
+
+ while (result>>16)
+ result = (result & 0xffff) + (result >> 16);
+
+ return result;
+}
+
+static uint16_t ip_checksum(void *data, size_t len)
+{
+ return ~ones_complement_sum((uint8_t*)data, len);
+}
+
+static int rtl8139_cplus_transmit_one(RTL8139State *s)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode: transmitter disabled\n"));
+ return 0;
+ }
+
+ if (!rtl8139_cp_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode: C+ transmitter disabled\n"));
+ return 0 ;
+ }
+
+ int descriptor = s->currCPlusTxDesc;
+
+ target_phys_addr_t cplus_tx_ring_desc =
+ rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]);
+
+ /* Normal priority ring */
+ cplus_tx_ring_desc += 16 * descriptor;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode reading TX descriptor %d from host memory at %08x0x%08x = 0x%8lx\n",
+ descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc));
+
+ uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
+
+ cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4);
+ txdw0 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4);
+ txdw1 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4);
+ txbufLO = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4);
+ txbufHI = le32_to_cpu(val);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TX descriptor %d %08x %08x %08x %08x\n",
+ descriptor,
+ txdw0, txdw1, txbufLO, txbufHI));
+
+/* w0 ownership flag */
+#define CP_TX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_TX_EOR (1<<30)
+/* first segment of received packet flag */
+#define CP_TX_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_TX_LS (1<<28)
+/* large send packet flag */
+#define CP_TX_LGSEN (1<<27)
+/* large send MSS mask, bits 16...25 */
+#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1)
+
+/* IP checksum offload flag */
+#define CP_TX_IPCS (1<<18)
+/* UDP checksum offload flag */
+#define CP_TX_UDPCS (1<<17)
+/* TCP checksum offload flag */
+#define CP_TX_TCPCS (1<<16)
+
+/* w0 bits 0...15 : buffer size */
+#define CP_TX_BUFFER_SIZE (1<<16)
+#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)
+/* w1 tag available flag */
+#define CP_RX_TAGC (1<<17)
+/* w1 bits 0...15 : VLAN tag */
+#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low 32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+/* set after transmission */
+/* FIFO underrun flag */
+#define CP_TX_STATUS_UNF (1<<25)
+/* transmit error summary flag, valid if set any of three below */
+#define CP_TX_STATUS_TES (1<<23)
+/* out-of-window collision flag */
+#define CP_TX_STATUS_OWC (1<<22)
+/* link failure flag */
+#define CP_TX_STATUS_LNKF (1<<21)
+/* excessive collisions flag */
+#define CP_TX_STATUS_EXC (1<<20)
+
+ if (!(txdw0 & CP_TX_OWN))
+ {
+ DEBUG_PRINT(("RTL8139: C+ Tx mode : descriptor %d is owned by host\n", descriptor));
+ return 0 ;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : transmitting from descriptor %d\n", descriptor));
+
+ if (txdw0 & CP_TX_FS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is first segment descriptor\n", descriptor));
+
+ /* reset internal buffer offset */
+ s->cplus_txbuffer_offset = 0;
+ }
+
+ int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK;
+ target_phys_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI);
+
+ /* make sure we have enough space to assemble the packet */
+ if (!s->cplus_txbuffer)
+ {
+ s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
+ s->cplus_txbuffer = malloc(s->cplus_txbuffer_len);
+ s->cplus_txbuffer_offset = 0;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len));
+ }
+
+ while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
+ {
+ s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE;
+ s->cplus_txbuffer = realloc(s->cplus_txbuffer, s->cplus_txbuffer_len);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len));
+ }
+
+ if (!s->cplus_txbuffer)
+ {
+ /* out of memory */
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmiter failed to reallocate %d bytes\n", s->cplus_txbuffer_len));
+
+ /* update tally counter */
+ ++s->tally_counters.TxERR;
+ ++s->tally_counters.TxAbt;
+
+ return 0;
+ }
+
+ /* append more data to the packet */
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmit reading %d bytes from host memory at %016" PRIx64 " to offset %d\n",
+ txsize, (uint64_t)tx_addr, s->cplus_txbuffer_offset));
+
+ cpu_physical_memory_read(tx_addr, s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize);
+ s->cplus_txbuffer_offset += txsize;
+
+ /* seek to next Rx descriptor */
+ if (txdw0 & CP_TX_EOR)
+ {
+ s->currCPlusTxDesc = 0;
+ }
+ else
+ {
+ ++s->currCPlusTxDesc;
+ if (s->currCPlusTxDesc >= 64)
+ s->currCPlusTxDesc = 0;
+ }
+
+ /* transfer ownership to target */
+ txdw0 &= ~CP_RX_OWN;
+
+ /* reset error indicator bits */
+ txdw0 &= ~CP_TX_STATUS_UNF;
+ txdw0 &= ~CP_TX_STATUS_TES;
+ txdw0 &= ~CP_TX_STATUS_OWC;
+ txdw0 &= ~CP_TX_STATUS_LNKF;
+ txdw0 &= ~CP_TX_STATUS_EXC;
+
+ /* update ring data */
+ val = cpu_to_le32(txdw0);
+ cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4);
+// val = cpu_to_le32(txdw1);
+// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4);
+
+ /* Now decide if descriptor being processed is holding the last segment of packet */
+ if (txdw0 & CP_TX_LS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor));
+
+ /* can transfer fully assembled packet */
+
+ uint8_t *saved_buffer = s->cplus_txbuffer;
+ int saved_size = s->cplus_txbuffer_offset;
+ int saved_buffer_len = s->cplus_txbuffer_len;
+
+ /* reset the card space to protect from recursive call */
+ s->cplus_txbuffer = NULL;
+ s->cplus_txbuffer_offset = 0;
+ s->cplus_txbuffer_len = 0;
+
+ if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task checksum\n"));
+
+ #define ETH_P_IP 0x0800 /* Internet Protocol packet */
+ #define ETH_HLEN 14
+ #define ETH_MTU 1500
+
+ /* ip packet header */
+ ip_header *ip = 0;
+ int hlen = 0;
+ uint8_t ip_protocol = 0;
+ uint16_t ip_data_len = 0;
+
+ uint8_t *eth_payload_data = 0;
+ size_t eth_payload_len = 0;
+
+ int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
+ if (proto == ETH_P_IP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode has IP packet\n"));
+
+ /* not aligned */
+ eth_payload_data = saved_buffer + ETH_HLEN;
+ eth_payload_len = saved_size - ETH_HLEN;
+
+ ip = (ip_header*)eth_payload_data;
+
+ if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode packet has bad IP version %d expected %d\n", IP_HEADER_VERSION(ip), IP_HEADER_VERSION_4));
+ ip = NULL;
+ } else {
+ hlen = IP_HEADER_LENGTH(ip);
+ ip_protocol = ip->ip_p;
+ ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
+ }
+ }
+
+ if (ip)
+ {
+ if (txdw0 & CP_TX_IPCS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode need IP checksum\n"));
+
+ if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
+ /* bad packet header len */
+ /* or packet too short */
+ }
+ else
+ {
+ ip->ip_sum = 0;
+ ip->ip_sum = ip_checksum(ip, hlen);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode IP header len=%d checksum=%04x\n", hlen, ip->ip_sum));
+ }
+ }
+
+ if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
+ {
+#if defined (DEBUG_RTL8139)
+ int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
+#endif
+ DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task TSO MTU=%d IP data %d frame data %d specified MSS=%d\n",
+ ETH_MTU, ip_data_len, saved_size - ETH_HLEN, large_send_mss));
+
+ int tcp_send_offset = 0;
+ int send_count = 0;
+
+ /* maximum IP header length is 60 bytes */
+ uint8_t saved_ip_header[60];
+
+ /* save IP header template; data area is used in tcp checksum calculation */
+ memcpy(saved_ip_header, eth_payload_data, hlen);
+
+ /* a placeholder for checksum calculation routine in tcp case */
+ uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
+ // size_t data_to_checksum_len = eth_payload_len - hlen + 12;
+
+ /* pointer to TCP header */
+ tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
+
+ int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
+
+ /* ETH_MTU = ip header len + tcp header len + payload */
+ int tcp_data_len = ip_data_len - tcp_hlen;
+ int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP data len %d TCP hlen %d TCP data len %d TCP chunk size %d\n",
+ ip_data_len, tcp_hlen, tcp_data_len, tcp_chunk_size));
+
+ /* note the cycle below overwrites IP header data,
+ but restores it from saved_ip_header before sending packet */
+
+ int is_last_frame = 0;
+
+ for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
+ {
+ uint16_t chunk_size = tcp_chunk_size;
+
+ /* check if this is the last frame */
+ if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
+ {
+ is_last_frame = 1;
+ chunk_size = tcp_data_len - tcp_send_offset;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP seqno %08x\n", be32_to_cpu(p_tcp_hdr->th_seq)));
+
+ /* add 4 TCP pseudoheader fields */
+ /* copy IP source and destination fields */
+ memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO calculating TCP checksum for packet with %d bytes data\n", tcp_hlen + chunk_size));
+
+ if (tcp_send_offset)
+ {
+ memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
+ }
+
+ /* keep PUSH and FIN flags only for the last frame */
+ if (!is_last_frame)
+ {
+ TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+ }
+
+ /* recalculate TCP checksum */
+ ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_tcpip_hdr->zeros = 0;
+ p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
+ p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
+
+ p_tcp_hdr->th_sum = 0;
+
+ int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP checksum %04x\n", tcp_checksum));
+
+ p_tcp_hdr->th_sum = tcp_checksum;
+
+ /* restore IP header */
+ memcpy(eth_payload_data, saved_ip_header, hlen);
+
+ /* set IP data length and recalculate IP checksum */
+ ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
+
+ /* increment IP id for subsequent frames */
+ ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
+
+ ip->ip_sum = 0;
+ ip->ip_sum = ip_checksum(eth_payload_data, hlen);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP header len=%d checksum=%04x\n", hlen, ip->ip_sum));
+
+ int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size));
+ rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0);
+
+ /* add transferred count to TCP sequence number */
+ p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+ ++send_count;
+ }
+
+ /* Stop sending this frame */
+ saved_size = 0;
+ }
+ else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n"));
+
+ /* maximum IP header length is 60 bytes */
+ uint8_t saved_ip_header[60];
+ memcpy(saved_ip_header, eth_payload_data, hlen);
+
+ uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
+ // size_t data_to_checksum_len = eth_payload_len - hlen + 12;
+
+ /* add 4 TCP pseudoheader fields */
+ /* copy IP source and destination fields */
+ memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+ if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode calculating TCP checksum for packet with %d bytes data\n", ip_data_len));
+
+ ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_tcpip_hdr->zeros = 0;
+ p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
+ p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+ tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
+
+ p_tcp_hdr->th_sum = 0;
+
+ int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TCP checksum %04x\n", tcp_checksum));
+
+ p_tcp_hdr->th_sum = tcp_checksum;
+ }
+ else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode calculating UDP checksum for packet with %d bytes data\n", ip_data_len));
+
+ ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_udpip_hdr->zeros = 0;
+ p_udpip_hdr->ip_proto = IP_PROTO_UDP;
+ p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+ udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
+
+ p_udp_hdr->uh_sum = 0;
+
+ int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode UDP checksum %04x\n", udp_checksum));
+
+ p_udp_hdr->uh_sum = udp_checksum;
+ }
+
+ /* restore IP header */
+ memcpy(eth_payload_data, saved_ip_header, hlen);
+ }
+ }
+ }
+
+ /* update tally counter */
+ ++s->tally_counters.TxOk;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size));
+
+ rtl8139_transfer_frame(s, saved_buffer, saved_size, 1);
+
+ /* restore card space if there was no recursion and reset offset */
+ if (!s->cplus_txbuffer)
+ {
+ s->cplus_txbuffer = saved_buffer;
+ s->cplus_txbuffer_len = saved_buffer_len;
+ s->cplus_txbuffer_offset = 0;
+ }
+ else
+ {
+ free(saved_buffer);
+ }
+ }
+ else
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission continue to next descriptor\n"));
+ }
+
+ return 1;
+}
+
+static void rtl8139_cplus_transmit(RTL8139State *s)
+{
+ int txcount = 0;
+
+ while (rtl8139_cplus_transmit_one(s))
+ {
+ ++txcount;
+ }
+
+ /* Mark transfer completed */
+ if (!txcount)
+ {
+ DEBUG_PRINT(("RTL8139: C+ mode : transmitter queue stalled, current TxDesc = %d\n",
+ s->currCPlusTxDesc));
+ }
+ else
+ {
+ /* update interrupt status */
+ s->IntrStatus |= TxOK;
+ rtl8139_update_irq(s);
+ }
+}
+
+static void rtl8139_transmit(RTL8139State *s)
+{
+ int descriptor = s->currTxDesc, txcount = 0;
+
+ /*while*/
+ if (rtl8139_transmit_one(s, descriptor))
+ {
+ ++s->currTxDesc;
+ s->currTxDesc %= 4;
+ ++txcount;
+ }
+
+ /* Mark transfer completed */
+ if (!txcount)
+ {
+ DEBUG_PRINT(("RTL8139: transmitter queue stalled, current TxDesc = %d\n", s->currTxDesc));
+ }
+}
+
+static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val)
+{
+
+ int descriptor = txRegOffset/4;
+
+ /* handle C+ transmit mode register configuration */
+
+ if (rtl8139_cp_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139C+ DTCCR write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor));
+
+ /* handle Dump Tally Counters command */
+ s->TxStatus[descriptor] = val;
+
+ if (descriptor == 0 && (val & 0x8))
+ {
+ target_phys_addr_t tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]);
+
+ /* dump tally counters to specified memory location */
+ RTL8139TallyCounters_physical_memory_write( tc_addr, &s->tally_counters);
+
+ /* mark dump completed */
+ s->TxStatus[0] &= ~0x8;
+ }
+
+ return;
+ }
+
+ DEBUG_PRINT(("RTL8139: TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor));
+
+ /* mask only reserved bits */
+ val &= ~0xff00c000; /* these bits are reset on write */
+ val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
+
+ s->TxStatus[descriptor] = val;
+
+ /* attempt to start transmission */
+ rtl8139_transmit(s);
+}
+
+static uint32_t rtl8139_TxStatus_read(RTL8139State *s, uint32_t txRegOffset)
+{
+ uint32_t ret = s->TxStatus[txRegOffset/4];
+
+ DEBUG_PRINT(("RTL8139: TxStatus read offset=0x%x val=0x%08x\n", txRegOffset, ret));
+
+ return ret;
+}
+
+static uint16_t rtl8139_TSAD_read(RTL8139State *s)
+{
+ uint16_t ret = 0;
+
+ /* Simulate TSAD, it is read only anyway */
+
+ ret = ((s->TxStatus[3] & TxStatOK )?TSAD_TOK3:0)
+ |((s->TxStatus[2] & TxStatOK )?TSAD_TOK2:0)
+ |((s->TxStatus[1] & TxStatOK )?TSAD_TOK1:0)
+ |((s->TxStatus[0] & TxStatOK )?TSAD_TOK0:0)
+
+ |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0)
+ |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0)
+ |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0)
+ |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0)
+
+ |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0)
+ |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0)
+ |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0)
+ |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0)
+
+ |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0)
+ |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0)
+ |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0)
+ |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ;
+
+
+ DEBUG_PRINT(("RTL8139: TSAD read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static uint16_t rtl8139_CSCR_read(RTL8139State *s)
+{
+ uint16_t ret = s->CSCR;
+
+ DEBUG_PRINT(("RTL8139: CSCR read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val));
+
+ s->TxAddr[txAddrOffset/4] = le32_to_cpu(val);
+}
+
+static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
+{
+ uint32_t ret = cpu_to_le32(s->TxAddr[txAddrOffset/4]);
+
+ DEBUG_PRINT(("RTL8139: TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret));
+
+ return ret;
+}
+
+static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxBufPtr write val=0x%04x\n", val));
+
+ /* this value is off by 16 */
+ s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize);
+
+ DEBUG_PRINT((" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr));
+}
+
+static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s)
+{
+ /* this value is off by 16 */
+ uint32_t ret = s->RxBufPtr - 0x10;
+
+ DEBUG_PRINT(("RTL8139: RxBufPtr read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
+{
+ /* this value is NOT off by 16 */
+ uint32_t ret = s->RxBufAddr;
+
+ DEBUG_PRINT(("RTL8139: RxBufAddr read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxBuf write val=0x%08x\n", val));
+
+ s->RxBuf = val;
+
+ /* may need to reset rxring here */
+}
+
+static uint32_t rtl8139_RxBuf_read(RTL8139State *s)
+{
+ uint32_t ret = s->RxBuf;
+
+ DEBUG_PRINT(("RTL8139: RxBuf read val=0x%08x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: IntrMask write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x1e00, s->IntrMask);
+
+ s->IntrMask = val;
+
+ rtl8139_update_irq(s);
+}
+
+static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
+{
+ uint32_t ret = s->IntrMask;
+
+ DEBUG_PRINT(("RTL8139: IntrMask read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: IntrStatus write(w) val=0x%04x\n", val));
+
+#if 0
+
+ /* writing to ISR has no effect */
+
+ return;
+
+#else
+ uint16_t newStatus = s->IntrStatus & ~val;
+
+ /* mask unwriteable bits */
+ newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
+
+ /* writing 1 to interrupt status register bit clears it */
+ s->IntrStatus = 0;
+ rtl8139_update_irq(s);
+
+ s->IntrStatus = newStatus;
+ rtl8139_update_irq(s);
+#endif
+}
+
+static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
+{
+ uint32_t ret = s->IntrStatus;
+
+ DEBUG_PRINT(("RTL8139: IntrStatus read(w) val=0x%04x\n", ret));
+
+#if 0
+
+ /* reading ISR clears all interrupts */
+ s->IntrStatus = 0;
+
+ rtl8139_update_irq(s);
+
+#endif
+
+ return ret;
+}
+
+static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: MultiIntr write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf000, s->MultiIntr);
+
+ s->MultiIntr = val;
+}
+
+static uint32_t rtl8139_MultiIntr_read(RTL8139State *s)
+{
+ uint32_t ret = s->MultiIntr;
+
+ DEBUG_PRINT(("RTL8139: MultiIntr read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xff;
+
+ switch (addr)
+ {
+ case MAC0 ... MAC0+5:
+ s->phys[addr - MAC0] = val;
+ break;
+ case MAC0+6 ... MAC0+7:
+ /* reserved */
+ break;
+ case MAR0 ... MAR0+7:
+ s->mult[addr - MAR0] = val;
+ break;
+ case ChipCmd:
+ rtl8139_ChipCmd_write(s, val);
+ break;
+ case Cfg9346:
+ rtl8139_Cfg9346_write(s, val);
+ break;
+ case TxConfig: /* windows driver sometimes writes using byte-lenth call */
+ rtl8139_TxConfig_writeb(s, val);
+ break;
+ case Config0:
+ rtl8139_Config0_write(s, val);
+ break;
+ case Config1:
+ rtl8139_Config1_write(s, val);
+ break;
+ case Config3:
+ rtl8139_Config3_write(s, val);
+ break;
+ case Config4:
+ rtl8139_Config4_write(s, val);
+ break;
+ case Config5:
+ rtl8139_Config5_write(s, val);
+ break;
+ case MediaStatus:
+ /* ignore */
+ DEBUG_PRINT(("RTL8139: not implemented write(b) to MediaStatus val=0x%02x\n", val));
+ break;
+
+ case HltClk:
+ DEBUG_PRINT(("RTL8139: HltClk write val=0x%08x\n", val));
+ if (val == 'R')
+ {
+ s->clock_enabled = 1;
+ }
+ else if (val == 'H')
+ {
+ s->clock_enabled = 0;
+ }
+ break;
+
+ case TxThresh:
+ DEBUG_PRINT(("RTL8139C+ TxThresh write(b) val=0x%02x\n", val));
+ s->TxThresh = val;
+ break;
+
+ case TxPoll:
+ DEBUG_PRINT(("RTL8139C+ TxPoll write(b) val=0x%02x\n", val));
+ if (val & (1 << 7))
+ {
+ DEBUG_PRINT(("RTL8139C+ TxPoll high priority transmission (not implemented)\n"));
+ //rtl8139_cplus_transmit(s);
+ }
+ if (val & (1 << 6))
+ {
+ DEBUG_PRINT(("RTL8139C+ TxPoll normal priority transmission\n"));
+ rtl8139_cplus_transmit(s);
+ }
+
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: not implemented write(b) addr=0x%x val=0x%02x\n", addr, val));
+ break;
+ }
+}
+
+static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xfe;
+
+ switch (addr)
+ {
+ case IntrMask:
+ rtl8139_IntrMask_write(s, val);
+ break;
+
+ case IntrStatus:
+ rtl8139_IntrStatus_write(s, val);
+ break;
+
+ case MultiIntr:
+ rtl8139_MultiIntr_write(s, val);
+ break;
+
+ case RxBufPtr:
+ rtl8139_RxBufPtr_write(s, val);
+ break;
+
+ case BasicModeCtrl:
+ rtl8139_BasicModeCtrl_write(s, val);
+ break;
+ case BasicModeStatus:
+ rtl8139_BasicModeStatus_write(s, val);
+ break;
+ case NWayAdvert:
+ DEBUG_PRINT(("RTL8139: NWayAdvert write(w) val=0x%04x\n", val));
+ s->NWayAdvert = val;
+ break;
+ case NWayLPAR:
+ DEBUG_PRINT(("RTL8139: forbidden NWayLPAR write(w) val=0x%04x\n", val));
+ break;
+ case NWayExpansion:
+ DEBUG_PRINT(("RTL8139: NWayExpansion write(w) val=0x%04x\n", val));
+ s->NWayExpansion = val;
+ break;
+
+ case CpCmd:
+ rtl8139_CpCmd_write(s, val);
+ break;
+
+ case IntrMitigate:
+ rtl8139_IntrMitigate_write(s, val);
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport write(w) addr=0x%x val=0x%04x via write(b)\n", addr, val));
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ rtl8139_io_writeb(opaque, addr, (val >> 8) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, val & 0xff);
+#else
+ rtl8139_io_writeb(opaque, addr, val & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+ break;
+ }
+}
+
+static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xfc;
+
+ switch (addr)
+ {
+ case RxMissed:
+ DEBUG_PRINT(("RTL8139: RxMissed clearing on write\n"));
+ s->RxMissed = 0;
+ break;
+
+ case TxConfig:
+ rtl8139_TxConfig_write(s, val);
+ break;
+
+ case RxConfig:
+ rtl8139_RxConfig_write(s, val);
+ break;
+
+ case TxStatus0 ... TxStatus0+4*4-1:
+ rtl8139_TxStatus_write(s, addr-TxStatus0, val);
+ break;
+
+ case TxAddr0 ... TxAddr0+4*4-1:
+ rtl8139_TxAddr_write(s, addr-TxAddr0, val);
+ break;
+
+ case RxBuf:
+ rtl8139_RxBuf_write(s, val);
+ break;
+
+ case RxRingAddrLO:
+ DEBUG_PRINT(("RTL8139: C+ RxRing low bits write val=0x%08x\n", val));
+ s->RxRingAddrLO = val;
+ break;
+
+ case RxRingAddrHI:
+ DEBUG_PRINT(("RTL8139: C+ RxRing high bits write val=0x%08x\n", val));
+ s->RxRingAddrHI = val;
+ break;
+
+ case Timer:
+ DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n"));
+ s->TCTR = 0;
+ s->TCTR_base = qemu_get_clock(vm_clock);
+ break;
+
+ case FlashReg:
+ DEBUG_PRINT(("RTL8139: FlashReg TimerInt write val=0x%08x\n", val));
+ s->TimerInt = val;
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport write(l) addr=0x%x val=0x%08x via write(b)\n", addr, val));
+#ifdef TARGET_WORDS_BIGENDIAN
+ rtl8139_io_writeb(opaque, addr, (val >> 24) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 3, val & 0xff);
+#else
+ rtl8139_io_writeb(opaque, addr, val & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+ break;
+ }
+}
+
+static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ int ret;
+
+ addr &= 0xff;
+
+ switch (addr)
+ {
+ case MAC0 ... MAC0+5:
+ ret = s->phys[addr - MAC0];
+ break;
+ case MAC0+6 ... MAC0+7:
+ ret = 0;
+ break;
+ case MAR0 ... MAR0+7:
+ ret = s->mult[addr - MAR0];
+ break;
+ case ChipCmd:
+ ret = rtl8139_ChipCmd_read(s);
+ break;
+ case Cfg9346:
+ ret = rtl8139_Cfg9346_read(s);
+ break;
+ case Config0:
+ ret = rtl8139_Config0_read(s);
+ break;
+ case Config1:
+ ret = rtl8139_Config1_read(s);
+ break;
+ case Config3:
+ ret = rtl8139_Config3_read(s);
+ break;
+ case Config4:
+ ret = rtl8139_Config4_read(s);
+ break;
+ case Config5:
+ ret = rtl8139_Config5_read(s);
+ break;
+
+ case MediaStatus:
+ ret = 0xd0;
+ DEBUG_PRINT(("RTL8139: MediaStatus read 0x%x\n", ret));
+ break;
+
+ case HltClk:
+ ret = s->clock_enabled;
+ DEBUG_PRINT(("RTL8139: HltClk read 0x%x\n", ret));
+ break;
+
+ case PCIRevisionID:
+ ret = RTL8139_PCI_REVID;
+ DEBUG_PRINT(("RTL8139: PCI Revision ID read 0x%x\n", ret));
+ break;
+
+ case TxThresh:
+ ret = s->TxThresh;
+ DEBUG_PRINT(("RTL8139C+ TxThresh read(b) val=0x%02x\n", ret));
+ break;
+
+ case 0x43: /* Part of TxConfig register. Windows driver tries to read it */
+ ret = s->TxConfig >> 24;
+ DEBUG_PRINT(("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret));
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: not implemented read(b) addr=0x%x\n", addr));
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ uint32_t ret;
+
+ addr &= 0xfe; /* mask lower bit */
+
+ switch (addr)
+ {
+ case IntrMask:
+ ret = rtl8139_IntrMask_read(s);
+ break;
+
+ case IntrStatus:
+ ret = rtl8139_IntrStatus_read(s);
+ break;
+
+ case MultiIntr:
+ ret = rtl8139_MultiIntr_read(s);
+ break;
+
+ case RxBufPtr:
+ ret = rtl8139_RxBufPtr_read(s);
+ break;
+
+ case RxBufAddr:
+ ret = rtl8139_RxBufAddr_read(s);
+ break;
+
+ case BasicModeCtrl:
+ ret = rtl8139_BasicModeCtrl_read(s);
+ break;
+ case BasicModeStatus:
+ ret = rtl8139_BasicModeStatus_read(s);
+ break;
+ case NWayAdvert:
+ ret = s->NWayAdvert;
+ DEBUG_PRINT(("RTL8139: NWayAdvert read(w) val=0x%04x\n", ret));
+ break;
+ case NWayLPAR:
+ ret = s->NWayLPAR;
+ DEBUG_PRINT(("RTL8139: NWayLPAR read(w) val=0x%04x\n", ret));
+ break;
+ case NWayExpansion:
+ ret = s->NWayExpansion;
+ DEBUG_PRINT(("RTL8139: NWayExpansion read(w) val=0x%04x\n", ret));
+ break;
+
+ case CpCmd:
+ ret = rtl8139_CpCmd_read(s);
+ break;
+
+ case IntrMitigate:
+ ret = rtl8139_IntrMitigate_read(s);
+ break;
+
+ case TxSummary:
+ ret = rtl8139_TSAD_read(s);
+ break;
+
+ case CSCR:
+ ret = rtl8139_CSCR_read(s);
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x via read(b)\n", addr));
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = rtl8139_io_readb(opaque, addr) << 8;
+ ret |= rtl8139_io_readb(opaque, addr + 1);
+#else
+ ret = rtl8139_io_readb(opaque, addr);
+ ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+#endif
+
+ DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x val=0x%04x\n", addr, ret));
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ uint32_t ret;
+
+ addr &= 0xfc; /* also mask low 2 bits */
+
+ switch (addr)
+ {
+ case RxMissed:
+ ret = s->RxMissed;
+
+ DEBUG_PRINT(("RTL8139: RxMissed read val=0x%08x\n", ret));
+ break;
+
+ case TxConfig:
+ ret = rtl8139_TxConfig_read(s);
+ break;
+
+ case RxConfig:
+ ret = rtl8139_RxConfig_read(s);
+ break;
+
+ case TxStatus0 ... TxStatus0+4*4-1:
+ ret = rtl8139_TxStatus_read(s, addr-TxStatus0);
+ break;
+
+ case TxAddr0 ... TxAddr0+4*4-1:
+ ret = rtl8139_TxAddr_read(s, addr-TxAddr0);
+ break;
+
+ case RxBuf:
+ ret = rtl8139_RxBuf_read(s);
+ break;
+
+ case RxRingAddrLO:
+ ret = s->RxRingAddrLO;
+ DEBUG_PRINT(("RTL8139: C+ RxRing low bits read val=0x%08x\n", ret));
+ break;
+
+ case RxRingAddrHI:
+ ret = s->RxRingAddrHI;
+ DEBUG_PRINT(("RTL8139: C+ RxRing high bits read val=0x%08x\n", ret));
+ break;
+
+ case Timer:
+ ret = s->TCTR;
+ DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret));
+ break;
+
+ case FlashReg:
+ ret = s->TimerInt;
+ DEBUG_PRINT(("RTL8139: FlashReg TimerInt read val=0x%08x\n", ret));
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport read(l) addr=0x%x via read(b)\n", addr));
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ ret = rtl8139_io_readb(opaque, addr) << 24;
+ ret |= rtl8139_io_readb(opaque, addr + 1) << 16;
+ ret |= rtl8139_io_readb(opaque, addr + 2) << 8;
+ ret |= rtl8139_io_readb(opaque, addr + 3);
+#else
+ ret = rtl8139_io_readb(opaque, addr);
+ ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+ ret |= rtl8139_io_readb(opaque, addr + 2) << 16;
+ ret |= rtl8139_io_readb(opaque, addr + 3) << 24;
+#endif
+
+ DEBUG_PRINT(("RTL8139: read(l) addr=0x%x val=%08x\n", addr, ret));
+ break;
+ }
+
+ return ret;
+}
+
+/* */
+
+static void rtl8139_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t rtl8139_ioport_readb(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_ioport_readw(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_ioport_readl(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void rtl8139_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t rtl8139_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return rtl8139_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ return rtl8139_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ return rtl8139_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void rtl8139_save(QEMUFile* f,void* opaque)
+{
+ RTL8139State* s=(RTL8139State*)opaque;
+ int i;
+
+ qemu_put_buffer(f, s->phys, 6);
+ qemu_put_buffer(f, s->mult, 8);
+
+ for (i=0; i<4; ++i)
+ {
+ qemu_put_be32s(f, &s->TxStatus[i]); /* TxStatus0 */
+ }
+ for (i=0; i<4; ++i)
+ {
+ qemu_put_be32s(f, &s->TxAddr[i]); /* TxAddr0 */
+ }
+
+ qemu_put_be32s(f, &s->RxBuf); /* Receive buffer */
+ qemu_put_be32s(f, &s->RxBufferSize);/* internal variable, receive ring buffer size in C mode */
+ qemu_put_be32s(f, &s->RxBufPtr);
+ qemu_put_be32s(f, &s->RxBufAddr);
+
+ qemu_put_be16s(f, &s->IntrStatus);
+ qemu_put_be16s(f, &s->IntrMask);
+
+ qemu_put_be32s(f, &s->TxConfig);
+ qemu_put_be32s(f, &s->RxConfig);
+ qemu_put_be32s(f, &s->RxMissed);
+ qemu_put_be16s(f, &s->CSCR);
+
+ qemu_put_8s(f, &s->Cfg9346);
+ qemu_put_8s(f, &s->Config0);
+ qemu_put_8s(f, &s->Config1);
+ qemu_put_8s(f, &s->Config3);
+ qemu_put_8s(f, &s->Config4);
+ qemu_put_8s(f, &s->Config5);
+
+ qemu_put_8s(f, &s->clock_enabled);
+ qemu_put_8s(f, &s->bChipCmdState);
+
+ qemu_put_be16s(f, &s->MultiIntr);
+
+ qemu_put_be16s(f, &s->BasicModeCtrl);
+ qemu_put_be16s(f, &s->BasicModeStatus);
+ qemu_put_be16s(f, &s->NWayAdvert);
+ qemu_put_be16s(f, &s->NWayLPAR);
+ qemu_put_be16s(f, &s->NWayExpansion);
+
+ qemu_put_be16s(f, &s->CpCmd);
+ qemu_put_8s(f, &s->TxThresh);
+
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_buffer(f, s->macaddr, 6);
+ qemu_put_be32s(f, &s->rtl8139_mmio_io_addr);
+
+ qemu_put_be32s(f, &s->currTxDesc);
+ qemu_put_be32s(f, &s->currCPlusRxDesc);
+ qemu_put_be32s(f, &s->currCPlusTxDesc);
+ qemu_put_be32s(f, &s->RxRingAddrLO);
+ qemu_put_be32s(f, &s->RxRingAddrHI);
+
+ for (i=0; i<EEPROM_9346_SIZE; ++i)
+ {
+ qemu_put_be16s(f, &s->eeprom.contents[i]);
+ }
+ qemu_put_be32s(f, &s->eeprom.mode);
+ qemu_put_be32s(f, &s->eeprom.tick);
+ qemu_put_8s(f, &s->eeprom.address);
+ qemu_put_be16s(f, &s->eeprom.input);
+ qemu_put_be16s(f, &s->eeprom.output);
+
+ qemu_put_8s(f, &s->eeprom.eecs);
+ qemu_put_8s(f, &s->eeprom.eesk);
+ qemu_put_8s(f, &s->eeprom.eedi);
+ qemu_put_8s(f, &s->eeprom.eedo);
+
+ qemu_put_be32s(f, &s->TCTR);
+ qemu_put_be32s(f, &s->TimerInt);
+ qemu_put_be64s(f, &s->TCTR_base);
+
+ RTL8139TallyCounters_save(f, &s->tally_counters);
+}
+
+static int rtl8139_load(QEMUFile* f,void* opaque,int version_id)
+{
+ RTL8139State* s=(RTL8139State*)opaque;
+ int i;
+
+ /* just 2 versions for now */
+ if (version_id > 2)
+ return -EINVAL;
+
+ /* saved since version 1 */
+ qemu_get_buffer(f, s->phys, 6);
+ qemu_get_buffer(f, s->mult, 8);
+
+ for (i=0; i<4; ++i)
+ {
+ qemu_get_be32s(f, &s->TxStatus[i]); /* TxStatus0 */
+ }
+ for (i=0; i<4; ++i)
+ {
+ qemu_get_be32s(f, &s->TxAddr[i]); /* TxAddr0 */
+ }
+
+ qemu_get_be32s(f, &s->RxBuf); /* Receive buffer */
+ qemu_get_be32s(f, &s->RxBufferSize);/* internal variable, receive ring buffer size in C mode */
+ qemu_get_be32s(f, &s->RxBufPtr);
+ qemu_get_be32s(f, &s->RxBufAddr);
+
+ qemu_get_be16s(f, &s->IntrStatus);
+ qemu_get_be16s(f, &s->IntrMask);
+
+ qemu_get_be32s(f, &s->TxConfig);
+ qemu_get_be32s(f, &s->RxConfig);
+ qemu_get_be32s(f, &s->RxMissed);
+ qemu_get_be16s(f, &s->CSCR);
+
+ qemu_get_8s(f, &s->Cfg9346);
+ qemu_get_8s(f, &s->Config0);
+ qemu_get_8s(f, &s->Config1);
+ qemu_get_8s(f, &s->Config3);
+ qemu_get_8s(f, &s->Config4);
+ qemu_get_8s(f, &s->Config5);
+
+ qemu_get_8s(f, &s->clock_enabled);
+ qemu_get_8s(f, &s->bChipCmdState);
+
+ qemu_get_be16s(f, &s->MultiIntr);
+
+ qemu_get_be16s(f, &s->BasicModeCtrl);
+ qemu_get_be16s(f, &s->BasicModeStatus);
+ qemu_get_be16s(f, &s->NWayAdvert);
+ qemu_get_be16s(f, &s->NWayLPAR);
+ qemu_get_be16s(f, &s->NWayExpansion);
+
+ qemu_get_be16s(f, &s->CpCmd);
+ qemu_get_8s(f, &s->TxThresh);
+
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_buffer(f, s->macaddr, 6);
+ qemu_get_be32s(f, &s->rtl8139_mmio_io_addr);
+
+ qemu_get_be32s(f, &s->currTxDesc);
+ qemu_get_be32s(f, &s->currCPlusRxDesc);
+ qemu_get_be32s(f, &s->currCPlusTxDesc);
+ qemu_get_be32s(f, &s->RxRingAddrLO);
+ qemu_get_be32s(f, &s->RxRingAddrHI);
+
+ for (i=0; i<EEPROM_9346_SIZE; ++i)
+ {
+ qemu_get_be16s(f, &s->eeprom.contents[i]);
+ }
+ qemu_get_be32s(f, &s->eeprom.mode);
+ qemu_get_be32s(f, &s->eeprom.tick);
+ qemu_get_8s(f, &s->eeprom.address);
+ qemu_get_be16s(f, &s->eeprom.input);
+ qemu_get_be16s(f, &s->eeprom.output);
+
+ qemu_get_8s(f, &s->eeprom.eecs);
+ qemu_get_8s(f, &s->eeprom.eesk);
+ qemu_get_8s(f, &s->eeprom.eedi);
+ qemu_get_8s(f, &s->eeprom.eedo);
+
+ /* saved since version 2 */
+ if (version_id >= 2)
+ {
+ qemu_get_be32s(f, &s->TCTR);
+ qemu_get_be32s(f, &s->TimerInt);
+ qemu_get_be64s(f, &s->TCTR_base);
+
+ RTL8139TallyCounters_load(f, &s->tally_counters);
+ }
+ else
+ {
+ /* not saved, use default */
+ s->TCTR = 0;
+ s->TimerInt = 0;
+ s->TCTR_base = 0;
+
+ RTL8139TallyCounters_clear(&s->tally_counters);
+ }
+
+ return 0;
+}
+
+/***********************************************************/
+/* PCI RTL8139 definitions */
+
+typedef struct PCIRTL8139State {
+ PCIDevice dev;
+ RTL8139State rtl8139;
+} PCIRTL8139State;
+
+static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIRTL8139State *d = (PCIRTL8139State *)pci_dev;
+ RTL8139State *s = &d->rtl8139;
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->rtl8139_mmio_io_addr);
+}
+
+static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIRTL8139State *d = (PCIRTL8139State *)pci_dev;
+ RTL8139State *s = &d->rtl8139;
+
+ register_ioport_write(addr, 0x100, 1, rtl8139_ioport_writeb, s);
+ register_ioport_read( addr, 0x100, 1, rtl8139_ioport_readb, s);
+
+ register_ioport_write(addr, 0x100, 2, rtl8139_ioport_writew, s);
+ register_ioport_read( addr, 0x100, 2, rtl8139_ioport_readw, s);
+
+ register_ioport_write(addr, 0x100, 4, rtl8139_ioport_writel, s);
+ register_ioport_read( addr, 0x100, 4, rtl8139_ioport_readl, s);
+}
+
+static CPUReadMemoryFunc *rtl8139_mmio_read[3] = {
+ rtl8139_mmio_readb,
+ rtl8139_mmio_readw,
+ rtl8139_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *rtl8139_mmio_write[3] = {
+ rtl8139_mmio_writeb,
+ rtl8139_mmio_writew,
+ rtl8139_mmio_writel,
+};
+
+static inline int64_t rtl8139_get_next_tctr_time(RTL8139State *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(1, ticks_per_sec, PCI_FREQUENCY);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+#if RTL8139_ONBOARD_TIMER
+static void rtl8139_timer(void *opaque)
+{
+ RTL8139State *s = opaque;
+
+ int is_timeout = 0;
+
+ int64_t curr_time;
+ uint32_t curr_tick;
+
+ if (!s->clock_enabled)
+ {
+ DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n"));
+ return;
+ }
+
+ curr_time = qemu_get_clock(vm_clock);
+
+ curr_tick = muldiv64(curr_time - s->TCTR_base, PCI_FREQUENCY, ticks_per_sec);
+
+ if (s->TimerInt && curr_tick >= s->TimerInt)
+ {
+ if (s->TCTR < s->TimerInt || curr_tick < s->TCTR)
+ {
+ is_timeout = 1;
+ }
+ }
+
+ s->TCTR = curr_tick;
+
+// DEBUG_PRINT(("RTL8139: >>> timer: tick=%08u\n", s->TCTR));
+
+ if (is_timeout)
+ {
+ DEBUG_PRINT(("RTL8139: >>> timer: timeout tick=%08u\n", s->TCTR));
+ s->IntrStatus |= PCSTimeout;
+ rtl8139_update_irq(s);
+ }
+
+ qemu_mod_timer(s->timer,
+ rtl8139_get_next_tctr_time(s,curr_time));
+}
+#endif /* RTL8139_ONBOARD_TIMER */
+
+void pci_rtl8139_init(PCIBus *bus, NICInfo *nd)
+{
+ PCIRTL8139State *d;
+ RTL8139State *s;
+ uint8_t *pci_conf;
+
+ d = (PCIRTL8139State *)pci_register_device(bus,
+ "RTL8139", sizeof(PCIRTL8139State),
+ -1,
+ NULL, NULL);
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0xec; /* Realtek 8139 */
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0x39;
+ pci_conf[0x03] = 0x81;
+ pci_conf[0x04] = 0x05; /* command = I/O space, Bus Master */
+ pci_conf[0x08] = RTL8139_PCI_REVID; /* PCI revision ID; >=0x20 is for 8139C+ */
+ pci_conf[0x0a] = 0x00; /* ethernet network controller */
+ pci_conf[0x0b] = 0x02;
+ pci_conf[0x0e] = 0x00; /* header_type */
+ pci_conf[0x3d] = 1; /* interrupt pin 0 */
+ pci_conf[0x34] = 0xdc;
+
+ s = &d->rtl8139;
+
+ /* I/O handler for memory-mapped I/O */
+ s->rtl8139_mmio_io_addr =
+ cpu_register_io_memory(0, rtl8139_mmio_read, rtl8139_mmio_write, s);
+
+ pci_register_io_region(&d->dev, 0, 0x100,
+ PCI_ADDRESS_SPACE_IO, rtl8139_ioport_map);
+
+ pci_register_io_region(&d->dev, 1, 0x100,
+ PCI_ADDRESS_SPACE_MEM, rtl8139_mmio_map);
+
+ s->irq = 16; /* PCI interrupt */
+ s->pci_dev = (PCIDevice *)d;
+ memcpy(s->macaddr, nd->macaddr, 6);
+ rtl8139_reset(s);
+ s->vc = qemu_new_vlan_client(nd->vlan, rtl8139_receive,
+ rtl8139_can_receive, s);
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "rtl8139 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ s->macaddr[0],
+ s->macaddr[1],
+ s->macaddr[2],
+ s->macaddr[3],
+ s->macaddr[4],
+ s->macaddr[5]);
+
+ s->cplus_txbuffer = NULL;
+ s->cplus_txbuffer_len = 0;
+ s->cplus_txbuffer_offset = 0;
+
+ /* XXX: instance number ? */
+ register_savevm("rtl8139", 0, 2, rtl8139_save, rtl8139_load, s);
+ register_savevm("rtl8139_pci", 0, 1, generic_pci_save, generic_pci_load,
+ &d->dev);
+
+#if RTL8139_ONBOARD_TIMER
+ s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
+
+ qemu_mod_timer(s->timer,
+ rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock)));
+#endif /* RTL8139_ONBOARD_TIMER */
+}
+
diff --git a/hw/sb16.c b/hw/sb16.c
new file mode 100644
index 0000000..04325ac
--- /dev/null
+++ b/hw/sb16.c
@@ -0,0 +1,1451 @@
+/*
+ * QEMU Soundblaster 16 emulation
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
+
+#define dolog(...) AUD_log ("sb16", __VA_ARGS__)
+
+/* #define DEBUG */
+/* #define DEBUG_SB16_MOST */
+
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define IO_READ_PROTO(name) \
+ uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+ void name (void *opaque, uint32_t nport, uint32_t val)
+
+static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
+static struct {
+ int ver_lo;
+ int ver_hi;
+ int irq;
+ int dma;
+ int hdma;
+ int port;
+} conf = {5, 4, 5, 1, 5, 0x220};
+
+typedef struct SB16State {
+ QEMUSoundCard card;
+ int irq;
+ int dma;
+ int hdma;
+ int port;
+ int ver;
+
+ int in_index;
+ int out_data_len;
+ int fmt_stereo;
+ int fmt_signed;
+ int fmt_bits;
+ audfmt_e fmt;
+ int dma_auto;
+ int block_size;
+ int fifo;
+ int freq;
+ int time_const;
+ int speaker;
+ int needed_bytes;
+ int cmd;
+ int use_hdma;
+ int highspeed;
+ int can_write;
+
+ int v2x6;
+
+ uint8_t csp_param;
+ uint8_t csp_value;
+ uint8_t csp_mode;
+ uint8_t csp_regs[256];
+ uint8_t csp_index;
+ uint8_t csp_reg83[4];
+ int csp_reg83r;
+ int csp_reg83w;
+
+ uint8_t in2_data[10];
+ uint8_t out_data[50];
+ uint8_t test_reg;
+ uint8_t last_read_byte;
+ int nzero;
+
+ int left_till_irq;
+
+ int dma_running;
+ int bytes_per_second;
+ int align;
+ int audio_free;
+ SWVoiceOut *voice;
+
+ QEMUTimer *aux_ts;
+ /* mixer state */
+ int mixer_nreg;
+ uint8_t mixer_regs[256];
+} SB16State;
+
+static void SB_audio_callback (void *opaque, int free);
+
+static int magic_of_irq (int irq)
+{
+ switch (irq) {
+ case 5:
+ return 2;
+ case 7:
+ return 4;
+ case 9:
+ return 1;
+ case 10:
+ return 8;
+ default:
+ dolog ("bad irq %d\n", irq);
+ return 2;
+ }
+}
+
+static int irq_of_magic (int magic)
+{
+ switch (magic) {
+ case 1:
+ return 9;
+ case 2:
+ return 5;
+ case 4:
+ return 7;
+ case 8:
+ return 10;
+ default:
+ dolog ("bad irq magic %d\n", magic);
+ return -1;
+ }
+}
+
+#if 0
+static void log_dsp (SB16State *dsp)
+{
+ ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
+ dsp->fmt_stereo ? "Stereo" : "Mono",
+ dsp->fmt_signed ? "Signed" : "Unsigned",
+ dsp->fmt_bits,
+ dsp->dma_auto ? "Auto" : "Single",
+ dsp->block_size,
+ dsp->freq,
+ dsp->time_const,
+ dsp->speaker);
+}
+#endif
+
+static void speaker (SB16State *s, int on)
+{
+ s->speaker = on;
+ /* AUD_enable (s->voice, on); */
+}
+
+static void control (SB16State *s, int hold)
+{
+ int dma = s->use_hdma ? s->hdma : s->dma;
+ s->dma_running = hold;
+
+ ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
+
+ if (hold) {
+ DMA_hold_DREQ (dma);
+ AUD_set_active_out (s->voice, 1);
+ }
+ else {
+ DMA_release_DREQ (dma);
+ AUD_set_active_out (s->voice, 0);
+ }
+}
+
+static void aux_timer (void *opaque)
+{
+ SB16State *s = opaque;
+ s->can_write = 1;
+ pic_set_irq (s->irq, 1);
+}
+
+#define DMA8_AUTO 1
+#define DMA8_HIGH 2
+
+static void continue_dma8 (SB16State *s)
+{
+ if (s->freq > 0) {
+ audsettings_t as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+}
+
+static void dma_cmd8 (SB16State *s, int mask, int dma_len)
+{
+ s->fmt = AUD_FMT_U8;
+ s->use_hdma = 0;
+ s->fmt_bits = 8;
+ s->fmt_signed = 0;
+ s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
+ if (-1 == s->time_const) {
+ if (s->freq <= 0)
+ s->freq = 11025;
+ }
+ else {
+ int tmp = (256 - s->time_const);
+ s->freq = (1000000 + (tmp / 2)) / tmp;
+ }
+
+ if (dma_len != -1) {
+ s->block_size = dma_len << s->fmt_stereo;
+ }
+ else {
+ /* This is apparently the only way to make both Act1/PL
+ and SecondReality/FC work
+
+ Act1 sets block size via command 0x48 and it's an odd number
+ SR does the same with even number
+ Both use stereo, and Creatives own documentation states that
+ 0x48 sets block size in bytes less one.. go figure */
+ s->block_size &= ~s->fmt_stereo;
+ }
+
+ s->freq >>= s->fmt_stereo;
+ s->left_till_irq = s->block_size;
+ s->bytes_per_second = (s->freq << s->fmt_stereo);
+ /* s->highspeed = (mask & DMA8_HIGH) != 0; */
+ s->dma_auto = (mask & DMA8_AUTO) != 0;
+ s->align = (1 << s->fmt_stereo) - 1;
+
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
+
+ ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+ "dma %d, auto %d, fifo %d, high %d\n",
+ s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+ s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+ continue_dma8 (s);
+ speaker (s, 1);
+}
+
+static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
+{
+ s->use_hdma = cmd < 0xc0;
+ s->fifo = (cmd >> 1) & 1;
+ s->dma_auto = (cmd >> 2) & 1;
+ s->fmt_signed = (d0 >> 4) & 1;
+ s->fmt_stereo = (d0 >> 5) & 1;
+
+ switch (cmd >> 4) {
+ case 11:
+ s->fmt_bits = 16;
+ break;
+
+ case 12:
+ s->fmt_bits = 8;
+ break;
+ }
+
+ if (-1 != s->time_const) {
+#if 1
+ int tmp = 256 - s->time_const;
+ s->freq = (1000000 + (tmp / 2)) / tmp;
+#else
+ /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
+ s->freq = 1000000 / ((255 - s->time_const));
+#endif
+ s->time_const = -1;
+ }
+
+ s->block_size = dma_len + 1;
+ s->block_size <<= (s->fmt_bits == 16);
+ if (!s->dma_auto) {
+ /* It is clear that for DOOM and auto-init this value
+ shouldn't take stereo into account, while Miles Sound Systems
+ setsound.exe with single transfer mode wouldn't work without it
+ wonders of SB16 yet again */
+ s->block_size <<= s->fmt_stereo;
+ }
+
+ ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+ "dma %d, auto %d, fifo %d, high %d\n",
+ s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+ s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+ if (16 == s->fmt_bits) {
+ if (s->fmt_signed) {
+ s->fmt = AUD_FMT_S16;
+ }
+ else {
+ s->fmt = AUD_FMT_U16;
+ }
+ }
+ else {
+ if (s->fmt_signed) {
+ s->fmt = AUD_FMT_S8;
+ }
+ else {
+ s->fmt = AUD_FMT_U8;
+ }
+ }
+
+ s->left_till_irq = s->block_size;
+
+ s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
+ s->highspeed = 0;
+ s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
+
+ if (s->freq) {
+ audsettings_t as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+ speaker (s, 1);
+}
+
+static inline void dsp_out_data (SB16State *s, uint8_t val)
+{
+ ldebug ("outdata %#x\n", val);
+ if ((size_t) s->out_data_len < sizeof (s->out_data)) {
+ s->out_data[s->out_data_len++] = val;
+ }
+}
+
+static inline uint8_t dsp_get_data (SB16State *s)
+{
+ if (s->in_index) {
+ return s->in2_data[--s->in_index];
+ }
+ else {
+ dolog ("buffer underflow\n");
+ return 0;
+ }
+}
+
+static void command (SB16State *s, uint8_t cmd)
+{
+ ldebug ("command %#x\n", cmd);
+
+ if (cmd > 0xaf && cmd < 0xd0) {
+ if (cmd & 8) {
+ dolog ("ADC not yet supported (command %#x)\n", cmd);
+ }
+
+ switch (cmd >> 4) {
+ case 11:
+ case 12:
+ break;
+ default:
+ dolog ("%#x wrong bits\n", cmd);
+ }
+ s->needed_bytes = 3;
+ }
+ else {
+ s->needed_bytes = 0;
+
+ switch (cmd) {
+ case 0x03:
+ dsp_out_data (s, 0x10); /* s->csp_param); */
+ goto warn;
+
+ case 0x04:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x05:
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x08:
+ /* __asm__ ("int3"); */
+ goto warn;
+
+ case 0x0e:
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x09:
+ dsp_out_data (s, 0xf8);
+ goto warn;
+
+ case 0x0f:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x10:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x14:
+ s->needed_bytes = 2;
+ s->block_size = 0;
+ break;
+
+ case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
+ dma_cmd8 (s, DMA8_AUTO, -1);
+ break;
+
+ case 0x20: /* Direct ADC, Juice/PL */
+ dsp_out_data (s, 0xff);
+ goto warn;
+
+ case 0x35:
+ dolog ("0x35 - MIDI command not implemented\n");
+ break;
+
+ case 0x40:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 1;
+ break;
+
+ case 0x41:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 2;
+ break;
+
+ case 0x42:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x45:
+ dsp_out_data (s, 0xaa);
+ goto warn;
+
+ case 0x47: /* Continue Auto-Initialize DMA 16bit */
+ break;
+
+ case 0x48:
+ s->needed_bytes = 2;
+ break;
+
+ case 0x74:
+ s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
+ dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
+ break;
+
+ case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x76: /* DMA DAC, 2.6-bit ADPCM */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
+ break;
+
+ case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x7d:
+ dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
+ dolog ("not implemented\n");
+ break;
+
+ case 0x7f:
+ dolog (
+ "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
+ );
+ dolog ("not implemented\n");
+ break;
+
+ case 0x80:
+ s->needed_bytes = 2;
+ break;
+
+ case 0x90:
+ case 0x91:
+ dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
+ break;
+
+ case 0xd0: /* halt DMA operation. 8bit */
+ control (s, 0);
+ break;
+
+ case 0xd1: /* speaker on */
+ speaker (s, 1);
+ break;
+
+ case 0xd3: /* speaker off */
+ speaker (s, 0);
+ break;
+
+ case 0xd4: /* continue DMA operation. 8bit */
+ /* KQ6 (or maybe Sierras audblst.drv in general) resets
+ the frequency between halt/continue */
+ continue_dma8 (s);
+ break;
+
+ case 0xd5: /* halt DMA operation. 16bit */
+ control (s, 0);
+ break;
+
+ case 0xd6: /* continue DMA operation. 16bit */
+ control (s, 1);
+ break;
+
+ case 0xd9: /* exit auto-init DMA after this block. 16bit */
+ s->dma_auto = 0;
+ break;
+
+ case 0xda: /* exit auto-init DMA after this block. 8bit */
+ s->dma_auto = 0;
+ break;
+
+ case 0xe0: /* DSP identification */
+ s->needed_bytes = 1;
+ break;
+
+ case 0xe1:
+ dsp_out_data (s, s->ver & 0xff);
+ dsp_out_data (s, s->ver >> 8);
+ break;
+
+ case 0xe2:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0xe3:
+ {
+ int i;
+ for (i = sizeof (e3) - 1; i >= 0; --i)
+ dsp_out_data (s, e3[i]);
+ }
+ break;
+
+ case 0xe4: /* write test reg */
+ s->needed_bytes = 1;
+ break;
+
+ case 0xe7:
+ dolog ("Attempt to probe for ESS (0xe7)?\n");
+ break;
+
+ case 0xe8: /* read test reg */
+ dsp_out_data (s, s->test_reg);
+ break;
+
+ case 0xf2:
+ case 0xf3:
+ dsp_out_data (s, 0xaa);
+ s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
+ pic_set_irq (s->irq, 1);
+ break;
+
+ case 0xf9:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0xfa:
+ dsp_out_data (s, 0);
+ goto warn;
+
+ case 0xfc: /* FIXME */
+ dsp_out_data (s, 0);
+ goto warn;
+
+ default:
+ dolog ("Unrecognized command %#x\n", cmd);
+ break;
+ }
+ }
+
+ if (!s->needed_bytes) {
+ ldebug ("\n");
+ }
+
+ exit:
+ if (!s->needed_bytes) {
+ s->cmd = -1;
+ }
+ else {
+ s->cmd = cmd;
+ }
+ return;
+
+ warn:
+ dolog ("warning: command %#x,%d is not truly understood yet\n",
+ cmd, s->needed_bytes);
+ goto exit;
+
+}
+
+static uint16_t dsp_get_lohi (SB16State *s)
+{
+ uint8_t hi = dsp_get_data (s);
+ uint8_t lo = dsp_get_data (s);
+ return (hi << 8) | lo;
+}
+
+static uint16_t dsp_get_hilo (SB16State *s)
+{
+ uint8_t lo = dsp_get_data (s);
+ uint8_t hi = dsp_get_data (s);
+ return (hi << 8) | lo;
+}
+
+static void complete (SB16State *s)
+{
+ int d0, d1, d2;
+ ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
+ s->cmd, s->in_index, s->needed_bytes);
+
+ if (s->cmd > 0xaf && s->cmd < 0xd0) {
+ d2 = dsp_get_data (s);
+ d1 = dsp_get_data (s);
+ d0 = dsp_get_data (s);
+
+ if (s->cmd & 8) {
+ dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+ s->cmd, d0, d1, d2);
+ }
+ else {
+ ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+ s->cmd, d0, d1, d2);
+ dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
+ }
+ }
+ else {
+ switch (s->cmd) {
+ case 0x04:
+ s->csp_mode = dsp_get_data (s);
+ s->csp_reg83r = 0;
+ s->csp_reg83w = 0;
+ ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
+ break;
+
+ case 0x05:
+ s->csp_param = dsp_get_data (s);
+ s->csp_value = dsp_get_data (s);
+ ldebug ("CSP command 0x05: param=%#x value=%#x\n",
+ s->csp_param,
+ s->csp_value);
+ break;
+
+ case 0x0e:
+ d0 = dsp_get_data (s);
+ d1 = dsp_get_data (s);
+ ldebug ("write CSP register %d <- %#x\n", d1, d0);
+ if (d1 == 0x83) {
+ ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
+ s->csp_reg83[s->csp_reg83r % 4] = d0;
+ s->csp_reg83r += 1;
+ }
+ else {
+ s->csp_regs[d1] = d0;
+ }
+ break;
+
+ case 0x0f:
+ d0 = dsp_get_data (s);
+ ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
+ d0, s->csp_regs[d0], s->csp_mode);
+ if (d0 == 0x83) {
+ ldebug ("0x83[%d] -> %#x\n",
+ s->csp_reg83w,
+ s->csp_reg83[s->csp_reg83w % 4]);
+ dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
+ s->csp_reg83w += 1;
+ }
+ else {
+ dsp_out_data (s, s->csp_regs[d0]);
+ }
+ break;
+
+ case 0x10:
+ d0 = dsp_get_data (s);
+ dolog ("cmd 0x10 d0=%#x\n", d0);
+ break;
+
+ case 0x14:
+ dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
+ break;
+
+ case 0x40:
+ s->time_const = dsp_get_data (s);
+ ldebug ("set time const %d\n", s->time_const);
+ break;
+
+ case 0x42: /* FT2 sets output freq with this, go figure */
+#if 0
+ dolog ("cmd 0x42 might not do what it think it should\n");
+#endif
+ case 0x41:
+ s->freq = dsp_get_hilo (s);
+ ldebug ("set freq %d\n", s->freq);
+ break;
+
+ case 0x48:
+ s->block_size = dsp_get_lohi (s) + 1;
+ ldebug ("set dma block len %d\n", s->block_size);
+ break;
+
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ /* ADPCM stuff, ignore */
+ break;
+
+ case 0x80:
+ {
+ int freq, samples, bytes;
+ int64_t ticks;
+
+ freq = s->freq > 0 ? s->freq : 11025;
+ samples = dsp_get_lohi (s) + 1;
+ bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
+ ticks = (bytes * ticks_per_sec) / freq;
+ if (ticks < ticks_per_sec / 1024) {
+ pic_set_irq (s->irq, 1);
+ }
+ else {
+ if (s->aux_ts) {
+ qemu_mod_timer (
+ s->aux_ts,
+ qemu_get_clock (vm_clock) + ticks
+ );
+ }
+ }
+ ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
+ }
+ break;
+
+ case 0xe0:
+ d0 = dsp_get_data (s);
+ s->out_data_len = 0;
+ ldebug ("E0 data = %#x\n", d0);
+ dsp_out_data (s, ~d0);
+ break;
+
+ case 0xe2:
+ d0 = dsp_get_data (s);
+ ldebug ("E2 = %#x\n", d0);
+ break;
+
+ case 0xe4:
+ s->test_reg = dsp_get_data (s);
+ break;
+
+ case 0xf9:
+ d0 = dsp_get_data (s);
+ ldebug ("command 0xf9 with %#x\n", d0);
+ switch (d0) {
+ case 0x0e:
+ dsp_out_data (s, 0xff);
+ break;
+
+ case 0x0f:
+ dsp_out_data (s, 0x07);
+ break;
+
+ case 0x37:
+ dsp_out_data (s, 0x38);
+ break;
+
+ default:
+ dsp_out_data (s, 0x00);
+ break;
+ }
+ break;
+
+ default:
+ dolog ("complete: unrecognized command %#x\n", s->cmd);
+ return;
+ }
+ }
+
+ ldebug ("\n");
+ s->cmd = -1;
+ return;
+}
+
+static void legacy_reset (SB16State *s)
+{
+ audsettings_t as;
+
+ s->freq = 11025;
+ s->fmt_signed = 0;
+ s->fmt_bits = 8;
+ s->fmt_stereo = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1;
+ as.fmt = AUD_FMT_U8;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+
+ /* Not sure about that... */
+ /* AUD_set_active_out (s->voice, 1); */
+}
+
+static void reset (SB16State *s)
+{
+ pic_set_irq (s->irq, 0);
+ if (s->dma_auto) {
+ pic_set_irq (s->irq, 1);
+ pic_set_irq (s->irq, 0);
+ }
+
+ s->mixer_regs[0x82] = 0;
+ s->dma_auto = 0;
+ s->in_index = 0;
+ s->out_data_len = 0;
+ s->left_till_irq = 0;
+ s->needed_bytes = 0;
+ s->block_size = -1;
+ s->nzero = 0;
+ s->highspeed = 0;
+ s->v2x6 = 0;
+ s->cmd = -1;
+
+ dsp_out_data(s, 0xaa);
+ speaker (s, 0);
+ control (s, 0);
+ legacy_reset (s);
+}
+
+static IO_WRITE_PROTO (dsp_write)
+{
+ SB16State *s = opaque;
+ int iport;
+
+ iport = nport - s->port;
+
+ ldebug ("write %#x <- %#x\n", nport, val);
+ switch (iport) {
+ case 0x06:
+ switch (val) {
+ case 0x00:
+ if (s->v2x6 == 1) {
+ if (0 && s->highspeed) {
+ s->highspeed = 0;
+ pic_set_irq (s->irq, 0);
+ control (s, 0);
+ }
+ else {
+ reset (s);
+ }
+ }
+ s->v2x6 = 0;
+ break;
+
+ case 0x01:
+ case 0x03: /* FreeBSD kludge */
+ s->v2x6 = 1;
+ break;
+
+ case 0xc6:
+ s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
+ break;
+
+ case 0xb8: /* Panic */
+ reset (s);
+ break;
+
+ case 0x39:
+ dsp_out_data (s, 0x38);
+ reset (s);
+ s->v2x6 = 0x39;
+ break;
+
+ default:
+ s->v2x6 = val;
+ break;
+ }
+ break;
+
+ case 0x0c: /* write data or command | write status */
+/* if (s->highspeed) */
+/* break; */
+
+ if (0 == s->needed_bytes) {
+ command (s, val);
+#if 0
+ if (0 == s->needed_bytes) {
+ log_dsp (s);
+ }
+#endif
+ }
+ else {
+ if (s->in_index == sizeof (s->in2_data)) {
+ dolog ("in data overrun\n");
+ }
+ else {
+ s->in2_data[s->in_index++] = val;
+ if (s->in_index == s->needed_bytes) {
+ s->needed_bytes = 0;
+ complete (s);
+#if 0
+ log_dsp (s);
+#endif
+ }
+ }
+ }
+ break;
+
+ default:
+ ldebug ("(nport=%#x, val=%#x)\n", nport, val);
+ break;
+ }
+}
+
+static IO_READ_PROTO (dsp_read)
+{
+ SB16State *s = opaque;
+ int iport, retval, ack = 0;
+
+ iport = nport - s->port;
+
+ switch (iport) {
+ case 0x06: /* reset */
+ retval = 0xff;
+ break;
+
+ case 0x0a: /* read data */
+ if (s->out_data_len) {
+ retval = s->out_data[--s->out_data_len];
+ s->last_read_byte = retval;
+ }
+ else {
+ if (s->cmd != -1) {
+ dolog ("empty output buffer for command %#x\n",
+ s->cmd);
+ }
+ retval = s->last_read_byte;
+ /* goto error; */
+ }
+ break;
+
+ case 0x0c: /* 0 can write */
+ retval = s->can_write ? 0 : 0x80;
+ break;
+
+ case 0x0d: /* timer interrupt clear */
+ /* dolog ("timer interrupt clear\n"); */
+ retval = 0;
+ break;
+
+ case 0x0e: /* data available status | irq 8 ack */
+ retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
+ if (s->mixer_regs[0x82] & 1) {
+ ack = 1;
+ s->mixer_regs[0x82] &= 1;
+ pic_set_irq (s->irq, 0);
+ }
+ break;
+
+ case 0x0f: /* irq 16 ack */
+ retval = 0xff;
+ if (s->mixer_regs[0x82] & 2) {
+ ack = 1;
+ s->mixer_regs[0x82] &= 2;
+ pic_set_irq (s->irq, 0);
+ }
+ break;
+
+ default:
+ goto error;
+ }
+
+ if (!ack) {
+ ldebug ("read %#x -> %#x\n", nport, retval);
+ }
+
+ return retval;
+
+ error:
+ dolog ("warning: dsp_read %#x error\n", nport);
+ return 0xff;
+}
+
+static void reset_mixer (SB16State *s)
+{
+ int i;
+
+ memset (s->mixer_regs, 0xff, 0x7f);
+ memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
+
+ s->mixer_regs[0x02] = 4; /* master volume 3bits */
+ s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
+ s->mixer_regs[0x08] = 0; /* CD volume 3bits */
+ s->mixer_regs[0x0a] = 0; /* voice volume 2bits */
+
+ /* d5=input filt, d3=lowpass filt, d1,d2=input source */
+ s->mixer_regs[0x0c] = 0;
+
+ /* d5=output filt, d1=stereo switch */
+ s->mixer_regs[0x0e] = 0;
+
+ /* voice volume L d5,d7, R d1,d3 */
+ s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
+ /* master ... */
+ s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
+ /* MIDI ... */
+ s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
+
+ for (i = 0x30; i < 0x48; i++) {
+ s->mixer_regs[i] = 0x20;
+ }
+}
+
+static IO_WRITE_PROTO(mixer_write_indexb)
+{
+ SB16State *s = opaque;
+ (void) nport;
+ s->mixer_nreg = val;
+}
+
+static IO_WRITE_PROTO(mixer_write_datab)
+{
+ SB16State *s = opaque;
+
+ (void) nport;
+ ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
+
+ switch (s->mixer_nreg) {
+ case 0x00:
+ reset_mixer (s);
+ break;
+
+ case 0x80:
+ {
+ int irq = irq_of_magic (val);
+ ldebug ("setting irq to %d (val=%#x)\n", irq, val);
+ if (irq > 0) {
+ s->irq = irq;
+ }
+ }
+ break;
+
+ case 0x81:
+ {
+ int dma, hdma;
+
+ dma = lsbindex (val & 0xf);
+ hdma = lsbindex (val & 0xf0);
+ if (dma != s->dma || hdma != s->hdma) {
+ dolog (
+ "attempt to change DMA "
+ "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
+ dma, s->dma, hdma, s->hdma, val);
+ }
+#if 0
+ s->dma = dma;
+ s->hdma = hdma;
+#endif
+ }
+ break;
+
+ case 0x82:
+ dolog ("attempt to write into IRQ status register (val=%#x)\n",
+ val);
+ return;
+
+ default:
+ if (s->mixer_nreg >= 0x80) {
+ ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
+ }
+ break;
+ }
+
+ s->mixer_regs[s->mixer_nreg] = val;
+}
+
+static IO_WRITE_PROTO(mixer_write_indexw)
+{
+ mixer_write_indexb (opaque, nport, val & 0xff);
+ mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
+}
+
+static IO_READ_PROTO(mixer_read)
+{
+ SB16State *s = opaque;
+
+ (void) nport;
+#ifndef DEBUG_SB16_MOST
+ if (s->mixer_nreg != 0x82) {
+ ldebug ("mixer_read[%#x] -> %#x\n",
+ s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+ }
+#else
+ ldebug ("mixer_read[%#x] -> %#x\n",
+ s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+#endif
+ return s->mixer_regs[s->mixer_nreg];
+}
+
+static int write_audio (SB16State *s, int nchan, int dma_pos,
+ int dma_len, int len)
+{
+ int temp, net;
+ uint8_t tmpbuf[4096];
+
+ temp = len;
+ net = 0;
+
+ while (temp) {
+ int left = dma_len - dma_pos;
+ int copied;
+ size_t to_copy;
+
+ to_copy = audio_MIN (temp, left);
+ if (to_copy > sizeof (tmpbuf)) {
+ to_copy = sizeof (tmpbuf);
+ }
+
+ copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+ copied = AUD_write (s->voice, tmpbuf, copied);
+
+ temp -= copied;
+ dma_pos = (dma_pos + copied) % dma_len;
+ net += copied;
+
+ if (!copied) {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ SB16State *s = opaque;
+ int till, copy, written, free;
+
+ if (s->left_till_irq < 0) {
+ s->left_till_irq = s->block_size;
+ }
+
+ if (s->voice) {
+ free = s->audio_free & ~s->align;
+ if ((free <= 0) || !dma_len) {
+ return dma_pos;
+ }
+ }
+ else {
+ free = dma_len;
+ }
+
+ copy = free;
+ till = s->left_till_irq;
+
+#ifdef DEBUG_SB16_MOST
+ dolog ("pos:%06d %d till:%d len:%d\n",
+ dma_pos, free, till, dma_len);
+#endif
+
+ if (till <= copy) {
+ if (0 == s->dma_auto) {
+ copy = till;
+ }
+ }
+
+ written = write_audio (s, nchan, dma_pos, dma_len, copy);
+ dma_pos = (dma_pos + written) % dma_len;
+ s->left_till_irq -= written;
+
+ if (s->left_till_irq <= 0) {
+ s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
+ pic_set_irq (s->irq, 1);
+ if (0 == s->dma_auto) {
+ control (s, 0);
+ speaker (s, 0);
+ }
+ }
+
+#ifdef DEBUG_SB16_MOST
+ ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
+ dma_pos, free, dma_len, s->left_till_irq, copy, written,
+ s->block_size);
+#endif
+
+ while (s->left_till_irq <= 0) {
+ s->left_till_irq = s->block_size + s->left_till_irq;
+ }
+
+ return dma_pos;
+}
+
+static void SB_audio_callback (void *opaque, int free)
+{
+ SB16State *s = opaque;
+ s->audio_free = free;
+}
+
+static void SB_save (QEMUFile *f, void *opaque)
+{
+ SB16State *s = opaque;
+
+ qemu_put_be32s (f, &s->irq);
+ qemu_put_be32s (f, &s->dma);
+ qemu_put_be32s (f, &s->hdma);
+ qemu_put_be32s (f, &s->port);
+ qemu_put_be32s (f, &s->ver);
+ qemu_put_be32s (f, &s->in_index);
+ qemu_put_be32s (f, &s->out_data_len);
+ qemu_put_be32s (f, &s->fmt_stereo);
+ qemu_put_be32s (f, &s->fmt_signed);
+ qemu_put_be32s (f, &s->fmt_bits);
+ qemu_put_be32s (f, &s->fmt);
+ qemu_put_be32s (f, &s->dma_auto);
+ qemu_put_be32s (f, &s->block_size);
+ qemu_put_be32s (f, &s->fifo);
+ qemu_put_be32s (f, &s->freq);
+ qemu_put_be32s (f, &s->time_const);
+ qemu_put_be32s (f, &s->speaker);
+ qemu_put_be32s (f, &s->needed_bytes);
+ qemu_put_be32s (f, &s->cmd);
+ qemu_put_be32s (f, &s->use_hdma);
+ qemu_put_be32s (f, &s->highspeed);
+ qemu_put_be32s (f, &s->can_write);
+ qemu_put_be32s (f, &s->v2x6);
+
+ qemu_put_8s (f, &s->csp_param);
+ qemu_put_8s (f, &s->csp_value);
+ qemu_put_8s (f, &s->csp_mode);
+ qemu_put_8s (f, &s->csp_param);
+ qemu_put_buffer (f, s->csp_regs, 256);
+ qemu_put_8s (f, &s->csp_index);
+ qemu_put_buffer (f, s->csp_reg83, 4);
+ qemu_put_be32s (f, &s->csp_reg83r);
+ qemu_put_be32s (f, &s->csp_reg83w);
+
+ qemu_put_buffer (f, s->in2_data, sizeof (s->in2_data));
+ qemu_put_buffer (f, s->out_data, sizeof (s->out_data));
+ qemu_put_8s (f, &s->test_reg);
+ qemu_put_8s (f, &s->last_read_byte);
+
+ qemu_put_be32s (f, &s->nzero);
+ qemu_put_be32s (f, &s->left_till_irq);
+ qemu_put_be32s (f, &s->dma_running);
+ qemu_put_be32s (f, &s->bytes_per_second);
+ qemu_put_be32s (f, &s->align);
+
+ qemu_put_be32s (f, &s->mixer_nreg);
+ qemu_put_buffer (f, s->mixer_regs, 256);
+}
+
+static int SB_load (QEMUFile *f, void *opaque, int version_id)
+{
+ SB16State *s = opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ qemu_get_be32s (f, &s->irq);
+ qemu_get_be32s (f, &s->dma);
+ qemu_get_be32s (f, &s->hdma);
+ qemu_get_be32s (f, &s->port);
+ qemu_get_be32s (f, &s->ver);
+ qemu_get_be32s (f, &s->in_index);
+ qemu_get_be32s (f, &s->out_data_len);
+ qemu_get_be32s (f, &s->fmt_stereo);
+ qemu_get_be32s (f, &s->fmt_signed);
+ qemu_get_be32s (f, &s->fmt_bits);
+ qemu_get_be32s (f, &s->fmt);
+ qemu_get_be32s (f, &s->dma_auto);
+ qemu_get_be32s (f, &s->block_size);
+ qemu_get_be32s (f, &s->fifo);
+ qemu_get_be32s (f, &s->freq);
+ qemu_get_be32s (f, &s->time_const);
+ qemu_get_be32s (f, &s->speaker);
+ qemu_get_be32s (f, &s->needed_bytes);
+ qemu_get_be32s (f, &s->cmd);
+ qemu_get_be32s (f, &s->use_hdma);
+ qemu_get_be32s (f, &s->highspeed);
+ qemu_get_be32s (f, &s->can_write);
+ qemu_get_be32s (f, &s->v2x6);
+
+ qemu_get_8s (f, &s->csp_param);
+ qemu_get_8s (f, &s->csp_value);
+ qemu_get_8s (f, &s->csp_mode);
+ qemu_get_8s (f, &s->csp_param);
+ qemu_get_buffer (f, s->csp_regs, 256);
+ qemu_get_8s (f, &s->csp_index);
+ qemu_get_buffer (f, s->csp_reg83, 4);
+ qemu_get_be32s (f, &s->csp_reg83r);
+ qemu_get_be32s (f, &s->csp_reg83w);
+
+ qemu_get_buffer (f, s->in2_data, sizeof (s->in2_data));
+ qemu_get_buffer (f, s->out_data, sizeof (s->out_data));
+ qemu_get_8s (f, &s->test_reg);
+ qemu_get_8s (f, &s->last_read_byte);
+
+ qemu_get_be32s (f, &s->nzero);
+ qemu_get_be32s (f, &s->left_till_irq);
+ qemu_get_be32s (f, &s->dma_running);
+ qemu_get_be32s (f, &s->bytes_per_second);
+ qemu_get_be32s (f, &s->align);
+
+ qemu_get_be32s (f, &s->mixer_nreg);
+ qemu_get_buffer (f, s->mixer_regs, 256);
+
+ if (s->voice) {
+ AUD_close_out (&s->card, s->voice);
+ s->voice = NULL;
+ }
+
+ if (s->dma_running) {
+ if (s->freq) {
+ audsettings_t as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+ speaker (s, s->speaker);
+ }
+ return 0;
+}
+
+int SB16_init (AudioState *audio)
+{
+ SB16State *s;
+ int i;
+ static const uint8_t dsp_write_ports[] = {0x6, 0xc};
+ static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
+
+ if (!audio) {
+ dolog ("No audio state\n");
+ return -1;
+ }
+
+ s = qemu_mallocz (sizeof (*s));
+ if (!s) {
+ dolog ("Could not allocate memory for SB16 (%zu bytes)\n",
+ sizeof (*s));
+ return -1;
+ }
+
+ s->cmd = -1;
+ s->irq = conf.irq;
+ s->dma = conf.dma;
+ s->hdma = conf.hdma;
+ s->port = conf.port;
+ s->ver = conf.ver_lo | (conf.ver_hi << 8);
+
+ s->mixer_regs[0x80] = magic_of_irq (s->irq);
+ s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
+ s->mixer_regs[0x82] = 2 << 5;
+
+ s->csp_regs[5] = 1;
+ s->csp_regs[9] = 0xf8;
+
+ reset_mixer (s);
+ s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
+ if (!s->aux_ts) {
+ dolog ("warning: Could not create auxiliary timer\n");
+ }
+
+ for (i = 0; i < LENOFA (dsp_write_ports); i++) {
+ register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s);
+ }
+
+ for (i = 0; i < LENOFA (dsp_read_ports); i++) {
+ register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s);
+ }
+
+ register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s);
+ register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s);
+ register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s);
+ register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s);
+
+ DMA_register_channel (s->hdma, SB_read_DMA, s);
+ DMA_register_channel (s->dma, SB_read_DMA, s);
+ s->can_write = 1;
+
+ register_savevm ("sb16", 0, 1, SB_save, SB_load, s);
+ AUD_register_card (audio, "sb16", &s->card);
+ return 0;
+}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
new file mode 100644
index 0000000..decab1f
--- /dev/null
+++ b/hw/scsi-disk.c
@@ -0,0 +1,478 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, args...) \
+do { printf("scsi-disk: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
+
+#include "vl.h"
+
+#define SENSE_NO_SENSE 0
+#define SENSE_ILLEGAL_REQUEST 5
+
+struct SCSIDevice
+{
+ int command;
+ uint32_t tag;
+ BlockDriverState *bdrv;
+ /* The qemu block layer uses a fixed 512 byte sector size.
+ This is the number of 512 byte blocks in a single scsi sector. */
+ int cluster_size;
+ /* When transfering data buf_pos and buf_len contain a partially
+ transferred block of data (or response to a command), and
+ sector/sector_count identify any remaining sectors.
+ Both sector and sector_count are in terms of qemu 512 byte blocks. */
+ /* ??? We should probably keep track of whether the data trasfer is
+ a read or a write. Currently we rely on the host getting it right. */
+ int sector;
+ int sector_count;
+ int buf_pos;
+ int buf_len;
+ int sense;
+ char buf[512];
+ scsi_completionfn completion;
+ void *opaque;
+};
+
+static void scsi_command_complete(SCSIDevice *s, int sense)
+{
+ s->sense = sense;
+ s->completion(s->opaque, s->tag, sense);
+}
+
+/* Read data from a scsi device. Returns nonzero on failure. */
+int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
+{
+ uint32_t n;
+
+ DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
+ if (s->buf_len == 0 && s->sector_count == 0)
+ return 1;
+
+ if (s->buf_len) {
+ n = s->buf_len;
+ if (n > len)
+ n = len;
+ memcpy(data, s->buf + s->buf_pos, n);
+ s->buf_pos += n;
+ s->buf_len -= n;
+ data += n;
+ len -= n;
+ if (s->buf_len == 0)
+ s->buf_pos = 0;
+ }
+
+ n = len / 512;
+ if (n > s->sector_count)
+ n = s->sector_count;
+
+ if (n != 0) {
+ bdrv_read(s->bdrv, s->sector, data, n);
+ data += n * 512;
+ len -= n * 512;
+ s->sector += n;
+ s->sector_count -= n;
+ }
+
+ if (len && s->sector_count) {
+ bdrv_read(s->bdrv, s->sector, s->buf, 1);
+ s->sector++;
+ s->sector_count--;
+ s->buf_pos = 0;
+ s->buf_len = 512;
+ /* Recurse to complete the partial read. */
+ return scsi_read_data(s, data, len);
+ }
+
+ if (len != 0)
+ return 1;
+
+ if (s->buf_len == 0 && s->sector_count == 0)
+ scsi_command_complete(s, SENSE_NO_SENSE);
+
+ return 0;
+}
+
+/* Read data to a scsi device. Returns nonzero on failure. */
+int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
+{
+ uint32_t n;
+
+ DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
+ if (s->buf_pos != 0) {
+ BADF("Bad state on write\n");
+ return 1;
+ }
+
+ if (s->sector_count == 0)
+ return 1;
+
+ if (s->buf_len != 0 || len < 512) {
+ n = 512 - s->buf_len;
+ if (n > len)
+ n = len;
+
+ memcpy(s->buf + s->buf_len, data, n);
+ data += n;
+ s->buf_len += n;
+ len -= n;
+ if (s->buf_len == 512) {
+ /* A full sector has been accumulated. Write it to disk. */
+ bdrv_write(s->bdrv, s->sector, s->buf, 1);
+ s->buf_len = 0;
+ s->sector++;
+ s->sector_count--;
+ }
+ }
+
+ n = len / 512;
+ if (n > s->sector_count)
+ n = s->sector_count;
+
+ if (n != 0) {
+ bdrv_write(s->bdrv, s->sector, data, n);
+ data += n * 512;
+ len -= n * 512;
+ s->sector += n;
+ s->sector_count -= n;
+ }
+
+ if (len >= 512)
+ return 1;
+
+ if (len && s->sector_count) {
+ /* Recurse to complete the partial write. */
+ return scsi_write_data(s, data, len);
+ }
+
+ if (len != 0)
+ return 1;
+
+ if (s->sector_count == 0)
+ scsi_command_complete(s, SENSE_NO_SENSE);
+
+ return 0;
+}
+
+/* Execute a scsi command. Returns the length of the data expected by the
+ command. This will be Positive for data transfers from the device
+ (eg. disk reads), negative for transfers to the device (eg. disk writes),
+ and zero if the command does not transfer any data. */
+
+int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
+{
+ int64_t nb_sectors;
+ uint32_t lba;
+ uint32_t len;
+ int cmdlen;
+ int is_write;
+
+ s->command = buf[0];
+ s->tag = tag;
+ s->sector_count = 0;
+ s->buf_pos = 0;
+ s->buf_len = 0;
+ is_write = 0;
+ DPRINTF("Command: 0x%02x", buf[0]);
+ switch (s->command >> 5) {
+ case 0:
+ lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
+ len = buf[4];
+ cmdlen = 6;
+ break;
+ case 1:
+ case 2:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[8] | (buf[7] << 8);
+ cmdlen = 10;
+ break;
+ case 4:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
+ cmdlen = 16;
+ break;
+ case 5:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
+ cmdlen = 12;
+ break;
+ default:
+ BADF("Unsupported command length, command %x\n", s->command);
+ goto fail;
+ }
+#ifdef DEBUG_SCSI
+ {
+ int i;
+ for (i = 1; i < cmdlen; i++) {
+ printf(" 0x%02x", buf[i]);
+ }
+ printf("\n");
+ }
+#endif
+ if (lun || buf[1] >> 5) {
+ /* Only LUN 0 supported. */
+ DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+ goto fail;
+ }
+ switch (s->command) {
+ case 0x0:
+ DPRINTF("Test Unit Ready\n");
+ break;
+ case 0x03:
+ DPRINTF("Request Sense (len %d)\n", len);
+ if (len < 4)
+ goto fail;
+ memset(buf, 0, 4);
+ s->buf[0] = 0xf0;
+ s->buf[1] = 0;
+ s->buf[2] = s->sense;
+ s->buf_len = 4;
+ break;
+ case 0x12:
+ DPRINTF("Inquiry (len %d)\n", len);
+ if (len < 36) {
+ BADF("Inquiry buffer too small (%d)\n", len);
+ }
+ memset(s->buf, 0, 36);
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ s->buf[0] = 5;
+ s->buf[1] = 0x80;
+ memcpy(&s->buf[16], "QEMU CD-ROM ", 16);
+ } else {
+ s->buf[0] = 0;
+ memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
+ }
+ memcpy(&s->buf[8], "QEMU ", 8);
+ memcpy(&s->buf[32], QEMU_VERSION, 4);
+ /* Identify device as SCSI-3 rev 1.
+ Some later commands are also implemented. */
+ s->buf[2] = 3;
+ s->buf[3] = 2; /* Format 2 */
+ s->buf[4] = 32;
+ s->buf_len = 36;
+ break;
+ case 0x16:
+ DPRINTF("Reserve(6)\n");
+ if (buf[1] & 1)
+ goto fail;
+ break;
+ case 0x17:
+ DPRINTF("Release(6)\n");
+ if (buf[1] & 1)
+ goto fail;
+ break;
+ case 0x1a:
+ case 0x5a:
+ {
+ char *p;
+ int page;
+
+ page = buf[2] & 0x3f;
+ DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
+ p = s->buf;
+ memset(p, 0, 4);
+ s->buf[1] = 0; /* Default media type. */
+ s->buf[3] = 0; /* Block descriptor length. */
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ s->buf[2] = 0x80; /* Readonly. */
+ }
+ p += 4;
+ if ((page == 8 || page == 0x3f)) {
+ /* Caching page. */
+ p[0] = 8;
+ p[1] = 0x12;
+ p[2] = 4; /* WCE */
+ p += 19;
+ }
+ if ((page == 0x3f || page == 0x2a)
+ && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+ /* CD Capabilities and Mechanical Status page. */
+ p[0] = 0x2a;
+ p[1] = 0x14;
+ p[2] = 3; // CD-R & CD-RW read
+ p[3] = 0; // Writing not supported
+ p[4] = 0x7f; /* Audio, composite, digital out,
+ mode 2 form 1&2, multi session */
+ p[5] = 0xff; /* CD DA, DA accurate, RW supported,
+ RW corrected, C2 errors, ISRC,
+ UPC, Bar code */
+ p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+ /* Locking supported, jumper present, eject, tray */
+ p[7] = 0; /* no volume & mute control, no
+ changer */
+ p[8] = (50 * 176) >> 8; // 50x read speed
+ p[9] = (50 * 176) & 0xff;
+ p[10] = 0 >> 8; // No volume
+ p[11] = 0 & 0xff;
+ p[12] = 2048 >> 8; // 2M buffer
+ p[13] = 2048 & 0xff;
+ p[14] = (16 * 176) >> 8; // 16x read speed current
+ p[15] = (16 * 176) & 0xff;
+ p[18] = (16 * 176) >> 8; // 16x write speed
+ p[19] = (16 * 176) & 0xff;
+ p[20] = (16 * 176) >> 8; // 16x write speed current
+ p[21] = (16 * 176) & 0xff;
+ p += 21;
+ }
+ s->buf_len = p - s->buf;
+ s->buf[0] = s->buf_len - 4;
+ if (s->buf_len > len)
+ s->buf_len = len;
+ }
+ break;
+ case 0x1b:
+ DPRINTF("Start Stop Unit\n");
+ break;
+ case 0x1e:
+ DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
+ bdrv_set_locked(s->bdrv, buf[4] & 1);
+ break;
+ case 0x25:
+ DPRINTF("Read Capacity\n");
+ /* The normal LEN field for this command is zero. */
+ memset(s->buf, 0, 8);
+ bdrv_get_geometry(s->bdrv, &nb_sectors);
+ s->buf[0] = (nb_sectors >> 24) & 0xff;
+ s->buf[1] = (nb_sectors >> 16) & 0xff;
+ s->buf[2] = (nb_sectors >> 8) & 0xff;
+ s->buf[3] = nb_sectors & 0xff;
+ s->buf[4] = 0;
+ s->buf[5] = 0;
+ s->buf[6] = s->cluster_size * 2;
+ s->buf[7] = 0;
+ s->buf_len = 8;
+ break;
+ case 0x08:
+ case 0x28:
+ DPRINTF("Read (sector %d, count %d)\n", lba, len);
+ s->sector = lba * s->cluster_size;
+ s->sector_count = len * s->cluster_size;
+ break;
+ case 0x0a:
+ case 0x2a:
+ DPRINTF("Write (sector %d, count %d)\n", lba, len);
+ s->sector = lba * s->cluster_size;
+ s->sector_count = len * s->cluster_size;
+ is_write = 1;
+ break;
+ case 0x35:
+ DPRINTF("Syncronise cache (sector %d, count %d)\n", lba, len);
+ bdrv_flush(s->bdrv);
+ break;
+ case 0x43:
+ {
+ int start_track, format, msf, toclen;
+
+ msf = buf[1] & 2;
+ format = buf[2] & 0xf;
+ start_track = buf[6];
+ bdrv_get_geometry(s->bdrv, &nb_sectors);
+ DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+ switch(format) {
+ case 0:
+ toclen = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
+ break;
+ case 1:
+ /* multi session : only a single session defined */
+ toclen = 12;
+ memset(s->buf, 0, 12);
+ s->buf[1] = 0x0a;
+ s->buf[2] = 0x01;
+ s->buf[3] = 0x01;
+ break;
+ case 2:
+ toclen = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
+ break;
+ default:
+ goto error_cmd;
+ }
+ if (toclen > 0) {
+ if (len > toclen)
+ len = toclen;
+ s->buf_len = len;
+ break;
+ }
+ error_cmd:
+ DPRINTF("Read TOC error\n");
+ goto fail;
+ }
+ case 0x46:
+ DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
+ memset(s->buf, 0, 8);
+ /* ??? This shoud probably return much more information. For now
+ just return the basic header indicating the CD-ROM profile. */
+ s->buf[7] = 8; // CD-ROM
+ s->buf_len = 8;
+ break;
+ case 0x56:
+ DPRINTF("Reserve(10)\n");
+ if (buf[1] & 3)
+ goto fail;
+ break;
+ case 0x57:
+ DPRINTF("Release(10)\n");
+ if (buf[1] & 3)
+ goto fail;
+ break;
+ case 0xa0:
+ DPRINTF("Report LUNs (len %d)\n", len);
+ if (len < 16)
+ goto fail;
+ memset(s->buf, 0, 16);
+ s->buf[3] = 8;
+ s->buf_len = 16;
+ break;
+ default:
+ DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+ fail:
+ scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
+ return 0;
+ }
+ if (s->sector_count == 0 && s->buf_len == 0) {
+ scsi_command_complete(s, SENSE_NO_SENSE);
+ }
+ len = s->sector_count * 512 + s->buf_len;
+ return is_write ? -len : len;
+}
+
+void scsi_disk_destroy(SCSIDevice *s)
+{
+ bdrv_close(s->bdrv);
+ qemu_free(s);
+}
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
+ scsi_completionfn completion,
+ void *opaque)
+{
+ SCSIDevice *s;
+
+ s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
+ s->bdrv = bdrv;
+ s->completion = completion;
+ s->opaque = opaque;
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ s->cluster_size = 4;
+ } else {
+ s->cluster_size = 1;
+ }
+
+ return s;
+}
+
diff --git a/hw/serial.c b/hw/serial.c
new file mode 100644
index 0000000..f36beb2
--- /dev/null
+++ b/hw/serial.c
@@ -0,0 +1,456 @@
+/*
+ * QEMU 16450 UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_SERIAL
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+#define UART_MCR_OUT2 0x08 /* Out2 complement */
+#define UART_MCR_OUT1 0x04 /* Out1 complement */
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
+#define UART_MSR_RI 0x40 /* Ring Indicator */
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+#define UART_MSR_CTS 0x10 /* Clear to Send */
+#define UART_MSR_DDCD 0x08 /* Delta DCD */
+#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
+#define UART_MSR_DDSR 0x02 /* Delta DSR */
+#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+
+struct SerialState {
+ uint8_t divider;
+ uint8_t rbr; /* receive register */
+ uint8_t ier;
+ uint8_t iir; /* read only */
+ uint8_t lcr;
+ uint8_t mcr;
+ uint8_t lsr; /* read only */
+ uint8_t msr; /* read only */
+ uint8_t scr;
+ /* NOTE: this hidden state is necessary for tx irq generation as
+ it can be reset while reading iir */
+ int thr_ipending;
+ SetIRQFunc *set_irq;
+ void *irq_opaque;
+ int irq;
+ CharDriverState *chr;
+ int last_break_enable;
+ target_ulong base;
+ int it_shift;
+};
+
+static void serial_update_irq(SerialState *s)
+{
+ if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
+ s->iir = UART_IIR_RDI;
+ } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
+ s->iir = UART_IIR_THRI;
+ } else {
+ s->iir = UART_IIR_NO_INT;
+ }
+ if (s->iir != UART_IIR_NO_INT) {
+ s->set_irq(s->irq_opaque, s->irq, 1);
+ } else {
+ s->set_irq(s->irq_opaque, s->irq, 0);
+ }
+}
+
+static void serial_update_parameters(SerialState *s)
+{
+ int speed, parity, data_bits, stop_bits;
+ QEMUSerialSetParams ssp;
+
+ if (s->lcr & 0x08) {
+ if (s->lcr & 0x10)
+ parity = 'E';
+ else
+ parity = 'O';
+ } else {
+ parity = 'N';
+ }
+ if (s->lcr & 0x04)
+ stop_bits = 2;
+ else
+ stop_bits = 1;
+ data_bits = (s->lcr & 0x03) + 5;
+ if (s->divider == 0)
+ return;
+ speed = 115200 / s->divider;
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+#if 0
+ printf("speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ SerialState *s = opaque;
+ unsigned char ch;
+
+ addr &= 7;
+#ifdef DEBUG_SERIAL
+ printf("serial: write addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0xff00) | val;
+ serial_update_parameters(s);
+ } else {
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ ch = val;
+ qemu_chr_write(s->chr, &ch, 1);
+ s->thr_ipending = 1;
+ s->lsr |= UART_LSR_THRE;
+ s->lsr |= UART_LSR_TEMT;
+ serial_update_irq(s);
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0x00ff) | (val << 8);
+ serial_update_parameters(s);
+ } else {
+ s->ier = val & 0x0f;
+ if (s->lsr & UART_LSR_THRE) {
+ s->thr_ipending = 1;
+ }
+ serial_update_irq(s);
+ }
+ break;
+ case 2:
+ break;
+ case 3:
+ {
+ int break_enable;
+ s->lcr = val;
+ serial_update_parameters(s);
+ break_enable = (val >> 6) & 1;
+ if (break_enable != s->last_break_enable) {
+ s->last_break_enable = break_enable;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enable);
+ }
+ }
+ break;
+ case 4:
+ s->mcr = val & 0x1f;
+ break;
+ case 5:
+ break;
+ case 6:
+ break;
+ case 7:
+ s->scr = val;
+ break;
+ }
+}
+
+static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t ret;
+
+ addr &= 7;
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = s->divider & 0xff;
+ } else {
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ serial_update_irq(s);
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = (s->divider >> 8) & 0xff;
+ } else {
+ ret = s->ier;
+ }
+ break;
+ case 2:
+ ret = s->iir;
+ /* reset THR pending bit */
+ if ((ret & 0x7) == UART_IIR_THRI)
+ s->thr_ipending = 0;
+ serial_update_irq(s);
+ break;
+ case 3:
+ ret = s->lcr;
+ break;
+ case 4:
+ ret = s->mcr;
+ break;
+ case 5:
+ ret = s->lsr;
+ break;
+ case 6:
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback, the modem output pins are connected to the
+ inputs */
+ ret = (s->mcr & 0x0c) << 4;
+ ret |= (s->mcr & 0x02) << 3;
+ ret |= (s->mcr & 0x01) << 5;
+ } else {
+ ret = s->msr;
+ }
+ break;
+ case 7:
+ ret = s->scr;
+ break;
+ }
+#ifdef DEBUG_SERIAL
+ printf("serial: read addr=0x%02x val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static int serial_can_receive(SerialState *s)
+{
+ return !(s->lsr & UART_LSR_DR);
+}
+
+static void serial_receive_byte(SerialState *s, int ch)
+{
+ s->rbr = ch;
+ s->lsr |= UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+static void serial_receive_break(SerialState *s)
+{
+ s->rbr = 0;
+ s->lsr |= UART_LSR_BI | UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+static int serial_can_receive1(void *opaque)
+{
+ SerialState *s = opaque;
+ return serial_can_receive(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ SerialState *s = opaque;
+ serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ SerialState *s = opaque;
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static void serial_save(QEMUFile *f, void *opaque)
+{
+ SerialState *s = opaque;
+
+ qemu_put_8s(f,&s->divider);
+ qemu_put_8s(f,&s->rbr);
+ qemu_put_8s(f,&s->ier);
+ qemu_put_8s(f,&s->iir);
+ qemu_put_8s(f,&s->lcr);
+ qemu_put_8s(f,&s->mcr);
+ qemu_put_8s(f,&s->lsr);
+ qemu_put_8s(f,&s->msr);
+ qemu_put_8s(f,&s->scr);
+}
+
+static int serial_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SerialState *s = opaque;
+
+ if(version_id != 1)
+ return -EINVAL;
+
+ qemu_get_8s(f,&s->divider);
+ qemu_get_8s(f,&s->rbr);
+ qemu_get_8s(f,&s->ier);
+ qemu_get_8s(f,&s->iir);
+ qemu_get_8s(f,&s->lcr);
+ qemu_get_8s(f,&s->mcr);
+ qemu_get_8s(f,&s->lsr);
+ qemu_get_8s(f,&s->msr);
+ qemu_get_8s(f,&s->scr);
+
+ return 0;
+}
+
+/* If fd is zero, it means that the serial device uses the console */
+SerialState *serial_init(SetIRQFunc *set_irq, void *opaque,
+ int base, int irq, CharDriverState *chr)
+{
+ SerialState *s;
+
+ s = qemu_mallocz(sizeof(SerialState));
+ if (!s)
+ return NULL;
+ s->set_irq = set_irq;
+ s->irq_opaque = opaque;
+ s->irq = irq;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+ s->iir = UART_IIR_NO_INT;
+ s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+
+ register_savevm("serial", base, 1, serial_save, serial_load, s);
+
+ register_ioport_write(base, 8, 1, serial_ioport_write, s);
+ register_ioport_read(base, 8, 1, serial_ioport_read, s);
+ s->chr = chr;
+ qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s);
+ qemu_chr_add_event_handler(chr, serial_event);
+ return s;
+}
+
+/* Memory mapped interface */
+static uint32_t serial_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+
+ return serial_ioport_read(s, (addr - s->base) >> s->it_shift) & 0xFF;
+}
+
+static void serial_mm_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, (addr - s->base) >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t serial_mm_readw (void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+
+ return serial_ioport_read(s, (addr - s->base) >> s->it_shift) & 0xFFFF;
+}
+
+static void serial_mm_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, (addr - s->base) >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t serial_mm_readl (void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+
+ return serial_ioport_read(s, (addr - s->base) >> s->it_shift);
+}
+
+static void serial_mm_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, (addr - s->base) >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc *serial_mm_read[] = {
+ &serial_mm_readb,
+ &serial_mm_readw,
+ &serial_mm_readl,
+};
+
+static CPUWriteMemoryFunc *serial_mm_write[] = {
+ &serial_mm_writeb,
+ &serial_mm_writew,
+ &serial_mm_writel,
+};
+
+SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque,
+ target_ulong base, int it_shift,
+ int irq, CharDriverState *chr)
+{
+ SerialState *s;
+ int s_io_memory;
+
+ s = qemu_mallocz(sizeof(SerialState));
+ if (!s)
+ return NULL;
+ s->set_irq = set_irq;
+ s->irq_opaque = opaque;
+ s->irq = irq;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+ s->iir = UART_IIR_NO_INT;
+ s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+ s->base = base;
+ s->it_shift = it_shift;
+
+ register_savevm("serial", base, 1, serial_save, serial_load, s);
+
+ s_io_memory = cpu_register_io_memory(0, serial_mm_read,
+ serial_mm_write, s);
+ cpu_register_physical_memory(base, 8 << it_shift, s_io_memory);
+ s->chr = chr;
+ qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s);
+ qemu_chr_add_event_handler(chr, serial_event);
+ return s;
+}
diff --git a/hw/sh7750.c b/hw/sh7750.c
new file mode 100644
index 0000000..21f9bc0
--- /dev/null
+++ b/hw/sh7750.c
@@ -0,0 +1,836 @@
+/*
+ * SH7750 device
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdio.h>
+#include <assert.h>
+#include "vl.h"
+#include "sh7750_regs.h"
+#include "sh7750_regnames.h"
+
+typedef struct {
+ uint8_t data[16];
+ uint8_t length; /* Number of characters in the FIFO */
+ uint8_t write_idx; /* Index of first character to write */
+ uint8_t read_idx; /* Index of first character to read */
+} fifo;
+
+#define NB_DEVICES 4
+
+typedef struct SH7750State {
+ /* CPU */
+ CPUSH4State *cpu;
+ /* Peripheral frequency in Hz */
+ uint32_t periph_freq;
+ /* SDRAM controller */
+ uint16_t rfcr;
+ /* First serial port */
+ CharDriverState *serial1;
+ uint8_t scscr1;
+ uint8_t scsmr1;
+ uint8_t scbrr1;
+ uint8_t scssr1;
+ uint8_t scssr1_read;
+ uint8_t sctsr1;
+ uint8_t sctsr1_loaded;
+ uint8_t sctdr1;
+ uint8_t scrdr1;
+ /* Second serial port */
+ CharDriverState *serial2;
+ uint16_t sclsr2;
+ uint16_t scscr2;
+ uint16_t scfcr2;
+ uint16_t scfsr2;
+ uint16_t scsmr2;
+ uint8_t scbrr2;
+ fifo serial2_receive_fifo;
+ fifo serial2_transmit_fifo;
+ /* Timers */
+ uint8_t tstr;
+ /* Timer 0 */
+ QEMUTimer *timer0;
+ uint16_t tcr0;
+ uint32_t tcor0;
+ uint32_t tcnt0;
+ /* IO ports */
+ uint16_t gpioic;
+ uint32_t pctra;
+ uint32_t pctrb;
+ uint16_t portdira; /* Cached */
+ uint16_t portpullupa; /* Cached */
+ uint16_t portdirb; /* Cached */
+ uint16_t portpullupb; /* Cached */
+ uint16_t pdtra;
+ uint16_t pdtrb;
+ uint16_t periph_pdtra; /* Imposed by the peripherals */
+ uint16_t periph_portdira; /* Direction seen from the peripherals */
+ uint16_t periph_pdtrb; /* Imposed by the peripherals */
+ uint16_t periph_portdirb; /* Direction seen from the peripherals */
+ sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */
+ /* Cache */
+ uint32_t ccr;
+} SH7750State;
+
+/**********************************************************************
+ Timers
+**********************************************************************/
+
+/* XXXXX At this time, timer0 works in underflow only mode, that is
+ the value of tcnt0 is read at alarm computation time and cannot
+ be read back by the guest OS */
+
+static void start_timer0(SH7750State * s)
+{
+ uint64_t now, next, prescaler;
+
+ if ((s->tcr0 & 6) == 6) {
+ fprintf(stderr, "rtc clock for timer 0 not supported\n");
+ assert(0);
+ }
+
+ if ((s->tcr0 & 7) == 5) {
+ fprintf(stderr, "timer 0 configuration not supported\n");
+ assert(0);
+ }
+
+ if ((s->tcr0 & 4) == 4)
+ prescaler = 1024;
+ else
+ prescaler = 4 << (s->tcr0 & 3);
+
+ now = qemu_get_clock(vm_clock);
+ /* XXXXX */
+ next =
+ now + muldiv64(prescaler * s->tcnt0, ticks_per_sec,
+ s->periph_freq);
+ if (next == now)
+ next = now + 1;
+ fprintf(stderr, "now=%016" PRIx64 ", next=%016" PRIx64 "\n", now, next);
+ fprintf(stderr, "timer will underflow in %f seconds\n",
+ (float) (next - now) / (float) ticks_per_sec);
+
+ qemu_mod_timer(s->timer0, next);
+}
+
+static void timer_start_changed(SH7750State * s)
+{
+ if (s->tstr & SH7750_TSTR_STR0) {
+ start_timer0(s);
+ } else {
+ fprintf(stderr, "timer 0 is stopped\n");
+ qemu_del_timer(s->timer0);
+ }
+}
+
+static void timer0_cb(void *opaque)
+{
+ SH7750State *s = opaque;
+
+ s->tcnt0 = (uint32_t) 0; /* XXXXX */
+ if (--s->tcnt0 == (uint32_t) - 1) {
+ fprintf(stderr, "timer 0 underflow\n");
+ s->tcnt0 = s->tcor0;
+ s->tcr0 |= SH7750_TCR_UNF;
+ if (s->tcr0 & SH7750_TCR_UNIE) {
+ fprintf(stderr,
+ "interrupt generation for timer 0 not supported\n");
+ assert(0);
+ }
+ }
+ start_timer0(s);
+}
+
+static void init_timers(SH7750State * s)
+{
+ s->tcor0 = 0xffffffff;
+ s->tcnt0 = 0xffffffff;
+ s->timer0 = qemu_new_timer(vm_clock, &timer0_cb, s);
+}
+
+/**********************************************************************
+ First serial port
+**********************************************************************/
+
+static int serial1_can_receive(void *opaque)
+{
+ SH7750State *s = opaque;
+
+ return s->scscr1 & SH7750_SCSCR_RE;
+}
+
+static void serial1_receive_char(SH7750State * s, uint8_t c)
+{
+ if (s->scssr1 & SH7750_SCSSR1_RDRF) {
+ s->scssr1 |= SH7750_SCSSR1_ORER;
+ return;
+ }
+
+ s->scrdr1 = c;
+ s->scssr1 |= SH7750_SCSSR1_RDRF;
+}
+
+static void serial1_receive(void *opaque, const uint8_t * buf, int size)
+{
+ SH7750State *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ serial1_receive_char(s, buf[i]);
+ }
+}
+
+static void serial1_event(void *opaque, int event)
+{
+ assert(0);
+}
+
+static void serial1_maybe_send(SH7750State * s)
+{
+ uint8_t c;
+
+ if (s->scssr1 & SH7750_SCSSR1_TDRE)
+ return;
+ c = s->sctdr1;
+ s->scssr1 |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND;
+ if (s->scscr1 & SH7750_SCSCR_TIE) {
+ fprintf(stderr, "interrupts for serial port 1 not implemented\n");
+ assert(0);
+ }
+ /* XXXXX Check for errors in write */
+ qemu_chr_write(s->serial1, &c, 1);
+}
+
+static void serial1_change_scssr1(SH7750State * s, uint8_t mem_value)
+{
+ uint8_t new_flags;
+
+ /* If transmit disable, TDRE and TEND stays up */
+ if ((s->scscr1 & SH7750_SCSCR_TE) == 0) {
+ mem_value |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND;
+ }
+
+ /* Only clear bits which have been read before and do not set any bit
+ in the flags */
+ new_flags = s->scssr1 & ~s->scssr1_read; /* Preserve unread flags */
+ new_flags &= mem_value | ~s->scssr1_read; /* Clear read flags */
+
+ s->scssr1 = (new_flags & 0xf8) | (mem_value & 1);
+ s->scssr1_read &= mem_value;
+
+ /* If TDRE has been cleared, TEND will also be cleared */
+ if ((s->scssr1 & SH7750_SCSSR1_TDRE) == 0) {
+ s->scssr1 &= ~SH7750_SCSSR1_TEND;
+ }
+
+ /* Check for transmission to start */
+ serial1_maybe_send(s);
+}
+
+static void serial1_update_parameters(SH7750State * s)
+{
+ QEMUSerialSetParams ssp;
+
+ if (s->scsmr1 & SH7750_SCSMR_CHR_7)
+ ssp.data_bits = 7;
+ else
+ ssp.data_bits = 8;
+ if (s->scsmr1 & SH7750_SCSMR_PE) {
+ if (s->scsmr1 & SH7750_SCSMR_PM_ODD)
+ ssp.parity = 'O';
+ else
+ ssp.parity = 'E';
+ } else
+ ssp.parity = 'N';
+ if (s->scsmr1 & SH7750_SCSMR_STOP_2)
+ ssp.stop_bits = 2;
+ else
+ ssp.stop_bits = 1;
+ fprintf(stderr, "SCSMR1=%04x SCBRR1=%02x\n", s->scsmr1, s->scbrr1);
+ ssp.speed = s->periph_freq /
+ (32 * s->scbrr1 * (1 << (2 * (s->scsmr1 & 3)))) - 1;
+ fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n",
+ ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed);
+ qemu_chr_ioctl(s->serial1, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void scscr1_changed(SH7750State * s)
+{
+ if (s->scscr1 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) {
+ if (!s->serial1) {
+ fprintf(stderr, "serial port 1 not bound to anything\n");
+ assert(0);
+ }
+ serial1_update_parameters(s);
+ }
+ if ((s->scscr1 & SH7750_SCSCR_RE) == 0) {
+ s->scssr1 |= SH7750_SCSSR1_TDRE;
+ }
+}
+
+static void init_serial1(SH7750State * s, int serial_nb)
+{
+ CharDriverState *chr;
+
+ s->scssr1 = 0x84;
+ chr = serial_hds[serial_nb];
+ if (!chr) {
+ fprintf(stderr,
+ "no serial port associated to SH7750 first serial port\n");
+ return;
+ }
+
+ s->serial1 = chr;
+ qemu_chr_add_read_handler(chr, serial1_can_receive,
+ serial1_receive, s);
+ qemu_chr_add_event_handler(chr, serial1_event);
+}
+
+/**********************************************************************
+ Second serial port
+**********************************************************************/
+
+static int serial2_can_receive(void *opaque)
+{
+ SH7750State *s = opaque;
+ static uint8_t max_fifo_size[] = { 15, 1, 4, 6, 8, 10, 12, 14 };
+
+ return s->serial2_receive_fifo.length <
+ max_fifo_size[(s->scfcr2 >> 9) & 7];
+}
+
+static void serial2_adjust_receive_flags(SH7750State * s)
+{
+ static uint8_t max_fifo_size[] = { 1, 4, 8, 14 };
+
+ /* XXXXX Add interrupt generation */
+ if (s->serial2_receive_fifo.length >=
+ max_fifo_size[(s->scfcr2 >> 7) & 3]) {
+ s->scfsr2 |= SH7750_SCFSR2_RDF;
+ s->scfsr2 &= ~SH7750_SCFSR2_DR;
+ } else {
+ s->scfsr2 &= ~SH7750_SCFSR2_RDF;
+ if (s->serial2_receive_fifo.length > 0)
+ s->scfsr2 |= SH7750_SCFSR2_DR;
+ else
+ s->scfsr2 &= ~SH7750_SCFSR2_DR;
+ }
+}
+
+static void serial2_append_char(SH7750State * s, uint8_t c)
+{
+ if (s->serial2_receive_fifo.length == 16) {
+ /* Overflow */
+ s->sclsr2 |= SH7750_SCLSR2_ORER;
+ return;
+ }
+
+ s->serial2_receive_fifo.data[s->serial2_receive_fifo.write_idx++] = c;
+ s->serial2_receive_fifo.length++;
+ serial2_adjust_receive_flags(s);
+}
+
+static void serial2_receive(void *opaque, const uint8_t * buf, int size)
+{
+ SH7750State *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++)
+ serial2_append_char(s, buf[i]);
+}
+
+static void serial2_event(void *opaque, int event)
+{
+ /* XXXXX */
+ assert(0);
+}
+
+static void serial2_update_parameters(SH7750State * s)
+{
+ QEMUSerialSetParams ssp;
+
+ if (s->scsmr2 & SH7750_SCSMR_CHR_7)
+ ssp.data_bits = 7;
+ else
+ ssp.data_bits = 8;
+ if (s->scsmr2 & SH7750_SCSMR_PE) {
+ if (s->scsmr2 & SH7750_SCSMR_PM_ODD)
+ ssp.parity = 'O';
+ else
+ ssp.parity = 'E';
+ } else
+ ssp.parity = 'N';
+ if (s->scsmr2 & SH7750_SCSMR_STOP_2)
+ ssp.stop_bits = 2;
+ else
+ ssp.stop_bits = 1;
+ fprintf(stderr, "SCSMR2=%04x SCBRR2=%02x\n", s->scsmr2, s->scbrr2);
+ ssp.speed = s->periph_freq /
+ (32 * s->scbrr2 * (1 << (2 * (s->scsmr2 & 3)))) - 1;
+ fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n",
+ ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed);
+ qemu_chr_ioctl(s->serial2, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void scscr2_changed(SH7750State * s)
+{
+ if (s->scscr2 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) {
+ if (!s->serial2) {
+ fprintf(stderr, "serial port 2 not bound to anything\n");
+ assert(0);
+ }
+ serial2_update_parameters(s);
+ }
+}
+
+static void init_serial2(SH7750State * s, int serial_nb)
+{
+ CharDriverState *chr;
+
+ s->scfsr2 = 0x0060;
+
+ chr = serial_hds[serial_nb];
+ if (!chr) {
+ fprintf(stderr,
+ "no serial port associated to SH7750 second serial port\n");
+ return;
+ }
+
+ s->serial2 = chr;
+ qemu_chr_add_read_handler(chr, serial2_can_receive,
+ serial2_receive, s);
+ qemu_chr_add_event_handler(chr, serial2_event);
+}
+
+static void init_serial_ports(SH7750State * s)
+{
+ init_serial1(s, 0);
+ init_serial2(s, 1);
+}
+
+/**********************************************************************
+ I/O ports
+**********************************************************************/
+
+int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device)
+{
+ int i;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] == NULL) {
+ s->devices[i] = device;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static uint16_t portdir(uint32_t v)
+{
+#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n))
+ return
+ EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) |
+ EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) |
+ EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) |
+ EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) |
+ EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) |
+ EVENPORTMASK(0);
+}
+
+static uint16_t portpullup(uint32_t v)
+{
+#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n))
+ return
+ ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) |
+ ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) |
+ ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) |
+ ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) |
+ ODDPORTMASK(1) | ODDPORTMASK(0);
+}
+
+static uint16_t porta_lines(SH7750State * s)
+{
+ return (s->portdira & s->pdtra) | /* CPU */
+ (s->periph_portdira & s->periph_pdtra) | /* Peripherals */
+ (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */
+}
+
+static uint16_t portb_lines(SH7750State * s)
+{
+ return (s->portdirb & s->pdtrb) | /* CPU */
+ (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */
+ (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */
+}
+
+static void gen_port_interrupts(SH7750State * s)
+{
+ /* XXXXX interrupts not generated */
+}
+
+static void porta_changed(SH7750State * s, uint16_t prev)
+{
+ uint16_t currenta, changes;
+ int i, r = 0;
+
+#if 0
+ fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n",
+ prev, porta_lines(s));
+ fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra);
+#endif
+ currenta = porta_lines(s);
+ if (currenta == prev)
+ return;
+ changes = currenta ^ prev;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) {
+ r |= s->devices[i]->port_change_cb(currenta, portb_lines(s),
+ &s->periph_pdtra,
+ &s->periph_portdira,
+ &s->periph_pdtrb,
+ &s->periph_portdirb);
+ }
+ }
+
+ if (r)
+ gen_port_interrupts(s);
+}
+
+static void portb_changed(SH7750State * s, uint16_t prev)
+{
+ uint16_t currentb, changes;
+ int i, r = 0;
+
+ currentb = portb_lines(s);
+ if (currentb == prev)
+ return;
+ changes = currentb ^ prev;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) {
+ r |= s->devices[i]->port_change_cb(portb_lines(s), currentb,
+ &s->periph_pdtra,
+ &s->periph_portdira,
+ &s->periph_pdtrb,
+ &s->periph_portdirb);
+ }
+ }
+
+ if (r)
+ gen_port_interrupts(s);
+}
+
+/**********************************************************************
+ Memory
+**********************************************************************/
+
+static void error_access(const char *kind, target_phys_addr_t addr)
+{
+ fprintf(stderr, "%s to %s (0x%08x) not supported\n",
+ kind, regname(addr), addr);
+}
+
+static void ignore_access(const char *kind, target_phys_addr_t addr)
+{
+ fprintf(stderr, "%s to %s (0x%08x) ignored\n",
+ kind, regname(addr), addr);
+}
+
+static uint32_t sh7750_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+ uint8_t r;
+
+ switch (addr) {
+ case SH7750_SCSSR1_A7:
+ r = s->scssr1;
+ s->scssr1_read |= r;
+ return s->scssr1;
+ case SH7750_SCRDR1_A7:
+ s->scssr1 &= ~SH7750_SCSSR1_RDRF;
+ return s->scrdr1;
+ default:
+ error_access("byte read", addr);
+ assert(0);
+ }
+}
+
+static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+ uint16_t r;
+
+ switch (addr) {
+ case SH7750_RFCR_A7:
+ fprintf(stderr,
+ "Read access to refresh count register, incrementing\n");
+ return s->rfcr++;
+ case SH7750_TCR0_A7:
+ return s->tcr0;
+ case SH7750_SCLSR2_A7:
+ /* Read and clear overflow bit */
+ r = s->sclsr2;
+ s->sclsr2 = 0;
+ return r;
+ case SH7750_SCSFR2_A7:
+ return s->scfsr2;
+ case SH7750_PDTRA_A7:
+ return porta_lines(s);
+ case SH7750_PDTRB_A7:
+ return portb_lines(s);
+ default:
+ error_access("word read", addr);
+ assert(0);
+ }
+}
+
+static uint32_t sh7750_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+
+ switch (addr) {
+ case SH7750_MMUCR_A7:
+ return s->cpu->mmucr;
+ case SH7750_PTEH_A7:
+ return s->cpu->pteh;
+ case SH7750_PTEL_A7:
+ return s->cpu->ptel;
+ case SH7750_TTB_A7:
+ return s->cpu->ttb;
+ case SH7750_TEA_A7:
+ return s->cpu->tea;
+ case SH7750_TRA_A7:
+ return s->cpu->tra;
+ case SH7750_EXPEVT_A7:
+ return s->cpu->expevt;
+ case SH7750_INTEVT_A7:
+ return s->cpu->intevt;
+ case SH7750_CCR_A7:
+ return s->ccr;
+ case 0x1f000030: /* Processor version PVR */
+ return 0x00050000; /* SH7750R */
+ case 0x1f000040: /* Processor version CVR */
+ return 0x00110000; /* Minimum caches */
+ case 0x1f000044: /* Processor version PRR */
+ return 0x00000100; /* SH7750R */
+ default:
+ error_access("long read", addr);
+ assert(0);
+ }
+}
+
+static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+
+ switch (addr) {
+ /* PRECHARGE ? XXXXX */
+ case SH7750_PRECHARGE0_A7:
+ case SH7750_PRECHARGE1_A7:
+ ignore_access("byte write", addr);
+ return;
+ case SH7750_SCBRR2_A7:
+ s->scbrr2 = mem_value;
+ return;
+ case SH7750_TSTR_A7:
+ s->tstr = mem_value;
+ timer_start_changed(s);
+ return;
+ case SH7750_SCSCR1_A7:
+ s->scscr1 = mem_value;
+ scscr1_changed(s);
+ return;
+ case SH7750_SCSMR1_A7:
+ s->scsmr1 = mem_value;
+ return;
+ case SH7750_SCBRR1_A7:
+ s->scbrr1 = mem_value;
+ return;
+ case SH7750_SCTDR1_A7:
+ s->scssr1 &= ~SH7750_SCSSR1_TEND;
+ s->sctdr1 = mem_value;
+ return;
+ case SH7750_SCSSR1_A7:
+ serial1_change_scssr1(s, mem_value);
+ return;
+ default:
+ error_access("byte write", addr);
+ assert(0);
+ }
+}
+
+static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+ uint16_t temp;
+
+ switch (addr) {
+ /* SDRAM controller */
+ case SH7750_SCBRR1_A7:
+ case SH7750_SCBRR2_A7:
+ case SH7750_BCR2_A7:
+ case SH7750_BCR3_A7:
+ case SH7750_RTCOR_A7:
+ case SH7750_RTCNT_A7:
+ case SH7750_RTCSR_A7:
+ ignore_access("word write", addr);
+ return;
+ /* IO ports */
+ case SH7750_PDTRA_A7:
+ temp = porta_lines(s);
+ s->pdtra = mem_value;
+ porta_changed(s, temp);
+ return;
+ case SH7750_PDTRB_A7:
+ temp = portb_lines(s);
+ s->pdtrb = mem_value;
+ portb_changed(s, temp);
+ return;
+ case SH7750_RFCR_A7:
+ fprintf(stderr, "Write access to refresh count register\n");
+ s->rfcr = mem_value;
+ return;
+ case SH7750_SCLSR2_A7:
+ s->sclsr2 = mem_value;
+ return;
+ case SH7750_SCSCR2_A7:
+ s->scscr2 = mem_value;
+ scscr2_changed(s);
+ return;
+ case SH7750_SCFCR2_A7:
+ s->scfcr2 = mem_value;
+ return;
+ case SH7750_SCSMR2_A7:
+ s->scsmr2 = mem_value;
+ return;
+ case SH7750_TCR0_A7:
+ s->tcr0 = mem_value;
+ return;
+ case SH7750_GPIOIC_A7:
+ s->gpioic = mem_value;
+ if (mem_value != 0) {
+ fprintf(stderr, "I/O interrupts not implemented\n");
+ assert(0);
+ }
+ return;
+ default:
+ error_access("word write", addr);
+ assert(0);
+ }
+}
+
+static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+ uint16_t temp;
+
+ switch (addr) {
+ /* SDRAM controller */
+ case SH7750_BCR1_A7:
+ case SH7750_BCR4_A7:
+ case SH7750_WCR1_A7:
+ case SH7750_WCR2_A7:
+ case SH7750_WCR3_A7:
+ case SH7750_MCR_A7:
+ ignore_access("long write", addr);
+ return;
+ /* IO ports */
+ case SH7750_PCTRA_A7:
+ temp = porta_lines(s);
+ s->pctra = mem_value;
+ s->portdira = portdir(mem_value);
+ s->portpullupa = portpullup(mem_value);
+ porta_changed(s, temp);
+ return;
+ case SH7750_PCTRB_A7:
+ temp = portb_lines(s);
+ s->pctrb = mem_value;
+ s->portdirb = portdir(mem_value);
+ s->portpullupb = portpullup(mem_value);
+ portb_changed(s, temp);
+ return;
+ case SH7750_TCNT0_A7:
+ s->tcnt0 = mem_value & 0xf;
+ return;
+ case SH7750_MMUCR_A7:
+ s->cpu->mmucr = mem_value;
+ return;
+ case SH7750_PTEH_A7:
+ s->cpu->pteh = mem_value;
+ return;
+ case SH7750_PTEL_A7:
+ s->cpu->ptel = mem_value;
+ return;
+ case SH7750_TTB_A7:
+ s->cpu->ttb = mem_value;
+ return;
+ case SH7750_TEA_A7:
+ s->cpu->tea = mem_value;
+ return;
+ case SH7750_TRA_A7:
+ s->cpu->tra = mem_value & 0x000007ff;
+ return;
+ case SH7750_EXPEVT_A7:
+ s->cpu->expevt = mem_value & 0x000007ff;
+ return;
+ case SH7750_INTEVT_A7:
+ s->cpu->intevt = mem_value & 0x000007ff;
+ return;
+ case SH7750_CCR_A7:
+ s->ccr = mem_value;
+ return;
+ default:
+ error_access("long write", addr);
+ assert(0);
+ }
+}
+
+static CPUReadMemoryFunc *sh7750_mem_read[] = {
+ sh7750_mem_readb,
+ sh7750_mem_readw,
+ sh7750_mem_readl
+};
+
+static CPUWriteMemoryFunc *sh7750_mem_write[] = {
+ sh7750_mem_writeb,
+ sh7750_mem_writew,
+ sh7750_mem_writel
+};
+
+SH7750State *sh7750_init(CPUSH4State * cpu)
+{
+ SH7750State *s;
+ int sh7750_io_memory;
+
+ s = qemu_mallocz(sizeof(SH7750State));
+ s->cpu = cpu;
+ s->periph_freq = 60000000; /* 60MHz */
+ sh7750_io_memory = cpu_register_io_memory(0,
+ sh7750_mem_read,
+ sh7750_mem_write, s);
+ cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory);
+ init_timers(s);
+ init_serial_ports(s);
+ return s;
+}
diff --git a/hw/sh7750_regnames.c b/hw/sh7750_regnames.c
new file mode 100644
index 0000000..5fcb0d6
--- /dev/null
+++ b/hw/sh7750_regnames.c
@@ -0,0 +1,128 @@
+#include "vl.h"
+#include "sh7750_regs.h"
+
+#define REGNAME(r) {r, #r},
+
+typedef struct {
+ uint32_t regaddr;
+ const char *regname;
+} regname_t;
+
+static regname_t regnames[] = {
+ REGNAME(SH7750_PTEH_A7)
+ REGNAME(SH7750_PTEL_A7)
+ REGNAME(SH7750_PTEA_A7)
+ REGNAME(SH7750_TTB_A7)
+ REGNAME(SH7750_TEA_A7)
+ REGNAME(SH7750_MMUCR_A7)
+ REGNAME(SH7750_CCR_A7)
+ REGNAME(SH7750_QACR0_A7)
+ REGNAME(SH7750_QACR1_A7)
+ REGNAME(SH7750_TRA_A7)
+ REGNAME(SH7750_EXPEVT_A7)
+ REGNAME(SH7750_INTEVT_A7)
+ REGNAME(SH7750_STBCR_A7)
+ REGNAME(SH7750_STBCR2_A7)
+ REGNAME(SH7750_FRQCR_A7)
+ REGNAME(SH7750_WTCNT_A7)
+ REGNAME(SH7750_WTCSR_A7)
+ REGNAME(SH7750_R64CNT_A7)
+ REGNAME(SH7750_RSECCNT_A7)
+ REGNAME(SH7750_RMINCNT_A7)
+ REGNAME(SH7750_RHRCNT_A7)
+ REGNAME(SH7750_RWKCNT_A7)
+ REGNAME(SH7750_RDAYCNT_A7)
+ REGNAME(SH7750_RMONCNT_A7)
+ REGNAME(SH7750_RYRCNT_A7)
+ REGNAME(SH7750_RSECAR_A7)
+ REGNAME(SH7750_RMINAR_A7)
+ REGNAME(SH7750_RHRAR_A7)
+ REGNAME(SH7750_RWKAR_A7)
+ REGNAME(SH7750_RDAYAR_A7)
+ REGNAME(SH7750_RMONAR_A7)
+ REGNAME(SH7750_RCR1_A7)
+ REGNAME(SH7750_RCR2_A7)
+ REGNAME(SH7750_TOCR_A7)
+ REGNAME(SH7750_TSTR_A7)
+ REGNAME(SH7750_TCOR0_A7)
+ REGNAME(SH7750_TCOR1_A7)
+ REGNAME(SH7750_TCOR2_A7)
+ REGNAME(SH7750_TCNT0_A7)
+ REGNAME(SH7750_TCNT1_A7)
+ REGNAME(SH7750_TCNT2_A7)
+ REGNAME(SH7750_TCR0_A7)
+ REGNAME(SH7750_TCR1_A7)
+ REGNAME(SH7750_TCR2_A7)
+ REGNAME(SH7750_TCPR2_A7)
+ REGNAME(SH7750_BCR1_A7)
+ REGNAME(SH7750_BCR2_A7)
+ REGNAME(SH7750_WCR1_A7)
+ REGNAME(SH7750_WCR2_A7)
+ REGNAME(SH7750_WCR3_A7)
+ REGNAME(SH7750_MCR_A7)
+ REGNAME(SH7750_PCR_A7)
+ REGNAME(SH7750_RTCSR_A7)
+ REGNAME(SH7750_RTCNT_A7)
+ REGNAME(SH7750_RTCOR_A7)
+ REGNAME(SH7750_RFCR_A7)
+ REGNAME(SH7750_SAR0_A7)
+ REGNAME(SH7750_SAR1_A7)
+ REGNAME(SH7750_SAR2_A7)
+ REGNAME(SH7750_SAR3_A7)
+ REGNAME(SH7750_DAR0_A7)
+ REGNAME(SH7750_DAR1_A7)
+ REGNAME(SH7750_DAR2_A7)
+ REGNAME(SH7750_DAR3_A7)
+ REGNAME(SH7750_DMATCR0_A7)
+ REGNAME(SH7750_DMATCR1_A7)
+ REGNAME(SH7750_DMATCR2_A7)
+ REGNAME(SH7750_DMATCR3_A7)
+ REGNAME(SH7750_CHCR0_A7)
+ REGNAME(SH7750_CHCR1_A7)
+ REGNAME(SH7750_CHCR2_A7)
+ REGNAME(SH7750_CHCR3_A7)
+ REGNAME(SH7750_DMAOR_A7)
+ REGNAME(SH7750_SCRDR1_A7)
+ REGNAME(SH7750_SCRDR2_A7)
+ REGNAME(SH7750_SCTDR1_A7)
+ REGNAME(SH7750_SCTDR2_A7)
+ REGNAME(SH7750_SCSMR1_A7)
+ REGNAME(SH7750_SCSMR2_A7)
+ REGNAME(SH7750_SCSCR1_A7)
+ REGNAME(SH7750_SCSCR2_A7)
+ REGNAME(SH7750_SCSSR1_A7)
+ REGNAME(SH7750_SCSFR2_A7)
+ REGNAME(SH7750_SCSPTR1_A7)
+ REGNAME(SH7750_SCSPTR2_A7)
+ REGNAME(SH7750_SCBRR1_A7)
+ REGNAME(SH7750_SCBRR2_A7)
+ REGNAME(SH7750_SCFCR2_A7)
+ REGNAME(SH7750_SCFDR2_A7)
+ REGNAME(SH7750_SCLSR2_A7)
+ REGNAME(SH7750_SCSCMR1_A7)
+ REGNAME(SH7750_PCTRA_A7)
+ REGNAME(SH7750_PDTRA_A7)
+ REGNAME(SH7750_PCTRB_A7)
+ REGNAME(SH7750_PDTRB_A7)
+ REGNAME(SH7750_GPIOIC_A7)
+ REGNAME(SH7750_ICR_A7)
+ REGNAME(SH7750_IPRA_A7)
+ REGNAME(SH7750_IPRB_A7)
+ REGNAME(SH7750_IPRC_A7)
+ REGNAME(SH7750_BCR3_A7)
+ REGNAME(SH7750_BCR4_A7)
+ REGNAME(SH7750_PRECHARGE0_A7)
+ REGNAME(SH7750_PRECHARGE1_A7) {(uint32_t) - 1, 0}
+};
+
+const char *regname(uint32_t addr)
+{
+ unsigned int i;
+
+ for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) {
+ if (regnames[i].regaddr == addr)
+ return regnames[i].regname;
+ }
+
+ return "<unknown reg>";
+}
diff --git a/hw/sh7750_regnames.h b/hw/sh7750_regnames.h
new file mode 100644
index 0000000..7463709
--- /dev/null
+++ b/hw/sh7750_regnames.h
@@ -0,0 +1,6 @@
+#ifndef _SH7750_REGNAMES_H
+#define _SH7750_REGNAMES_H
+
+const char *regname(uint32_t addr);
+
+#endif /* _SH7750_REGNAMES_H */
diff --git a/hw/sh7750_regs.h b/hw/sh7750_regs.h
new file mode 100644
index 0000000..44ae95b
--- /dev/null
+++ b/hw/sh7750_regs.h
@@ -0,0 +1,1623 @@
+/*
+ * SH-7750 memory-mapped registers
+ * This file based on information provided in the following document:
+ * "Hitachi SuperH (tm) RISC engine. SH7750 Series (SH7750, SH7750S)
+ * Hardware Manual"
+ * Document Number ADE-602-124C, Rev. 4.0, 4/21/00, Hitachi Ltd.
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Alexandra Kossovsky <sasha@oktet.ru>
+ * Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * @(#) sh7750_regs.h,v 1.2.4.1 2003/09/04 18:46:00 joel Exp
+ */
+
+#ifndef __SH7750_REGS_H__
+#define __SH7750_REGS_H__
+
+/*
+ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and
+ * in 0x1f000000 - 0x1fffffff (area 7 address)
+ */
+#define SH7750_P4_BASE 0xff000000 /* Accessable only in
+ priveleged mode */
+#define SH7750_A7_BASE 0x1f000000 /* Accessable only using TLB */
+
+#define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs))
+#define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs))
+
+/*
+ * MMU Registers
+ */
+
+/* Page Table Entry High register - PTEH */
+#define SH7750_PTEH_REGOFS 0x000000 /* offset */
+#define SH7750_PTEH SH7750_P4_REG32(SH7750_PTEH_REGOFS)
+#define SH7750_PTEH_A7 SH7750_A7_REG32(SH7750_PTEH_REGOFS)
+#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */
+#define SH7750_PTEH_VPN_S 10
+#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */
+#define SH7750_PTEH_ASID_S 0
+
+/* Page Table Entry Low register - PTEL */
+#define SH7750_PTEL_REGOFS 0x000004 /* offset */
+#define SH7750_PTEL SH7750_P4_REG32(SH7750_PTEL_REGOFS)
+#define SH7750_PTEL_A7 SH7750_A7_REG32(SH7750_PTEL_REGOFS)
+#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */
+#define SH7750_PTEL_PPN_S 10
+#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */
+#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */
+#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */
+#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */
+#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */
+#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */
+#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */
+#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */
+#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */
+#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */
+#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */
+#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */
+#define SH7750_PTEL_C 0x00000008 /* Cacheability
+ (0 - page not cacheable) */
+#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been
+ performed to a page) */
+#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are
+ shared by processes) */
+#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the
+ cache write mode:
+ 0 - Copy-back mode
+ 1 - Write-through mode */
+
+/* Page Table Entry Assistance register - PTEA */
+#define SH7750_PTEA_REGOFS 0x000034 /* offset */
+#define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS)
+#define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS)
+#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit
+ 0 - use area 5 wait states
+ 1 - use area 6 wait states */
+#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */
+#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */
+#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */
+#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */
+#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */
+#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */
+#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */
+#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */
+#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */
+
+
+/* Translation table base register */
+#define SH7750_TTB_REGOFS 0x000008 /* offset */
+#define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS)
+#define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS)
+
+/* TLB exeption address register - TEA */
+#define SH7750_TEA_REGOFS 0x00000c /* offset */
+#define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS)
+#define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS)
+
+/* MMU control register - MMUCR */
+#define SH7750_MMUCR_REGOFS 0x000010 /* offset */
+#define SH7750_MMUCR SH7750_P4_REG32(SH7750_MMUCR_REGOFS)
+#define SH7750_MMUCR_A7 SH7750_A7_REG32(SH7750_MMUCR_REGOFS)
+#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */
+#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */
+#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */
+#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */
+#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */
+#define SH7750_MMUCR_URC_S 10
+#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */
+#define SH7750_MMUCR_URB_S 18
+#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */
+#define SH7750_MMUCR_LRUI_S 26
+
+
+
+
+/*
+ * Cache registers
+ * IC -- instructions cache
+ * OC -- operand cache
+ */
+
+/* Cache Control Register - CCR */
+#define SH7750_CCR_REGOFS 0x00001c /* offset */
+#define SH7750_CCR SH7750_P4_REG32(SH7750_CCR_REGOFS)
+#define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS)
+
+#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */
+#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit:
+ set it to clear IC */
+#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */
+#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */
+#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit
+ if you set OCE = 0,
+ you should set ORA = 0 */
+#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */
+#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */
+#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */
+#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */
+
+/* Queue address control register 0 - QACR0 */
+#define SH7750_QACR0_REGOFS 0x000038 /* offset */
+#define SH7750_QACR0 SH7750_P4_REG32(SH7750_QACR0_REGOFS)
+#define SH7750_QACR0_A7 SH7750_A7_REG32(SH7750_QACR0_REGOFS)
+
+/* Queue address control register 1 - QACR1 */
+#define SH7750_QACR1_REGOFS 0x00003c /* offset */
+#define SH7750_QACR1 SH7750_P4_REG32(SH7750_QACR1_REGOFS)
+#define SH7750_QACR1_A7 SH7750_A7_REG32(SH7750_QACR1_REGOFS)
+
+
+/*
+ * Exeption-related registers
+ */
+
+/* Immediate data for TRAPA instuction - TRA */
+#define SH7750_TRA_REGOFS 0x000020 /* offset */
+#define SH7750_TRA SH7750_P4_REG32(SH7750_TRA_REGOFS)
+#define SH7750_TRA_A7 SH7750_A7_REG32(SH7750_TRA_REGOFS)
+
+#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */
+#define SH7750_TRA_IMM_S 2
+
+/* Exeption event register - EXPEVT */
+#define SH7750_EXPEVT_REGOFS 0x000024
+#define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS)
+#define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS)
+
+#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */
+#define SH7750_EXPEVT_EX_S 0
+
+/* Interrupt event register */
+#define SH7750_INTEVT_REGOFS 0x000028
+#define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS)
+#define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS)
+#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */
+#define SH7750_INTEVT_EX_S 0
+
+/*
+ * Exception/interrupt codes
+ */
+#define SH7750_EVT_TO_NUM(evt) ((evt) >> 5)
+
+/* Reset exception category */
+#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */
+#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */
+#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */
+
+/* General exception category */
+#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */
+#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */
+#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception /
+ DTLB miss exception (read) */
+#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation /
+ DTLB protection violation (read) */
+#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction
+ exception */
+#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction
+ exception */
+#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */
+#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */
+#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */
+#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */
+#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */
+#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation
+ exception (write) */
+#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */
+#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */
+#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */
+
+/* Interrupt exception category */
+#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */
+#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */
+#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */
+#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */
+#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */
+#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */
+#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */
+#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */
+#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */
+#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */
+#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */
+#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */
+#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */
+#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */
+#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */
+#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */
+
+/* Peripheral Module Interrupts - Timer Unit (TMU) */
+#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */
+#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */
+#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */
+#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */
+
+/* Peripheral Module Interrupts - Real-Time Clock (RTC) */
+#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */
+#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */
+#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */
+
+/* Peripheral Module Interrupts - Serial Communication Interface (SCI) */
+#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */
+#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */
+#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */
+#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */
+
+/* Peripheral Module Interrupts - Watchdog Timer (WDT) */
+#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt
+ (used when WDT operates in
+ interval timer mode) */
+
+/* Peripheral Module Interrupts - Memory Refresh Unit (REF) */
+#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */
+#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow
+ interrupt */
+
+/* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */
+#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */
+
+/* Peripheral Module Interrupts - General-Purpose I/O (GPIO) */
+#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */
+
+/* Peripheral Module Interrupts - DMA Controller (DMAC) */
+#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */
+
+/* Peripheral Module Interrupts - Serial Communication Interface with FIFO */
+/* (SCIF) */
+#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */
+#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or
+ Receive Data ready interrupt */
+#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */
+#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */
+
+/*
+ * Power Management
+ */
+#define SH7750_STBCR_REGOFS 0xC00004 /* offset */
+#define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS)
+#define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS)
+
+#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode:
+ 0 - Transition to SLEEP mode on SLEEP
+ 1 - Transition to STANDBY mode on SLEEP */
+#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in
+ standby mode:
+ 0 - normal state
+ 1 - high-impendance state */
+
+#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */
+#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */
+#define SH7750_STBCR_DMAC_STP SH7750_STBCR_MSTP4
+#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */
+#define SH7750_STBCR_SCIF_STP SH7750_STBCR_MSTP3
+#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */
+#define SH7750_STBCR_TMU_STP SH7750_STBCR_MSTP2
+#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */
+#define SH7750_STBCR_RTC_STP SH7750_STBCR_MSTP1
+#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */
+#define SH7750_STBCR_SCI_STP SH7750_STBCR_MSTP0
+
+#define SH7750_STBCR_STBY 0x80
+
+
+#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */
+#define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS)
+#define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS)
+
+#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode:
+ 0 - transition to sleep or standby mode
+ as it is specified in STBY bit
+ 1 - transition to deep sleep mode on
+ execution of SLEEP instruction */
+#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue
+ in the cache controller */
+#define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6
+#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User
+ Break Controller (UBC) */
+#define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5
+
+/*
+ * Clock Pulse Generator (CPG)
+ */
+#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */
+#define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS)
+#define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS)
+
+#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable
+ 0 - CKIO pin goes to HiZ/pullup
+ 1 - Clock is output from CKIO */
+#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */
+#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */
+
+#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */
+#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */
+#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */
+#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */
+#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */
+#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */
+#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */
+
+#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */
+#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */
+#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */
+#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */
+#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */
+#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */
+#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */
+
+#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency
+ division ratio: */
+#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */
+#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */
+#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */
+#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */
+#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */
+
+/*
+ * Watchdog Timer (WDT)
+ */
+
+/* Watchdog Timer Counter register - WTCNT */
+#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */
+#define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS)
+#define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS)
+#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written,
+ you have to set the upper byte to
+ 0x5A */
+
+/* Watchdog Timer Control/Status register - WTCSR */
+#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */
+#define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS)
+#define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS)
+#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written,
+ you have to set the upper byte to
+ 0xA5 */
+#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */
+#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */
+#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */
+#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */
+#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */
+#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */
+#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */
+#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */
+#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */
+#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */
+#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */
+#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */
+#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */
+#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */
+#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */
+#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */
+#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */
+#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */
+
+/*
+ * Real-Time Clock (RTC)
+ */
+/* 64-Hz Counter Register (byte, read-only) - R64CNT */
+#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */
+#define SH7750_R64CNT SH7750_P4_REG32(SH7750_R64CNT_REGOFS)
+#define SH7750_R64CNT_A7 SH7750_A7_REG32(SH7750_R64CNT_REGOFS)
+
+/* Second Counter Register (byte, BCD-coded) - RSECCNT */
+#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */
+#define SH7750_RSECCNT SH7750_P4_REG32(SH7750_RSECCNT_REGOFS)
+#define SH7750_RSECCNT_A7 SH7750_A7_REG32(SH7750_RSECCNT_REGOFS)
+
+/* Minute Counter Register (byte, BCD-coded) - RMINCNT */
+#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */
+#define SH7750_RMINCNT SH7750_P4_REG32(SH7750_RMINCNT_REGOFS)
+#define SH7750_RMINCNT_A7 SH7750_A7_REG32(SH7750_RMINCNT_REGOFS)
+
+/* Hour Counter Register (byte, BCD-coded) - RHRCNT */
+#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */
+#define SH7750_RHRCNT SH7750_P4_REG32(SH7750_RHRCNT_REGOFS)
+#define SH7750_RHRCNT_A7 SH7750_A7_REG32(SH7750_RHRCNT_REGOFS)
+
+/* Day-of-Week Counter Register (byte) - RWKCNT */
+#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */
+#define SH7750_RWKCNT SH7750_P4_REG32(SH7750_RWKCNT_REGOFS)
+#define SH7750_RWKCNT_A7 SH7750_A7_REG32(SH7750_RWKCNT_REGOFS)
+
+#define SH7750_RWKCNT_SUN 0 /* Sunday */
+#define SH7750_RWKCNT_MON 1 /* Monday */
+#define SH7750_RWKCNT_TUE 2 /* Tuesday */
+#define SH7750_RWKCNT_WED 3 /* Wednesday */
+#define SH7750_RWKCNT_THU 4 /* Thursday */
+#define SH7750_RWKCNT_FRI 5 /* Friday */
+#define SH7750_RWKCNT_SAT 6 /* Saturday */
+
+/* Day Counter Register (byte, BCD-coded) - RDAYCNT */
+#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */
+#define SH7750_RDAYCNT SH7750_P4_REG32(SH7750_RDAYCNT_REGOFS)
+#define SH7750_RDAYCNT_A7 SH7750_A7_REG32(SH7750_RDAYCNT_REGOFS)
+
+/* Month Counter Register (byte, BCD-coded) - RMONCNT */
+#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */
+#define SH7750_RMONCNT SH7750_P4_REG32(SH7750_RMONCNT_REGOFS)
+#define SH7750_RMONCNT_A7 SH7750_A7_REG32(SH7750_RMONCNT_REGOFS)
+
+/* Year Counter Register (half, BCD-coded) - RYRCNT */
+#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */
+#define SH7750_RYRCNT SH7750_P4_REG32(SH7750_RYRCNT_REGOFS)
+#define SH7750_RYRCNT_A7 SH7750_A7_REG32(SH7750_RYRCNT_REGOFS)
+
+/* Second Alarm Register (byte, BCD-coded) - RSECAR */
+#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */
+#define SH7750_RSECAR SH7750_P4_REG32(SH7750_RSECAR_REGOFS)
+#define SH7750_RSECAR_A7 SH7750_A7_REG32(SH7750_RSECAR_REGOFS)
+#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */
+
+/* Minute Alarm Register (byte, BCD-coded) - RMINAR */
+#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */
+#define SH7750_RMINAR SH7750_P4_REG32(SH7750_RMINAR_REGOFS)
+#define SH7750_RMINAR_A7 SH7750_A7_REG32(SH7750_RMINAR_REGOFS)
+#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */
+
+/* Hour Alarm Register (byte, BCD-coded) - RHRAR */
+#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */
+#define SH7750_RHRAR SH7750_P4_REG32(SH7750_RHRAR_REGOFS)
+#define SH7750_RHRAR_A7 SH7750_A7_REG32(SH7750_RHRAR_REGOFS)
+#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */
+
+/* Day-of-Week Alarm Register (byte) - RWKAR */
+#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */
+#define SH7750_RWKAR SH7750_P4_REG32(SH7750_RWKAR_REGOFS)
+#define SH7750_RWKAR_A7 SH7750_A7_REG32(SH7750_RWKAR_REGOFS)
+#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */
+
+#define SH7750_RWKAR_SUN 0 /* Sunday */
+#define SH7750_RWKAR_MON 1 /* Monday */
+#define SH7750_RWKAR_TUE 2 /* Tuesday */
+#define SH7750_RWKAR_WED 3 /* Wednesday */
+#define SH7750_RWKAR_THU 4 /* Thursday */
+#define SH7750_RWKAR_FRI 5 /* Friday */
+#define SH7750_RWKAR_SAT 6 /* Saturday */
+
+/* Day Alarm Register (byte, BCD-coded) - RDAYAR */
+#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */
+#define SH7750_RDAYAR SH7750_P4_REG32(SH7750_RDAYAR_REGOFS)
+#define SH7750_RDAYAR_A7 SH7750_A7_REG32(SH7750_RDAYAR_REGOFS)
+#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */
+
+/* Month Counter Register (byte, BCD-coded) - RMONAR */
+#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */
+#define SH7750_RMONAR SH7750_P4_REG32(SH7750_RMONAR_REGOFS)
+#define SH7750_RMONAR_A7 SH7750_A7_REG32(SH7750_RMONAR_REGOFS)
+#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */
+
+/* RTC Control Register 1 (byte) - RCR1 */
+#define SH7750_RCR1_REGOFS 0xC80038 /* offset */
+#define SH7750_RCR1 SH7750_P4_REG32(SH7750_RCR1_REGOFS)
+#define SH7750_RCR1_A7 SH7750_A7_REG32(SH7750_RCR1_REGOFS)
+#define SH7750_RCR1_CF 0x80 /* Carry Flag */
+#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define SH7750_RCR1_AF 0x01 /* Alarm Flag */
+
+/* RTC Control Register 2 (byte) - RCR2 */
+#define SH7750_RCR2_REGOFS 0xC8003C /* offset */
+#define SH7750_RCR2 SH7750_P4_REG32(SH7750_RCR2_REGOFS)
+#define SH7750_RCR2_A7 SH7750_A7_REG32(SH7750_RCR2_REGOFS)
+#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */
+#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */
+#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */
+#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */
+#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */
+#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */
+#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */
+#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */
+#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */
+#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */
+#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */
+#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */
+#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */
+#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month,
+ year counters are stopped
+ 1 - sec, min, hr, day-of-week, month,
+ year counters operate normally */
+
+
+/*
+ * Timer Unit (TMU)
+ */
+/* Timer Output Control Register (byte) - TOCR */
+#define SH7750_TOCR_REGOFS 0xD80000 /* offset */
+#define SH7750_TOCR SH7750_P4_REG32(SH7750_TOCR_REGOFS)
+#define SH7750_TOCR_A7 SH7750_A7_REG32(SH7750_TOCR_REGOFS)
+#define SH7750_TOCR_TCOE 0x01 /* Timer Clock Pin Control:
+ 0 - TCLK is used as external clock
+ input or input capture control
+ 1 - TCLK is used as on-chip RTC
+ output clock pin */
+
+/* Timer Start Register (byte) - TSTR */
+#define SH7750_TSTR_REGOFS 0xD80004 /* offset */
+#define SH7750_TSTR SH7750_P4_REG32(SH7750_TSTR_REGOFS)
+#define SH7750_TSTR_A7 SH7750_A7_REG32(SH7750_TSTR_REGOFS)
+#define SH7750_TSTR_STR2 0x04 /* TCNT2 performs count operations */
+#define SH7750_TSTR_STR1 0x02 /* TCNT1 performs count operations */
+#define SH7750_TSTR_STR0 0x01 /* TCNT0 performs count operations */
+#define SH7750_TSTR_STR(n) (1 << (n))
+
+/* Timer Constant Register - TCOR0, TCOR1, TCOR2 */
+#define SH7750_TCOR_REGOFS(n) (0xD80008 + ((n)*12)) /* offset */
+#define SH7750_TCOR(n) SH7750_P4_REG32(SH7750_TCOR_REGOFS(n))
+#define SH7750_TCOR_A7(n) SH7750_A7_REG32(SH7750_TCOR_REGOFS(n))
+#define SH7750_TCOR0 SH7750_TCOR(0)
+#define SH7750_TCOR1 SH7750_TCOR(1)
+#define SH7750_TCOR2 SH7750_TCOR(2)
+#define SH7750_TCOR0_A7 SH7750_TCOR_A7(0)
+#define SH7750_TCOR1_A7 SH7750_TCOR_A7(1)
+#define SH7750_TCOR2_A7 SH7750_TCOR_A7(2)
+
+/* Timer Counter Register - TCNT0, TCNT1, TCNT2 */
+#define SH7750_TCNT_REGOFS(n) (0xD8000C + ((n)*12)) /* offset */
+#define SH7750_TCNT(n) SH7750_P4_REG32(SH7750_TCNT_REGOFS(n))
+#define SH7750_TCNT_A7(n) SH7750_A7_REG32(SH7750_TCNT_REGOFS(n))
+#define SH7750_TCNT0 SH7750_TCNT(0)
+#define SH7750_TCNT1 SH7750_TCNT(1)
+#define SH7750_TCNT2 SH7750_TCNT(2)
+#define SH7750_TCNT0_A7 SH7750_TCNT_A7(0)
+#define SH7750_TCNT1_A7 SH7750_TCNT_A7(1)
+#define SH7750_TCNT2_A7 SH7750_TCNT_A7(2)
+
+/* Timer Control Register (half) - TCR0, TCR1, TCR2 */
+#define SH7750_TCR_REGOFS(n) (0xD80010 + ((n)*12)) /* offset */
+#define SH7750_TCR(n) SH7750_P4_REG32(SH7750_TCR_REGOFS(n))
+#define SH7750_TCR_A7(n) SH7750_A7_REG32(SH7750_TCR_REGOFS(n))
+#define SH7750_TCR0 SH7750_TCR(0)
+#define SH7750_TCR1 SH7750_TCR(1)
+#define SH7750_TCR2 SH7750_TCR(2)
+#define SH7750_TCR0_A7 SH7750_TCR_A7(0)
+#define SH7750_TCR1_A7 SH7750_TCR_A7(1)
+#define SH7750_TCR2_A7 SH7750_TCR_A7(2)
+
+#define SH7750_TCR2_ICPF 0x200 /* Input Capture Interrupt Flag
+ (1 - input capture has occured) */
+#define SH7750_TCR_UNF 0x100 /* Underflow flag */
+#define SH7750_TCR2_ICPE 0x0C0 /* Input Capture Control: */
+#define SH7750_TCR2_ICPE_DIS 0x000 /* Input Capture function is not used */
+#define SH7750_TCR2_ICPE_NOINT 0x080 /* Input Capture function is used, but
+ input capture interrupt is not
+ enabled */
+#define SH7750_TCR2_ICPE_INT 0x0C0 /* Input Capture function is used,
+ input capture interrupt enabled */
+#define SH7750_TCR_UNIE 0x020 /* Underflow Interrupt Control
+ (1 - underflow interrupt enabled) */
+#define SH7750_TCR_CKEG 0x018 /* Clock Edge selection: */
+#define SH7750_TCR_CKEG_RAISE 0x000 /* Count/capture on rising edge */
+#define SH7750_TCR_CKEG_FALL 0x008 /* Count/capture on falling edge */
+#define SH7750_TCR_CKEG_BOTH 0x018 /* Count/capture on both rising and
+ falling edges */
+#define SH7750_TCR_TPSC 0x007 /* Timer prescaler */
+#define SH7750_TCR_TPSC_DIV4 0x000 /* Counts on peripheral clock/4 */
+#define SH7750_TCR_TPSC_DIV16 0x001 /* Counts on peripheral clock/16 */
+#define SH7750_TCR_TPSC_DIV64 0x002 /* Counts on peripheral clock/64 */
+#define SH7750_TCR_TPSC_DIV256 0x003 /* Counts on peripheral clock/256 */
+#define SH7750_TCR_TPSC_DIV1024 0x004 /* Counts on peripheral clock/1024 */
+#define SH7750_TCR_TPSC_RTC 0x006 /* Counts on on-chip RTC output clk */
+#define SH7750_TCR_TPSC_EXT 0x007 /* Counts on external clock */
+
+/* Input Capture Register (read-only) - TCPR2 */
+#define SH7750_TCPR2_REGOFS 0xD8002C /* offset */
+#define SH7750_TCPR2 SH7750_P4_REG32(SH7750_TCPR2_REGOFS)
+#define SH7750_TCPR2_A7 SH7750_A7_REG32(SH7750_TCPR2_REGOFS)
+
+/*
+ * Bus State Controller - BSC
+ */
+/* Bus Control Register 1 - BCR1 */
+#define SH7750_BCR1_REGOFS 0x800000 /* offset */
+#define SH7750_BCR1 SH7750_P4_REG32(SH7750_BCR1_REGOFS)
+#define SH7750_BCR1_A7 SH7750_A7_REG32(SH7750_BCR1_REGOFS)
+#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */
+#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */
+#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */
+#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control:
+ 0 - pull-up resistor is on for
+ control input pins
+ 1 - pull-up resistor is off */
+#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control:
+ 0 - pull-up resistor is on for
+ control output pins
+ 1 - pull-up resistor is off */
+#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode:
+ 0 - Area 1 SRAM is set to
+ normal mode
+ 1 - Area 1 SRAM is set to byte
+ control mode */
+#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode:
+ 0 - Area 4 SRAM is set to
+ normal mode
+ 1 - Area 4 SRAM is set to byte
+ control mode */
+#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable:
+ 0 - External requests are not
+ accepted
+ 1 - External requests are
+ accepted */
+#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit:
+ 0 - Master Mode
+ 1 - Partial-sharing Mode */
+#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface:
+ 0 - SRAM/burst ROM interface
+ 1 - MPX interface */
+#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies
+ the state of A[25:0], BS\, CSn\,
+ RD/WR\, CE2A\, CE2B\ in standby
+ mode and when bus is released:
+ 0 - signals go to High-Z mode
+ 1 - signals driven */
+#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies
+ the state of the RAS\, RAS2\, WEn\,
+ CASn\, DQMn, RD\, CASS\, FRAME\,
+ RD2\ signals in standby mode and
+ when bus is released:
+ 0 - signals go to High-Z mode
+ 1 - signals driven */
+#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */
+#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */
+#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */
+#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */
+#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */
+#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */
+#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */
+#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX
+ interface. */
+#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 -
+ synchronous DRAM */
+#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous
+ DRAM interface */
+#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 -
+ DRAM interface */
+#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM
+ interface */
+
+#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type:
+ 0 - SRAM interface
+ 1 - PCMCIA interface */
+
+/* Bus Control Register 2 (half) - BCR2 */
+#define SH7750_BCR2_REGOFS 0x800004 /* offset */
+#define SH7750_BCR2 SH7750_P4_REG32(SH7750_BCR2_REGOFS)
+#define SH7750_BCR2_A7 SH7750_A7_REG32(SH7750_BCR2_REGOFS)
+
+#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */
+#define SH7750_BCR2_A0SZ_S 14
+#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */
+#define SH7750_BCR2_A6SZ_S 12
+#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */
+#define SH7750_BCR2_A5SZ_S 10
+#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */
+#define SH7750_BCR2_A4SZ_S 8
+#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */
+#define SH7750_BCR2_A3SZ_S 6
+#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */
+#define SH7750_BCR2_A2SZ_S 4
+#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */
+#define SH7750_BCR2_A1SZ_S 2
+#define SH7750_BCR2_SZ_64 0 /* 64 bits */
+#define SH7750_BCR2_SZ_8 1 /* 8 bits */
+#define SH7750_BCR2_SZ_16 2 /* 16 bits */
+#define SH7750_BCR2_SZ_32 3 /* 32 bits */
+#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable :
+ 0 - D51-D32 are not used as a port
+ 1 - D51-D32 are used as a port */
+
+/* Wait Control Register 1 - WCR1 */
+#define SH7750_WCR1_REGOFS 0x800008 /* offset */
+#define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS)
+#define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS)
+#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle
+ specification */
+#define SH7750_WCR1_DMAIW_S 28
+#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A6IW_S 24
+#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A5IW_S 20
+#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A4IW_S 16
+#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A3IW_S 12
+#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A2IW_S 8
+#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A1IW_S 4
+#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A0IW_S 0
+
+/* Wait Control Register 2 - WCR2 */
+#define SH7750_WCR2_REGOFS 0x80000C /* offset */
+#define SH7750_WCR2 SH7750_P4_REG32(SH7750_WCR2_REGOFS)
+#define SH7750_WCR2_A7 SH7750_A7_REG32(SH7750_WCR2_REGOFS)
+
+#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */
+#define SH7750_WCR2_A6W_S 29
+#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */
+#define SH7750_WCR2_A6B_S 26
+#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */
+#define SH7750_WCR2_A5W_S 23
+#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */
+#define SH7750_WCR2_A5B_S 20
+#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */
+#define SH7750_WCR2_A4W_S 17
+#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */
+#define SH7750_WCR2_A3W_S 13
+#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */
+#define SH7750_WCR2_A2W_S 9
+#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */
+#define SH7750_WCR2_A1W_S 6
+#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */
+#define SH7750_WCR2_A0W_S 3
+#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */
+#define SH7750_WCR2_A0B_S 0
+
+#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */
+#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */
+#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */
+#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */
+#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */
+#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */
+#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */
+#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */
+
+#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */
+
+/* DRAM CAS\ Assertion Delay (area 3,2) */
+#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */
+#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */
+
+/* SDRAM CAS\ Latency Cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */
+#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */
+
+/* Wait Control Register 3 - WCR3 */
+#define SH7750_WCR3_REGOFS 0x800010 /* offset */
+#define SH7750_WCR3 SH7750_P4_REG32(SH7750_WCR3_REGOFS)
+#define SH7750_WCR3_A7 SH7750_A7_REG32(SH7750_WCR3_REGOFS)
+
+#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */
+#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */
+#define SH7750_WCR3_A6H_S 24
+#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */
+#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */
+#define SH7750_WCR3_A5H_S 20
+#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */
+#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */
+#define SH7750_WCR3_A4H_S 16
+#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */
+#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */
+#define SH7750_WCR3_A3H_S 12
+#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */
+#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */
+#define SH7750_WCR3_A2H_S 8
+#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */
+#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */
+#define SH7750_WCR3_A1H_S 4
+#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */
+#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */
+#define SH7750_WCR3_A0H_S 0
+
+#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */
+#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */
+#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */
+#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */
+
+#define SH7750_MCR_REGOFS 0x800014 /* offset */
+#define SH7750_MCR SH7750_P4_REG32(SH7750_MCR_REGOFS)
+#define SH7750_MCR_A7 SH7750_A7_REG32(SH7750_MCR_REGOFS)
+
+#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */
+#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */
+#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */
+#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of
+ Refresh: */
+#define SH7750_MCR_TRC_0 0x00000000 /* 0 */
+#define SH7750_MCR_TRC_3 0x08000000 /* 3 */
+#define SH7750_MCR_TRC_6 0x10000000 /* 6 */
+#define SH7750_MCR_TRC_9 0x18000000 /* 9 */
+#define SH7750_MCR_TRC_12 0x20000000 /* 12 */
+#define SH7750_MCR_TRC_15 0x28000000 /* 15 */
+#define SH7750_MCR_TRC_18 0x30000000 /* 18 */
+#define SH7750_MCR_TRC_21 0x38000000 /* 21 */
+
+#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */
+#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */
+#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */
+
+#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period
+ SDRAM: minimum number of cycles
+ until the next bank active cmd
+ is output after precharging */
+#define SH7750_MCR_TPC_S 19
+#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */
+#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */
+#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */
+#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */
+#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */
+#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */
+#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */
+#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */
+
+#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time
+ SDRAM: bank active-read/write cmd
+ delay time */
+#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */
+#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */
+#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */
+#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */
+#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */
+#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */
+#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */
+
+#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */
+#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */
+#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */
+#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */
+#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */
+#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */
+
+#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS
+ asserting period
+ SDRAM: Command interval after
+ synchronous DRAM refresh */
+#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */
+#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */
+#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */
+#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */
+#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */
+#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */
+#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */
+#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */
+
+#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */
+
+#define SH7750_MCR_BE 0x00000200 /* Burst Enable */
+#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */
+#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */
+#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */
+#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */
+
+#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */
+#define SH7750_MCR_AMX_S 3
+#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */
+#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */
+#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */
+#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */
+#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */
+/* See SH7750 Hardware Manual for SDRAM address multiplexor selection */
+
+#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */
+#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */
+#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */
+#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */
+#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */
+
+/* SDRAM Mode Set address */
+#define SH7750_SDRAM_MODE_A2_BASE 0xFF900000
+#define SH7750_SDRAM_MODE_A3_BASE 0xFF940000
+#define SH7750_SDRAM_MODE_A2_32BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 2))
+#define SH7750_SDRAM_MODE_A3_32BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 2))
+#define SH7750_SDRAM_MODE_A2_64BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 3))
+#define SH7750_SDRAM_MODE_A3_64BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 3))
+
+
+/* PCMCIA Control Register (half) - PCR */
+#define SH7750_PCR_REGOFS 0x800018 /* offset */
+#define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS)
+#define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS)
+
+#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait
+ states to be added to the number of
+ waits specified by WCR2 in a low-speed
+ PCMCIA wait cycle */
+#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */
+#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */
+#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */
+#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */
+
+#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait
+ states to be added to the number of
+ waits specified by WCR2 in a low-speed
+ PCMCIA wait cycle */
+#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */
+#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */
+#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */
+#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */
+
+#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay,
+ delay time from address output to
+ OE\/WE\ assertion on the connected
+ PCMCIA interface */
+#define SH7750_PCR_A5TED_S 9
+#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */
+#define SH7750_PCR_A6TED_S 6
+
+#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */
+#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */
+#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */
+#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */
+#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */
+#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */
+#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */
+#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */
+
+#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay,
+ address hold delay time from OE\/WE\
+ negation in a write on the connected
+ PCMCIA interface */
+#define SH7750_PCR_A5TEH_S 3
+
+#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */
+#define SH7750_PCR_A6TEH_S 0
+
+#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */
+#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */
+#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */
+#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */
+#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */
+#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */
+#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */
+#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */
+
+/* Refresh Timer Control/Status Register (half) - RTSCR */
+#define SH7750_RTCSR_REGOFS 0x80001C /* offset */
+#define SH7750_RTCSR SH7750_P4_REG32(SH7750_RTCSR_REGOFS)
+#define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS)
+
+#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */
+#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a
+ match between the refresh timer
+ counter and refresh time constant) */
+#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */
+#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */
+#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */
+#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */
+#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */
+#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */
+#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */
+#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */
+#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */
+#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */
+
+#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */
+#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt
+ Enable */
+#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */
+#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */
+#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */
+
+/* Refresh Timer Counter (half) - RTCNT */
+#define SH7750_RTCNT_REGOFS 0x800020 /* offset */
+#define SH7750_RTCNT SH7750_P4_REG32(SH7750_RTCNT_REGOFS)
+#define SH7750_RTCNT_A7 SH7750_A7_REG32(SH7750_RTCNT_REGOFS)
+
+#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */
+
+/* Refresh Time Constant Register (half) - RTCOR */
+#define SH7750_RTCOR_REGOFS 0x800024 /* offset */
+#define SH7750_RTCOR SH7750_P4_REG32(SH7750_RTCOR_REGOFS)
+#define SH7750_RTCOR_A7 SH7750_A7_REG32(SH7750_RTCOR_REGOFS)
+
+#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */
+
+/* Refresh Count Register (half) - RFCR */
+#define SH7750_RFCR_REGOFS 0x800028 /* offset */
+#define SH7750_RFCR SH7750_P4_REG32(SH7750_RFCR_REGOFS)
+#define SH7750_RFCR_A7 SH7750_A7_REG32(SH7750_RFCR_REGOFS)
+
+#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */
+
+/*
+ * Direct Memory Access Controller (DMAC)
+ */
+
+/* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */
+#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */
+#define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n))
+#define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n))
+#define SH7750_SAR0 SH7750_SAR(0)
+#define SH7750_SAR1 SH7750_SAR(1)
+#define SH7750_SAR2 SH7750_SAR(2)
+#define SH7750_SAR3 SH7750_SAR(3)
+#define SH7750_SAR0_A7 SH7750_SAR_A7(0)
+#define SH7750_SAR1_A7 SH7750_SAR_A7(1)
+#define SH7750_SAR2_A7 SH7750_SAR_A7(2)
+#define SH7750_SAR3_A7 SH7750_SAR_A7(3)
+
+/* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */
+#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */
+#define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n))
+#define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n))
+#define SH7750_DAR0 SH7750_DAR(0)
+#define SH7750_DAR1 SH7750_DAR(1)
+#define SH7750_DAR2 SH7750_DAR(2)
+#define SH7750_DAR3 SH7750_DAR(3)
+#define SH7750_DAR0_A7 SH7750_DAR_A7(0)
+#define SH7750_DAR1_A7 SH7750_DAR_A7(1)
+#define SH7750_DAR2_A7 SH7750_DAR_A7(2)
+#define SH7750_DAR3_A7 SH7750_DAR_A7(3)
+
+/* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */
+#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */
+#define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n))
+#define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n))
+#define SH7750_DMATCR0_P4 SH7750_DMATCR(0)
+#define SH7750_DMATCR1_P4 SH7750_DMATCR(1)
+#define SH7750_DMATCR2_P4 SH7750_DMATCR(2)
+#define SH7750_DMATCR3_P4 SH7750_DMATCR(3)
+#define SH7750_DMATCR0_A7 SH7750_DMATCR_A7(0)
+#define SH7750_DMATCR1_A7 SH7750_DMATCR_A7(1)
+#define SH7750_DMATCR2_A7 SH7750_DMATCR_A7(2)
+#define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3)
+
+/* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */
+#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */
+#define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n))
+#define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n))
+#define SH7750_CHCR0 SH7750_CHCR(0)
+#define SH7750_CHCR1 SH7750_CHCR(1)
+#define SH7750_CHCR2 SH7750_CHCR(2)
+#define SH7750_CHCR3 SH7750_CHCR(3)
+#define SH7750_CHCR0_A7 SH7750_CHCR_A7(0)
+#define SH7750_CHCR1_A7 SH7750_CHCR_A7(1)
+#define SH7750_CHCR2_A7 SH7750_CHCR_A7(2)
+#define SH7750_CHCR3_A7 SH7750_CHCR_A7(3)
+
+#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */
+#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */
+#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */
+#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */
+#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */
+#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */
+#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */
+#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */
+#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */
+
+#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select,
+ specifies CS5 or CS6 space wait
+ control for PCMCIA access */
+
+#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */
+#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */
+#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */
+#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */
+#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */
+#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */
+#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */
+#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */
+#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */
+
+#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control
+ Select, specifies CS5 or CS6
+ space wait control for PCMCIA
+ access */
+
+#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */
+#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */
+#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */
+
+#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */
+#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */
+#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */
+
+#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */
+#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */
+#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */
+
+#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */
+#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */
+#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */
+
+#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */
+#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */
+#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */
+#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */
+
+#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */
+#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */
+#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */
+#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */
+
+#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */
+#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address
+ Mode (External Addr Space->
+ External Addr Space) */
+#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single
+ Address Mode (External Addr
+ Space -> External Device) */
+#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single
+ Address Mode, (External
+ Device -> External Addr
+ Space) */
+#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr
+ Space -> External Addr Space) */
+
+#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr
+ Space -> On-chip Peripheral
+ Module) */
+#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip
+ Peripheral Module ->
+ External Addr Space */
+#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr
+ transfer request (external
+ address space -> SCTDR1) */
+#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr
+ transfer request (SCRDR1 ->
+ External Addr Space) */
+#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr
+ transfer request (external
+ address space -> SCFTDR1) */
+#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr
+ transfer request (SCFRDR2 ->
+ External Addr Space) */
+#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture
+ interrupt), (external address
+ space -> external address
+ space) */
+#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture
+ interrupt), (external address
+ space -> on-chip peripheral
+ module) */
+#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture
+ interrupt), (on-chip
+ peripheral module -> external
+ address space) */
+
+#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */
+#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */
+#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */
+
+#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */
+#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */
+#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */
+#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */
+#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */
+#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */
+
+#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */
+#define SH7750_CHCR_TE 0x00000002 /* Transfer End */
+#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */
+
+/* DMA Operation Register - DMAOR */
+#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */
+#define SH7750_DMAOR SH7750_P4_REG32(SH7750_DMAOR_REGOFS)
+#define SH7750_DMAOR_A7 SH7750_A7_REG32(SH7750_DMAOR_REGOFS)
+
+#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */
+
+#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */
+#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */
+#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */
+#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */
+#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */
+
+#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */
+#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */
+#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */
+#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */
+
+/*
+ * Serial Communication Interface - SCI
+ * Serial Communication Interface with FIFO - SCIF
+ */
+/* SCI Receive Data Register (byte, read-only) - SCRDR1, SCFRDR2 */
+#define SH7750_SCRDR_REGOFS(n) ((n) == 1 ? 0xE00014 : 0xE80014) /* offset */
+#define SH7750_SCRDR(n) SH7750_P4_REG32(SH7750_SCRDR_REGOFS(n))
+#define SH7750_SCRDR1 SH7750_SCRDR(1)
+#define SH7750_SCRDR2 SH7750_SCRDR(2)
+#define SH7750_SCRDR_A7(n) SH7750_A7_REG32(SH7750_SCRDR_REGOFS(n))
+#define SH7750_SCRDR1_A7 SH7750_SCRDR_A7(1)
+#define SH7750_SCRDR2_A7 SH7750_SCRDR_A7(2)
+
+/* SCI Transmit Data Register (byte) - SCTDR1, SCFTDR2 */
+#define SH7750_SCTDR_REGOFS(n) ((n) == 1 ? 0xE0000C : 0xE8000C) /* offset */
+#define SH7750_SCTDR(n) SH7750_P4_REG32(SH7750_SCTDR_REGOFS(n))
+#define SH7750_SCTDR1 SH7750_SCTDR(1)
+#define SH7750_SCTDR2 SH7750_SCTDR(2)
+#define SH7750_SCTDR_A7(n) SH7750_A7_REG32(SH7750_SCTDR_REGOFS(n))
+#define SH7750_SCTDR1_A7 SH7750_SCTDR_A7(1)
+#define SH7750_SCTDR2_A7 SH7750_SCTDR_A7(2)
+
+/* SCI Serial Mode Register - SCSMR1(byte), SCSMR2(half) */
+#define SH7750_SCSMR_REGOFS(n) ((n) == 1 ? 0xE00000 : 0xE80000) /* offset */
+#define SH7750_SCSMR(n) SH7750_P4_REG32(SH7750_SCSMR_REGOFS(n))
+#define SH7750_SCSMR1 SH7750_SCSMR(1)
+#define SH7750_SCSMR2 SH7750_SCSMR(2)
+#define SH7750_SCSMR_A7(n) SH7750_A7_REG32(SH7750_SCSMR_REGOFS(n))
+#define SH7750_SCSMR1_A7 SH7750_SCSMR_A7(1)
+#define SH7750_SCSMR2_A7 SH7750_SCSMR_A7(2)
+
+#define SH7750_SCSMR1_CA 0x80 /* Communication Mode (C/A\): */
+#define SH7750_SCSMR1_CA_ASYNC 0x00 /* Asynchronous Mode */
+#define SH7750_SCSMR1_CA_SYNC 0x80 /* Synchronous Mode */
+#define SH7750_SCSMR_CHR 0x40 /* Character Length: */
+#define SH7750_SCSMR_CHR_8 0x00 /* 8-bit data */
+#define SH7750_SCSMR_CHR_7 0x40 /* 7-bit data */
+#define SH7750_SCSMR_PE 0x20 /* Parity Enable */
+#define SH7750_SCSMR_PM 0x10 /* Parity Mode: */
+#define SH7750_SCSMR_PM_EVEN 0x00 /* Even Parity */
+#define SH7750_SCSMR_PM_ODD 0x10 /* Odd Parity */
+#define SH7750_SCSMR_STOP 0x08 /* Stop Bit Length: */
+#define SH7750_SCSMR_STOP_1 0x00 /* 1 stop bit */
+#define SH7750_SCSMR_STOP_2 0x08 /* 2 stop bit */
+#define SH7750_SCSMR1_MP 0x04 /* Multiprocessor Mode */
+#define SH7750_SCSMR_CKS 0x03 /* Clock Select */
+#define SH7750_SCSMR_CKS_S 0
+#define SH7750_SCSMR_CKS_DIV1 0x00 /* Periph clock */
+#define SH7750_SCSMR_CKS_DIV4 0x01 /* Periph clock / 4 */
+#define SH7750_SCSMR_CKS_DIV16 0x02 /* Periph clock / 16 */
+#define SH7750_SCSMR_CKS_DIV64 0x03 /* Periph clock / 64 */
+
+/* SCI Serial Control Register - SCSCR1(byte), SCSCR2(half) */
+#define SH7750_SCSCR_REGOFS(n) ((n) == 1 ? 0xE00008 : 0xE80008) /* offset */
+#define SH7750_SCSCR(n) SH7750_P4_REG32(SH7750_SCSCR_REGOFS(n))
+#define SH7750_SCSCR1 SH7750_SCSCR(1)
+#define SH7750_SCSCR2 SH7750_SCSCR(2)
+#define SH7750_SCSCR_A7(n) SH7750_A7_REG32(SH7750_SCSCR_REGOFS(n))
+#define SH7750_SCSCR1_A7 SH7750_SCSCR_A7(1)
+#define SH7750_SCSCR2_A7 SH7750_SCSCR_A7(2)
+
+#define SH7750_SCSCR_TIE 0x80 /* Transmit Interrupt Enable */
+#define SH7750_SCSCR_RIE 0x40 /* Receive Interrupt Enable */
+#define SH7750_SCSCR_TE 0x20 /* Transmit Enable */
+#define SH7750_SCSCR_RE 0x10 /* Receive Enable */
+#define SH7750_SCSCR1_MPIE 0x08 /* Multiprocessor Interrupt Enable */
+#define SH7750_SCSCR2_REIE 0x08 /* Receive Error Interrupt Enable */
+#define SH7750_SCSCR1_TEIE 0x04 /* Transmit End Interrupt Enable */
+#define SH7750_SCSCR1_CKE 0x03 /* Clock Enable: */
+#define SH7750_SCSCR_CKE_INTCLK 0x00 /* Use Internal Clock */
+#define SH7750_SCSCR_CKE_EXTCLK 0x02 /* Use External Clock from SCK */
+#define SH7750_SCSCR1_CKE_ASYNC_SCK_CLKOUT 0x01 /* Use SCK as a clock output
+ in asynchronous mode */
+
+/* SCI Serial Status Register - SCSSR1(byte), SCSFR2(half) */
+#define SH7750_SCSSR_REGOFS(n) ((n) == 1 ? 0xE00010 : 0xE80010) /* offset */
+#define SH7750_SCSSR(n) SH7750_P4_REG32(SH7750_SCSSR_REGOFS(n))
+#define SH7750_SCSSR1 SH7750_SCSSR(1)
+#define SH7750_SCSFR2 SH7750_SCSSR(2)
+#define SH7750_SCSSR_A7(n) SH7750_A7_REG32(SH7750_SCSSR_REGOFS(n))
+#define SH7750_SCSSR1_A7 SH7750_SCSSR_A7(1)
+#define SH7750_SCSFR2_A7 SH7750_SCSSR_A7(2)
+
+#define SH7750_SCSSR1_TDRE 0x80 /* Transmit Data Register Empty */
+#define SH7750_SCSSR1_RDRF 0x40 /* Receive Data Register Full */
+#define SH7750_SCSSR1_ORER 0x20 /* Overrun Error */
+#define SH7750_SCSSR1_FER 0x10 /* Framing Error */
+#define SH7750_SCSSR1_PER 0x08 /* Parity Error */
+#define SH7750_SCSSR1_TEND 0x04 /* Transmit End */
+#define SH7750_SCSSR1_MPB 0x02 /* Multiprocessor Bit */
+#define SH7750_SCSSR1_MPBT 0x01 /* Multiprocessor Bit Transfer */
+
+#define SH7750_SCFSR2_PERN 0xF000 /* Number of Parity Errors */
+#define SH7750_SCFSR2_PERN_S 12
+#define SH7750_SCFSR2_FERN 0x0F00 /* Number of Framing Errors */
+#define SH7750_SCFSR2_FERN_S 8
+#define SH7750_SCFSR2_ER 0x0080 /* Receive Error */
+#define SH7750_SCFSR2_TEND 0x0040 /* Transmit End */
+#define SH7750_SCFSR2_TDFE 0x0020 /* Transmit FIFO Data Empty */
+#define SH7750_SCFSR2_BRK 0x0010 /* Break Detect */
+#define SH7750_SCFSR2_FER 0x0008 /* Framing Error */
+#define SH7750_SCFSR2_PER 0x0004 /* Parity Error */
+#define SH7750_SCFSR2_RDF 0x0002 /* Receive FIFO Data Full */
+#define SH7750_SCFSR2_DR 0x0001 /* Receive Data Ready */
+
+/* SCI Serial Port Register - SCSPTR1(byte) */
+#define SH7750_SCSPTR1_REGOFS 0xE0001C /* offset */
+#define SH7750_SCSPTR1 SH7750_P4_REG32(SH7750_SCSPTR1_REGOFS)
+#define SH7750_SCSPTR1_A7 SH7750_A7_REG32(SH7750_SCSPTR1_REGOFS)
+
+#define SH7750_SCSPTR1_EIO 0x80 /* Error Interrupt Only */
+#define SH7750_SCSPTR1_SPB1IO 0x08 /* 1: Output SPB1DT bit to SCK pin */
+#define SH7750_SCSPTR1_SPB1DT 0x04 /* Serial Port Clock Port Data */
+#define SH7750_SCSPTR1_SPB0IO 0x02 /* 1: Output SPB0DT bit to TxD pin */
+#define SH7750_SCSPTR1_SPB0DT 0x01 /* Serial Port Break Data */
+
+/* SCIF Serial Port Register - SCSPTR2(half) */
+#define SH7750_SCSPTR2_REGOFS 0xE80020 /* offset */
+#define SH7750_SCSPTR2 SH7750_P4_REG32(SH7750_SCSPTR2_REGOFS)
+#define SH7750_SCSPTR2_A7 SH7750_A7_REG32(SH7750_SCSPTR2_REGOFS)
+
+#define SH7750_SCSPTR2_RTSIO 0x80 /* 1: Output RTSDT bit to RTS2\ pin */
+#define SH7750_SCSPTR2_RTSDT 0x40 /* RTS Port Data */
+#define SH7750_SCSPTR2_CTSIO 0x20 /* 1: Output CTSDT bit to CTS2\ pin */
+#define SH7750_SCSPTR2_CTSDT 0x10 /* CTS Port Data */
+#define SH7750_SCSPTR2_SPB2IO 0x02 /* 1: Output SPBDT bit to TxD2 pin */
+#define SH7750_SCSPTR2_SPB2DT 0x01 /* Serial Port Break Data */
+
+/* SCI Bit Rate Register - SCBRR1(byte), SCBRR2(byte) */
+#define SH7750_SCBRR_REGOFS(n) ((n) == 1 ? 0xE00004 : 0xE80004) /* offset */
+#define SH7750_SCBRR(n) SH7750_P4_REG32(SH7750_SCBRR_REGOFS(n))
+#define SH7750_SCBRR1 SH7750_SCBRR_P4(1)
+#define SH7750_SCBRR2 SH7750_SCBRR_P4(2)
+#define SH7750_SCBRR_A7(n) SH7750_A7_REG32(SH7750_SCBRR_REGOFS(n))
+#define SH7750_SCBRR1_A7 SH7750_SCBRR_A7(1)
+#define SH7750_SCBRR2_A7 SH7750_SCBRR_A7(2)
+
+/* SCIF FIFO Control Register - SCFCR2(half) */
+#define SH7750_SCFCR2_REGOFS 0xE80018 /* offset */
+#define SH7750_SCFCR2 SH7750_P4_REG32(SH7750_SCFCR2_REGOFS)
+#define SH7750_SCFCR2_A7 SH7750_A7_REG32(SH7750_SCFCR2_REGOFS)
+
+#define SH7750_SCFCR2_RSTRG 0x700 /* RTS2\ Output Active Trigger; RTS2\
+ signal goes to high level when the
+ number of received data stored in
+ FIFO exceeds the trigger number */
+#define SH7750_SCFCR2_RSTRG_15 0x000 /* 15 bytes */
+#define SH7750_SCFCR2_RSTRG_1 0x000 /* 1 byte */
+#define SH7750_SCFCR2_RSTRG_4 0x000 /* 4 bytes */
+#define SH7750_SCFCR2_RSTRG_6 0x000 /* 6 bytes */
+#define SH7750_SCFCR2_RSTRG_8 0x000 /* 8 bytes */
+#define SH7750_SCFCR2_RSTRG_10 0x000 /* 10 bytes */
+#define SH7750_SCFCR2_RSTRG_14 0x000 /* 14 bytes */
+
+#define SH7750_SCFCR2_RTRG 0x0C0 /* Receive FIFO Data Number Trigger,
+ Receive Data Full (RDF) Flag sets
+ when number of receive data bytes is
+ equal or greater than the trigger
+ number */
+#define SH7750_SCFCR2_RTRG_1 0x000 /* 1 byte */
+#define SH7750_SCFCR2_RTRG_4 0x040 /* 4 bytes */
+#define SH7750_SCFCR2_RTRG_8 0x080 /* 8 bytes */
+#define SH7750_SCFCR2_RTRG_14 0x0C0 /* 14 bytes */
+
+#define SH7750_SCFCR2_TTRG 0x030 /* Transmit FIFO Data Number Trigger,
+ Transmit FIFO Data Register Empty (TDFE)
+ flag sets when the number of remaining
+ transmit data bytes is equal or less
+ than the trigger number */
+#define SH7750_SCFCR2_TTRG_8 0x000 /* 8 bytes */
+#define SH7750_SCFCR2_TTRG_4 0x010 /* 4 bytes */
+#define SH7750_SCFCR2_TTRG_2 0x020 /* 2 bytes */
+#define SH7750_SCFCR2_TTRG_1 0x030 /* 1 byte */
+
+#define SH7750_SCFCR2_MCE 0x008 /* Modem Control Enable */
+#define SH7750_SCFCR2_TFRST 0x004 /* Transmit FIFO Data Register Reset,
+ invalidates the transmit data in the
+ transmit FIFO */
+#define SH7750_SCFCR2_RFRST 0x002 /* Receive FIFO Data Register Reset,
+ invalidates the receive data in the
+ receive FIFO data register and resets
+ it to the empty state */
+#define SH7750_SCFCR2_LOOP 0x001 /* Loopback Test */
+
+/* SCIF FIFO Data Count Register - SCFDR2(half, read-only) */
+#define SH7750_SCFDR2_REGOFS 0xE8001C /* offset */
+#define SH7750_SCFDR2 SH7750_P4_REG32(SH7750_SCFDR2_REGOFS)
+#define SH7750_SCFDR2_A7 SH7750_A7_REG32(SH7750_SCFDR2_REGOFS)
+
+#define SH7750_SCFDR2_T 0x1F00 /* Number of untransmitted data bytes
+ in transmit FIFO */
+#define SH7750_SCFDR2_T_S 8
+#define SH7750_SCFDR2_R 0x001F /* Number of received data bytes in
+ receive FIFO */
+#define SH7750_SCFDR2_R_S 0
+
+/* SCIF Line Status Register - SCLSR2(half, read-only) */
+#define SH7750_SCLSR2_REGOFS 0xE80024 /* offset */
+#define SH7750_SCLSR2 SH7750_P4_REG32(SH7750_SCLSR2_REGOFS)
+#define SH7750_SCLSR2_A7 SH7750_A7_REG32(SH7750_SCLSR2_REGOFS)
+
+#define SH7750_SCLSR2_ORER 0x0001 /* Overrun Error */
+
+/*
+ * SCI-based Smart Card Interface
+ */
+/* Smart Card Mode Register - SCSCMR1(byte) */
+#define SH7750_SCSCMR1_REGOFS 0xE00018 /* offset */
+#define SH7750_SCSCMR1 SH7750_P4_REG32(SH7750_SCSCMR1_REGOFS)
+#define SH7750_SCSCMR1_A7 SH7750_A7_REG32(SH7750_SCSCMR1_REGOFS)
+
+#define SH7750_SCSCMR1_SDIR 0x08 /* Smart Card Data Transfer Direction: */
+#define SH7750_SCSCMR1_SDIR_LSBF 0x00 /* LSB-first */
+#define SH7750_SCSCMR1_SDIR_MSBF 0x08 /* MSB-first */
+
+#define SH7750_SCSCMR1_SINV 0x04 /* Smart Card Data Inversion */
+#define SH7750_SCSCMR1_SMIF 0x01 /* Smart Card Interface Mode Select */
+
+/* Smart-card specific bits in other registers */
+/* SCSMR1: */
+#define SH7750_SCSMR1_GSM 0x80 /* GSM mode select */
+
+/* SCSSR1: */
+#define SH7750_SCSSR1_ERS 0x10 /* Error Signal Status */
+
+/*
+ * I/O Ports
+ */
+/* Port Control Register A - PCTRA */
+#define SH7750_PCTRA_REGOFS 0x80002C /* offset */
+#define SH7750_PCTRA SH7750_P4_REG32(SH7750_PCTRA_REGOFS)
+#define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS)
+
+#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */
+#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */
+#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */
+#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */
+
+/* Port Data Register A - PDTRA(half) */
+#define SH7750_PDTRA_REGOFS 0x800030 /* offset */
+#define SH7750_PDTRA SH7750_P4_REG32(SH7750_PDTRA_REGOFS)
+#define SH7750_PDTRA_A7 SH7750_A7_REG32(SH7750_PDTRA_REGOFS)
+
+#define SH7750_PDTRA_BIT(n) (1 << (n))
+
+/* Port Control Register B - PCTRB */
+#define SH7750_PCTRB_REGOFS 0x800040 /* offset */
+#define SH7750_PCTRB SH7750_P4_REG32(SH7750_PCTRB_REGOFS)
+#define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS)
+
+#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */
+#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */
+#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */
+#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */
+
+/* Port Data Register B - PDTRB(half) */
+#define SH7750_PDTRB_REGOFS 0x800044 /* offset */
+#define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS)
+#define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS)
+
+#define SH7750_PDTRB_BIT(n) (1 << ((n)-16))
+
+/* GPIO Interrupt Control Register - GPIOIC(half) */
+#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */
+#define SH7750_GPIOIC SH7750_P4_REG32(SH7750_GPIOIC_REGOFS)
+#define SH7750_GPIOIC_A7 SH7750_A7_REG32(SH7750_GPIOIC_REGOFS)
+
+#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */
+
+/*
+ * Interrupt Controller - INTC
+ */
+/* Interrupt Control Register - ICR (half) */
+#define SH7750_ICR_REGOFS 0xD00000 /* offset */
+#define SH7750_ICR SH7750_P4_REG32(SH7750_ICR_REGOFS)
+#define SH7750_ICR_A7 SH7750_A7_REG32(SH7750_ICR_REGOFS)
+
+#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */
+#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */
+
+#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */
+#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while
+ SR.BL bit is set to 1 */
+#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit
+ set to 1 */
+
+#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */
+#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling
+ edge of NMI input */
+#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising
+ edge of NMI input */
+
+#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */
+#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded
+ interrupt requests */
+#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent
+ interrupt requests */
+
+/* Interrupt Priority Register A - IPRA (half) */
+#define SH7750_IPRA_REGOFS 0xD00004 /* offset */
+#define SH7750_IPRA SH7750_P4_REG32(SH7750_IPRA_REGOFS)
+#define SH7750_IPRA_A7 SH7750_A7_REG32(SH7750_IPRA_REGOFS)
+
+#define SH7750_IPRA_TMU0 0xF000 /* TMU0 interrupt priority */
+#define SH7750_IPRA_TMU0_S 12
+#define SH7750_IPRA_TMU1 0x0F00 /* TMU1 interrupt priority */
+#define SH7750_IPRA_TMU1_S 8
+#define SH7750_IPRA_TMU2 0x00F0 /* TMU2 interrupt priority */
+#define SH7750_IPRA_TMU2_S 4
+#define SH7750_IPRA_RTC 0x000F /* RTC interrupt priority */
+#define SH7750_IPRA_RTC_S 0
+
+/* Interrupt Priority Register B - IPRB (half) */
+#define SH7750_IPRB_REGOFS 0xD00008 /* offset */
+#define SH7750_IPRB SH7750_P4_REG32(SH7750_IPRB_REGOFS)
+#define SH7750_IPRB_A7 SH7750_A7_REG32(SH7750_IPRB_REGOFS)
+
+#define SH7750_IPRB_WDT 0xF000 /* WDT interrupt priority */
+#define SH7750_IPRB_WDT_S 12
+#define SH7750_IPRB_REF 0x0F00 /* Memory Refresh unit interrupt
+ priority */
+#define SH7750_IPRB_REF_S 8
+#define SH7750_IPRB_SCI1 0x00F0 /* SCI1 interrupt priority */
+#define SH7750_IPRB_SCI1_S 4
+
+/* Interrupt Priority Register ó - IPRó (half) */
+#define SH7750_IPRC_REGOFS 0xD00004 /* offset */
+#define SH7750_IPRC SH7750_P4_REG32(SH7750_IPRC_REGOFS)
+#define SH7750_IPRC_A7 SH7750_A7_REG32(SH7750_IPRC_REGOFS)
+
+#define SH7750_IPRC_GPIO 0xF000 /* GPIO interrupt priority */
+#define SH7750_IPRC_GPIO_S 12
+#define SH7750_IPRC_DMAC 0x0F00 /* DMAC interrupt priority */
+#define SH7750_IPRC_DMAC_S 8
+#define SH7750_IPRC_SCIF 0x00F0 /* SCIF interrupt priority */
+#define SH7750_IPRC_SCIF_S 4
+#define SH7750_IPRC_HUDI 0x000F /* H-UDI interrupt priority */
+#define SH7750_IPRC_HUDI_S 0
+
+
+/*
+ * User Break Controller registers
+ */
+#define SH7750_BARA 0x200000 /* Break address regiser A */
+#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */
+#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */
+#define SH7750_BARB 0x20000c /* Break address regiser B */
+#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */
+#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */
+#define SH7750_BASRB 0x000018 /* Break ASID regiser B */
+#define SH7750_BDRB 0x200018 /* Break data regiser B */
+#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */
+#define SH7750_BRCR 0x200020 /* Break control register */
+
+#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */
+
+/*
+ * Missing in RTEMS, added for QEMU
+ */
+#define SH7750_BCR3_A7 0x1f800050
+#define SH7750_BCR4_A7 0x1e0a00f0
+#define SH7750_PRECHARGE0_A7 0x1f900088
+#define SH7750_PRECHARGE1_A7 0x1f940088
+
+#endif
diff --git a/hw/shix.c b/hw/shix.c
new file mode 100644
index 0000000..9577c09
--- /dev/null
+++ b/hw/shix.c
@@ -0,0 +1,111 @@
+/*
+ * SHIX 2.0 board description
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ Shix 2.0 board by Alexis Polti, described at
+ http://perso.enst.fr/~polti/realisations/shix20/
+
+ More information in target-sh4/README.sh4
+*/
+#include "vl.h"
+
+#define BIOS_FILENAME "shix_bios.bin"
+#define BIOS_ADDRESS 0xA0000000
+
+void DMA_run(void)
+{
+ /* XXXXX */
+}
+
+void irq_info(void)
+{
+ /* XXXXX */
+}
+
+void pic_set_irq(int irq, int level)
+{
+ /* XXXXX */
+}
+
+void pic_info()
+{
+ /* XXXXX */
+}
+
+void vga_update_display()
+{
+ /* XXXXX */
+}
+
+void vga_invalidate_display()
+{
+ /* XXXXX */
+}
+
+void vga_screen_dump(const char *filename)
+{
+ /* XXXXX */
+}
+
+void shix_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState * ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ int ret;
+ CPUState *env;
+ struct SH7750State *s;
+
+ printf("Initializing CPU\n");
+ env = cpu_init();
+
+ /* Allocate memory space */
+ printf("Allocating ROM\n");
+ cpu_register_physical_memory(0x00000000, 0x00004000, IO_MEM_ROM);
+ printf("Allocating SDRAM 1\n");
+ cpu_register_physical_memory(0x08000000, 0x01000000, 0x00004000);
+ printf("Allocating SDRAM 2\n");
+ cpu_register_physical_memory(0x0c000000, 0x01000000, 0x01004000);
+
+ /* Load BIOS in 0 (and access it through P2, 0xA0000000) */
+ printf("%s: load BIOS '%s'\n", __func__, BIOS_FILENAME);
+ ret = load_image(BIOS_FILENAME, phys_ram_base);
+ if (ret < 0) { /* Check bios size */
+ fprintf(stderr, "ret=%d\n", ret);
+ fprintf(stderr, "qemu: could not load SHIX bios '%s'\n",
+ BIOS_FILENAME);
+ exit(1);
+ }
+
+ /* Register peripherals */
+ s = sh7750_init(env);
+ /* XXXXX Check success */
+ tc58128_init(s, "shix_linux_nand.bin", NULL);
+ fprintf(stderr, "initialization terminated\n");
+}
+
+QEMUMachine shix_machine = {
+ "shix",
+ "shix card",
+ shix_init
+};
diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c
new file mode 100644
index 0000000..288fb50
--- /dev/null
+++ b/hw/slavio_intctl.c
@@ -0,0 +1,400 @@
+/*
+ * QEMU Sparc SLAVIO interrupt controller emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+//#define DEBUG_IRQ_COUNT
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, args...) \
+do { printf("IRQ: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+/*
+ * Registers of interrupt controller in sun4m.
+ *
+ * This is the interrupt controller part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * There is a system master controller and one for each cpu.
+ *
+ */
+
+#define MAX_CPUS 16
+
+typedef struct SLAVIO_INTCTLState {
+ uint32_t intreg_pending[MAX_CPUS];
+ uint32_t intregm_pending;
+ uint32_t intregm_disabled;
+ uint32_t target_cpu;
+#ifdef DEBUG_IRQ_COUNT
+ uint64_t irq_count[32];
+#endif
+ CPUState *cpu_envs[MAX_CPUS];
+} SLAVIO_INTCTLState;
+
+#define INTCTL_MAXADDR 0xf
+#define INTCTLM_MAXADDR 0xf
+static void slavio_check_interrupts(void *opaque);
+
+// per-cpu interrupt controller
+static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr;
+ int cpu;
+
+ cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12;
+ saddr = (addr & INTCTL_MAXADDR) >> 2;
+ switch (saddr) {
+ case 0:
+ return s->intreg_pending[cpu];
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr;
+ int cpu;
+
+ cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12;
+ saddr = (addr & INTCTL_MAXADDR) >> 2;
+ switch (saddr) {
+ case 1: // clear pending softints
+ if (val & 0x4000)
+ val |= 80000000;
+ val &= 0xfffe0000;
+ s->intreg_pending[cpu] &= ~val;
+ DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]);
+ break;
+ case 2: // set softint
+ val &= 0xfffe0000;
+ s->intreg_pending[cpu] |= val;
+ slavio_check_interrupts(s);
+ DPRINTF("Set cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]);
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = {
+ slavio_intctl_mem_readl,
+ slavio_intctl_mem_readl,
+ slavio_intctl_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = {
+ slavio_intctl_mem_writel,
+ slavio_intctl_mem_writel,
+ slavio_intctl_mem_writel,
+};
+
+// master system interrupt controller
+static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & INTCTLM_MAXADDR) >> 2;
+ switch (saddr) {
+ case 0:
+ return s->intregm_pending & 0x7fffffff;
+ case 1:
+ return s->intregm_disabled;
+ case 4:
+ return s->target_cpu;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & INTCTLM_MAXADDR) >> 2;
+ switch (saddr) {
+ case 2: // clear (enable)
+ // Force clear unused bits
+ val &= ~0x4fb2007f;
+ s->intregm_disabled &= ~val;
+ DPRINTF("Enabled master irq mask %x, curmask %x\n", val, s->intregm_disabled);
+ slavio_check_interrupts(s);
+ break;
+ case 3: // set (disable, clear pending)
+ // Force clear unused bits
+ val &= ~0x4fb2007f;
+ s->intregm_disabled |= val;
+ s->intregm_pending &= ~val;
+ DPRINTF("Disabled master irq mask %x, curmask %x\n", val, s->intregm_disabled);
+ break;
+ case 4:
+ s->target_cpu = val & (MAX_CPUS - 1);
+ DPRINTF("Set master irq cpu %d\n", s->target_cpu);
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = {
+ slavio_intctlm_mem_readl,
+ slavio_intctlm_mem_readl,
+ slavio_intctlm_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = {
+ slavio_intctlm_mem_writel,
+ slavio_intctlm_mem_writel,
+ slavio_intctlm_mem_writel,
+};
+
+void slavio_pic_info(void *opaque)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ term_printf("per-cpu %d: pending 0x%08x\n", i, s->intreg_pending[i]);
+ }
+ term_printf("master: pending 0x%08x, disabled 0x%08x\n", s->intregm_pending, s->intregm_disabled);
+}
+
+void slavio_irq_info(void *opaque)
+{
+#ifndef DEBUG_IRQ_COUNT
+ term_printf("irq statistic code not compiled.\n");
+#else
+ SLAVIO_INTCTLState *s = opaque;
+ int i;
+ int64_t count;
+
+ term_printf("IRQ statistics:\n");
+ for (i = 0; i < 32; i++) {
+ count = s->irq_count[i];
+ if (count > 0)
+ term_printf("%2d: %" PRId64 "\n", i, count);
+ }
+#endif
+}
+
+static const uint32_t intbit_to_level[32] = {
+ 2, 3, 5, 7, 9, 11, 0, 14, 3, 5, 7, 9, 11, 13, 12, 12,
+ 6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 15, 0,
+};
+
+static void slavio_check_interrupts(void *opaque)
+{
+ CPUState *env;
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t pending = s->intregm_pending;
+ unsigned int i, j, max = 0;
+
+ pending &= ~s->intregm_disabled;
+
+ if (pending && !(s->intregm_disabled & 0x80000000)) {
+ for (i = 0; i < 32; i++) {
+ if (pending & (1 << i)) {
+ if (max < intbit_to_level[i])
+ max = intbit_to_level[i];
+ }
+ }
+ env = s->cpu_envs[s->target_cpu];
+ if (!env) {
+ DPRINTF("No CPU %d, not triggered (pending %x)\n", s->target_cpu, pending);
+ }
+ else {
+ if (env->halted)
+ env->halted = 0;
+ if (env->interrupt_index == 0) {
+ DPRINTF("Triggered CPU %d pil %d\n", s->target_cpu, max);
+#ifdef DEBUG_IRQ_COUNT
+ s->irq_count[max]++;
+#endif
+ env->interrupt_index = TT_EXTINT | max;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ else
+ DPRINTF("Not triggered (pending %x), pending exception %x\n", pending, env->interrupt_index);
+ }
+ }
+ else
+ DPRINTF("Not triggered (pending %x), disabled %x\n", pending, s->intregm_disabled);
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ max = 0;
+ env = s->cpu_envs[i];
+ if (!env)
+ continue;
+ for (j = 17; j < 32; j++) {
+ if (s->intreg_pending[i] & (1 << j)) {
+ if (max < j - 16)
+ max = j - 16;
+ }
+ }
+ if (max > 0) {
+ if (env->halted)
+ env->halted = 0;
+ if (env->interrupt_index == 0) {
+ DPRINTF("Triggered softint %d for cpu %d (pending %x)\n", max, i, pending);
+#ifdef DEBUG_IRQ_COUNT
+ s->irq_count[max]++;
+#endif
+ env->interrupt_index = TT_EXTINT | max;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ }
+ }
+}
+
+/*
+ * "irq" here is the bit number in the system interrupt register to
+ * separate serial and keyboard interrupts sharing a level.
+ */
+void slavio_pic_set_irq(void *opaque, int irq, int level)
+{
+ SLAVIO_INTCTLState *s = opaque;
+
+ DPRINTF("Set cpu %d irq %d level %d\n", s->target_cpu, irq, level);
+ if (irq < 32) {
+ uint32_t mask = 1 << irq;
+ uint32_t pil = intbit_to_level[irq];
+ if (pil > 0) {
+ if (level) {
+ s->intregm_pending |= mask;
+ s->intreg_pending[s->target_cpu] |= 1 << pil;
+ }
+ else {
+ s->intregm_pending &= ~mask;
+ s->intreg_pending[s->target_cpu] &= ~(1 << pil);
+ }
+ }
+ }
+ slavio_check_interrupts(s);
+}
+
+void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu)
+{
+ SLAVIO_INTCTLState *s = opaque;
+
+ DPRINTF("Set cpu %d local irq %d level %d\n", cpu, irq, level);
+ if (cpu == (unsigned int)-1) {
+ slavio_pic_set_irq(opaque, irq, level);
+ return;
+ }
+ if (irq < 32) {
+ uint32_t pil = intbit_to_level[irq];
+ if (pil > 0) {
+ if (level) {
+ s->intreg_pending[cpu] |= 1 << pil;
+ }
+ else {
+ s->intreg_pending[cpu] &= ~(1 << pil);
+ }
+ }
+ }
+ slavio_check_interrupts(s);
+}
+
+static void slavio_intctl_save(QEMUFile *f, void *opaque)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ qemu_put_be32s(f, &s->intreg_pending[i]);
+ }
+ qemu_put_be32s(f, &s->intregm_pending);
+ qemu_put_be32s(f, &s->intregm_disabled);
+ qemu_put_be32s(f, &s->target_cpu);
+}
+
+static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ qemu_get_be32s(f, &s->intreg_pending[i]);
+ }
+ qemu_get_be32s(f, &s->intregm_pending);
+ qemu_get_be32s(f, &s->intregm_disabled);
+ qemu_get_be32s(f, &s->target_cpu);
+ return 0;
+}
+
+static void slavio_intctl_reset(void *opaque)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ s->intreg_pending[i] = 0;
+ }
+ s->intregm_disabled = ~0xffb2007f;
+ s->intregm_pending = 0;
+ s->target_cpu = 0;
+}
+
+void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ s->cpu_envs[cpu] = env;
+}
+
+void *slavio_intctl_init(uint32_t addr, uint32_t addrg)
+{
+ int slavio_intctl_io_memory, slavio_intctlm_io_memory, i;
+ SLAVIO_INTCTLState *s;
+
+ s = qemu_mallocz(sizeof(SLAVIO_INTCTLState));
+ if (!s)
+ return NULL;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ slavio_intctl_io_memory = cpu_register_io_memory(0, slavio_intctl_mem_read, slavio_intctl_mem_write, s);
+ cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_MAXADDR, slavio_intctl_io_memory);
+ }
+
+ slavio_intctlm_io_memory = cpu_register_io_memory(0, slavio_intctlm_mem_read, slavio_intctlm_mem_write, s);
+ cpu_register_physical_memory(addrg, INTCTLM_MAXADDR, slavio_intctlm_io_memory);
+
+ register_savevm("slavio_intctl", addr, 1, slavio_intctl_save, slavio_intctl_load, s);
+ qemu_register_reset(slavio_intctl_reset, s);
+ slavio_intctl_reset(s);
+ return s;
+}
+
diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c
new file mode 100644
index 0000000..904f44e
--- /dev/null
+++ b/hw/slavio_misc.c
@@ -0,0 +1,244 @@
+/*
+ * QEMU Sparc SLAVIO aux io port emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+/* debug misc */
+//#define DEBUG_MISC
+
+/*
+ * This is the auxio port, chip control and system control part of
+ * chip STP2001 (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * This also includes the PMC CPU idle controller.
+ */
+
+#ifdef DEBUG_MISC
+#define MISC_DPRINTF(fmt, args...) \
+do { printf("MISC: " fmt , ##args); } while (0)
+#else
+#define MISC_DPRINTF(fmt, args...)
+#endif
+
+typedef struct MiscState {
+ int irq;
+ uint8_t config;
+ uint8_t aux1, aux2;
+ uint8_t diag, mctrl, sysctrl;
+} MiscState;
+
+#define MISC_MAXADDR 1
+
+static void slavio_misc_update_irq(void *opaque)
+{
+ MiscState *s = opaque;
+
+ if ((s->aux2 & 0x4) && (s->config & 0x8)) {
+ pic_set_irq(s->irq, 1);
+ } else {
+ pic_set_irq(s->irq, 0);
+ }
+}
+
+static void slavio_misc_reset(void *opaque)
+{
+ MiscState *s = opaque;
+
+ // Diagnostic and system control registers not cleared in reset
+ s->config = s->aux1 = s->aux2 = s->mctrl = 0;
+}
+
+void slavio_set_power_fail(void *opaque, int power_failing)
+{
+ MiscState *s = opaque;
+
+ MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config);
+ if (power_failing && (s->config & 0x8)) {
+ s->aux2 |= 0x4;
+ } else {
+ s->aux2 &= ~0x4;
+ }
+ slavio_misc_update_irq(s);
+}
+
+static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ MiscState *s = opaque;
+
+ switch (addr & 0xfff0000) {
+ case 0x1800000:
+ MISC_DPRINTF("Write config %2.2x\n", val & 0xff);
+ s->config = val & 0xff;
+ slavio_misc_update_irq(s);
+ break;
+ case 0x1900000:
+ MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
+ s->aux1 = val & 0xff;
+ break;
+ case 0x1910000:
+ val &= 0x3;
+ MISC_DPRINTF("Write aux2 %2.2x\n", val);
+ val |= s->aux2 & 0x4;
+ if (val & 0x2) // Clear Power Fail int
+ val &= 0x1;
+ s->aux2 = val;
+ if (val & 1)
+ qemu_system_shutdown_request();
+ slavio_misc_update_irq(s);
+ break;
+ case 0x1a00000:
+ MISC_DPRINTF("Write diag %2.2x\n", val & 0xff);
+ s->diag = val & 0xff;
+ break;
+ case 0x1b00000:
+ MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff);
+ s->mctrl = val & 0xff;
+ break;
+ case 0x1f00000:
+ MISC_DPRINTF("Write system control %2.2x\n", val & 0xff);
+ if (val & 1) {
+ s->sysctrl = 0x2;
+ qemu_system_reset_request();
+ }
+ break;
+ case 0xa000000:
+ MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
+#if 0
+ // XXX almost works
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
+#endif
+ break;
+ }
+}
+
+static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr & 0xfff0000) {
+ case 0x1800000:
+ ret = s->config;
+ MISC_DPRINTF("Read config %2.2x\n", ret);
+ break;
+ case 0x1900000:
+ ret = s->aux1;
+ MISC_DPRINTF("Read aux1 %2.2x\n", ret);
+ break;
+ case 0x1910000:
+ ret = s->aux2;
+ MISC_DPRINTF("Read aux2 %2.2x\n", ret);
+ break;
+ case 0x1a00000:
+ ret = s->diag;
+ MISC_DPRINTF("Read diag %2.2x\n", ret);
+ break;
+ case 0x1b00000:
+ ret = s->mctrl;
+ MISC_DPRINTF("Read modem control %2.2x\n", ret);
+ break;
+ case 0x1f00000:
+ MISC_DPRINTF("Read system control %2.2x\n", ret);
+ ret = s->sysctrl;
+ break;
+ case 0xa000000:
+ MISC_DPRINTF("Read power management %2.2x\n", ret);
+ break;
+ }
+ return ret;
+}
+
+static CPUReadMemoryFunc *slavio_misc_mem_read[3] = {
+ slavio_misc_mem_readb,
+ slavio_misc_mem_readb,
+ slavio_misc_mem_readb,
+};
+
+static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = {
+ slavio_misc_mem_writeb,
+ slavio_misc_mem_writeb,
+ slavio_misc_mem_writeb,
+};
+
+static void slavio_misc_save(QEMUFile *f, void *opaque)
+{
+ MiscState *s = opaque;
+
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_8s(f, &s->config);
+ qemu_put_8s(f, &s->aux1);
+ qemu_put_8s(f, &s->aux2);
+ qemu_put_8s(f, &s->diag);
+ qemu_put_8s(f, &s->mctrl);
+ qemu_put_8s(f, &s->sysctrl);
+}
+
+static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MiscState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_8s(f, &s->config);
+ qemu_get_8s(f, &s->aux1);
+ qemu_get_8s(f, &s->aux2);
+ qemu_get_8s(f, &s->diag);
+ qemu_get_8s(f, &s->mctrl);
+ qemu_get_8s(f, &s->sysctrl);
+ return 0;
+}
+
+void *slavio_misc_init(uint32_t base, int irq)
+{
+ int slavio_misc_io_memory;
+ MiscState *s;
+
+ s = qemu_mallocz(sizeof(MiscState));
+ if (!s)
+ return NULL;
+
+ slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read, slavio_misc_mem_write, s);
+ // Slavio control
+ cpu_register_physical_memory(base + 0x1800000, MISC_MAXADDR, slavio_misc_io_memory);
+ // AUX 1
+ cpu_register_physical_memory(base + 0x1900000, MISC_MAXADDR, slavio_misc_io_memory);
+ // AUX 2
+ cpu_register_physical_memory(base + 0x1910000, MISC_MAXADDR, slavio_misc_io_memory);
+ // Diagnostics
+ cpu_register_physical_memory(base + 0x1a00000, MISC_MAXADDR, slavio_misc_io_memory);
+ // Modem control
+ cpu_register_physical_memory(base + 0x1b00000, MISC_MAXADDR, slavio_misc_io_memory);
+ // System control
+ cpu_register_physical_memory(base + 0x1f00000, MISC_MAXADDR, slavio_misc_io_memory);
+ // Power management
+ cpu_register_physical_memory(base + 0xa000000, MISC_MAXADDR, slavio_misc_io_memory);
+
+ s->irq = irq;
+
+ register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, s);
+ qemu_register_reset(slavio_misc_reset, s);
+ slavio_misc_reset(s);
+ return s;
+}
diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c
new file mode 100644
index 0000000..b13e7c4
--- /dev/null
+++ b/hw/slavio_serial.c
@@ -0,0 +1,545 @@
+/*
+ * QEMU Sparc SLAVIO serial port emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+/* debug serial */
+//#define DEBUG_SERIAL
+
+/* debug keyboard */
+//#define DEBUG_KBD
+
+/* debug mouse */
+//#define DEBUG_MOUSE
+
+/*
+ * This is the serial port, mouse and keyboard part of chip STP2001
+ * (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
+ * mouse and keyboard ports don't implement all functions and they are
+ * only asynchronous. There is no DMA.
+ *
+ */
+
+#ifdef DEBUG_SERIAL
+#define SER_DPRINTF(fmt, args...) \
+do { printf("SER: " fmt , ##args); } while (0)
+#define pic_set_irq(irq, level) \
+do { printf("SER: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
+#else
+#define SER_DPRINTF(fmt, args...)
+#endif
+#ifdef DEBUG_KBD
+#define KBD_DPRINTF(fmt, args...) \
+do { printf("KBD: " fmt , ##args); } while (0)
+#else
+#define KBD_DPRINTF(fmt, args...)
+#endif
+#ifdef DEBUG_MOUSE
+#define MS_DPRINTF(fmt, args...) \
+do { printf("SER: " fmt , ##args); } while (0)
+#else
+#define MS_DPRINTF(fmt, args...)
+#endif
+
+typedef enum {
+ chn_a, chn_b,
+} chn_id_t;
+
+typedef enum {
+ ser, kbd, mouse,
+} chn_type_t;
+
+#define KBD_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[KBD_QUEUE_SIZE];
+ int rptr, wptr, count;
+} KBDQueue;
+
+typedef struct ChannelState {
+ int irq;
+ int reg;
+ int rxint, txint;
+ chn_id_t chn; // this channel, A (base+4) or B (base+0)
+ chn_type_t type;
+ struct ChannelState *otherchn;
+ uint8_t rx, tx, wregs[16], rregs[16];
+ KBDQueue queue;
+ CharDriverState *chr;
+} ChannelState;
+
+struct SerialState {
+ struct ChannelState chn[2];
+};
+
+#define SERIAL_MAXADDR 7
+
+static void handle_kbd_command(ChannelState *s, int val);
+static int serial_can_receive(void *opaque);
+static void serial_receive_byte(ChannelState *s, int ch);
+
+static void put_queue(void *opaque, int b)
+{
+ ChannelState *s = opaque;
+ KBDQueue *q = &s->queue;
+
+ KBD_DPRINTF("put: 0x%02x\n", b);
+ if (q->count >= KBD_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == KBD_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ serial_receive_byte(s, 0);
+}
+
+static uint32_t get_queue(void *opaque)
+{
+ ChannelState *s = opaque;
+ KBDQueue *q = &s->queue;
+ int val;
+
+ if (q->count == 0) {
+ return 0;
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == KBD_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ }
+ KBD_DPRINTF("get 0x%02x\n", val);
+ if (q->count > 0)
+ serial_receive_byte(s, 0);
+ return val;
+}
+
+static void slavio_serial_update_irq(ChannelState *s)
+{
+ if ((s->wregs[1] & 1) && // interrupts enabled
+ (((s->wregs[1] & 2) && s->txint == 1) || // tx ints enabled, pending
+ ((((s->wregs[1] & 0x18) == 8) || ((s->wregs[1] & 0x18) == 0x10)) &&
+ s->rxint == 1) || // rx ints enabled, pending
+ ((s->wregs[15] & 0x80) && (s->rregs[0] & 0x80)))) { // break int e&p
+ pic_set_irq(s->irq, 1);
+ } else {
+ pic_set_irq(s->irq, 0);
+ }
+}
+
+static void slavio_serial_reset_chn(ChannelState *s)
+{
+ int i;
+
+ s->reg = 0;
+ for (i = 0; i < SERIAL_MAXADDR; i++) {
+ s->rregs[i] = 0;
+ s->wregs[i] = 0;
+ }
+ s->wregs[4] = 4;
+ s->wregs[9] = 0xc0;
+ s->wregs[11] = 8;
+ s->wregs[14] = 0x30;
+ s->wregs[15] = 0xf8;
+ s->rregs[0] = 0x44;
+ s->rregs[1] = 6;
+
+ s->rx = s->tx = 0;
+ s->rxint = s->txint = 0;
+}
+
+static void slavio_serial_reset(void *opaque)
+{
+ SerialState *s = opaque;
+ slavio_serial_reset_chn(&s->chn[0]);
+ slavio_serial_reset_chn(&s->chn[1]);
+}
+
+static inline void clr_rxint(ChannelState *s)
+{
+ s->rxint = 0;
+ if (s->chn == 0)
+ s->rregs[3] &= ~0x20;
+ else {
+ s->otherchn->rregs[3] &= ~4;
+ }
+ slavio_serial_update_irq(s);
+}
+
+static inline void set_rxint(ChannelState *s)
+{
+ s->rxint = 1;
+ if (s->chn == 0)
+ s->rregs[3] |= 0x20;
+ else {
+ s->otherchn->rregs[3] |= 4;
+ }
+ slavio_serial_update_irq(s);
+}
+
+static inline void clr_txint(ChannelState *s)
+{
+ s->txint = 0;
+ if (s->chn == 0)
+ s->rregs[3] &= ~0x10;
+ else {
+ s->otherchn->rregs[3] &= ~2;
+ }
+ slavio_serial_update_irq(s);
+}
+
+static inline void set_txint(ChannelState *s)
+{
+ s->txint = 1;
+ if (s->chn == 0)
+ s->rregs[3] |= 0x10;
+ else {
+ s->otherchn->rregs[3] |= 2;
+ }
+ slavio_serial_update_irq(s);
+}
+
+static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SerialState *ser = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ int newreg, channel;
+
+ val &= 0xff;
+ saddr = (addr & 3) >> 1;
+ channel = (addr & SERIAL_MAXADDR) >> 2;
+ s = &ser->chn[channel];
+ switch (saddr) {
+ case 0:
+ SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, val & 0xff);
+ newreg = 0;
+ switch (s->reg) {
+ case 0:
+ newreg = val & 7;
+ val &= 0x38;
+ switch (val) {
+ case 8:
+ newreg |= 0x8;
+ break;
+ case 0x20:
+ clr_rxint(s);
+ break;
+ case 0x28:
+ clr_txint(s);
+ break;
+ case 0x38:
+ clr_rxint(s);
+ clr_txint(s);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 1 ... 8:
+ case 10 ... 15:
+ s->wregs[s->reg] = val;
+ break;
+ case 9:
+ switch (val & 0xc0) {
+ case 0:
+ default:
+ break;
+ case 0x40:
+ slavio_serial_reset_chn(&ser->chn[1]);
+ return;
+ case 0x80:
+ slavio_serial_reset_chn(&ser->chn[0]);
+ return;
+ case 0xc0:
+ slavio_serial_reset(ser);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ if (s->reg == 0)
+ s->reg = newreg;
+ else
+ s->reg = 0;
+ break;
+ case 1:
+ SER_DPRINTF("Write channel %c, ch %d\n", channel? 'b' : 'a', val);
+ if (s->wregs[5] & 8) { // tx enabled
+ s->tx = val;
+ if (s->chr)
+ qemu_chr_write(s->chr, &s->tx, 1);
+ else if (s->type == kbd) {
+ handle_kbd_command(s, val);
+ }
+ s->txint = 1;
+ s->rregs[0] |= 4; // Tx buffer empty
+ s->rregs[1] |= 1; // All sent
+ set_txint(s);
+ slavio_serial_update_irq(s);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *ser = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ uint32_t ret;
+ int channel;
+
+ saddr = (addr & 3) >> 1;
+ channel = (addr & SERIAL_MAXADDR) >> 2;
+ s = &ser->chn[channel];
+ switch (saddr) {
+ case 0:
+ SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, s->rregs[s->reg]);
+ ret = s->rregs[s->reg];
+ s->reg = 0;
+ return ret;
+ case 1:
+ s->rregs[0] &= ~1;
+ clr_rxint(s);
+ if (s->type == kbd)
+ ret = get_queue(s);
+ else
+ ret = s->rx;
+ SER_DPRINTF("Read channel %c, ch %d\n", channel? 'b' : 'a', ret);
+ return ret;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int serial_can_receive(void *opaque)
+{
+ ChannelState *s = opaque;
+ if (((s->wregs[3] & 1) == 0) // Rx not enabled
+ || ((s->rregs[0] & 1) == 1)) // char already available
+ return 0;
+ else
+ return 1;
+}
+
+static void serial_receive_byte(ChannelState *s, int ch)
+{
+ SER_DPRINTF("put ch %d\n", ch);
+ s->rregs[0] |= 1;
+ s->rx = ch;
+ set_rxint(s);
+}
+
+static void serial_receive_break(ChannelState *s)
+{
+ s->rregs[0] |= 0x80;
+ slavio_serial_update_irq(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ ChannelState *s = opaque;
+ serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ ChannelState *s = opaque;
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static CPUReadMemoryFunc *slavio_serial_mem_read[3] = {
+ slavio_serial_mem_readb,
+ slavio_serial_mem_readb,
+ slavio_serial_mem_readb,
+};
+
+static CPUWriteMemoryFunc *slavio_serial_mem_write[3] = {
+ slavio_serial_mem_writeb,
+ slavio_serial_mem_writeb,
+ slavio_serial_mem_writeb,
+};
+
+static void slavio_serial_save_chn(QEMUFile *f, ChannelState *s)
+{
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_be32s(f, &s->reg);
+ qemu_put_be32s(f, &s->rxint);
+ qemu_put_be32s(f, &s->txint);
+ qemu_put_8s(f, &s->rx);
+ qemu_put_8s(f, &s->tx);
+ qemu_put_buffer(f, s->wregs, 16);
+ qemu_put_buffer(f, s->rregs, 16);
+}
+
+static void slavio_serial_save(QEMUFile *f, void *opaque)
+{
+ SerialState *s = opaque;
+
+ slavio_serial_save_chn(f, &s->chn[0]);
+ slavio_serial_save_chn(f, &s->chn[1]);
+}
+
+static int slavio_serial_load_chn(QEMUFile *f, ChannelState *s, int version_id)
+{
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_be32s(f, &s->reg);
+ qemu_get_be32s(f, &s->rxint);
+ qemu_get_be32s(f, &s->txint);
+ qemu_get_8s(f, &s->rx);
+ qemu_get_8s(f, &s->tx);
+ qemu_get_buffer(f, s->wregs, 16);
+ qemu_get_buffer(f, s->rregs, 16);
+ return 0;
+}
+
+static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SerialState *s = opaque;
+ int ret;
+
+ ret = slavio_serial_load_chn(f, &s->chn[0], version_id);
+ if (ret != 0)
+ return ret;
+ ret = slavio_serial_load_chn(f, &s->chn[1], version_id);
+ return ret;
+
+}
+
+SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2)
+{
+ int slavio_serial_io_memory, i;
+ SerialState *s;
+
+ s = qemu_mallocz(sizeof(SerialState));
+ if (!s)
+ return NULL;
+
+ slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s);
+ cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory);
+
+ s->chn[0].chr = chr1;
+ s->chn[1].chr = chr2;
+
+ for (i = 0; i < 2; i++) {
+ s->chn[i].irq = irq;
+ s->chn[i].chn = 1 - i;
+ s->chn[i].type = ser;
+ if (s->chn[i].chr) {
+ qemu_chr_add_read_handler(s->chn[i].chr, serial_can_receive, serial_receive1, &s->chn[i]);
+ qemu_chr_add_event_handler(s->chn[i].chr, serial_event);
+ }
+ }
+ s->chn[0].otherchn = &s->chn[1];
+ s->chn[1].otherchn = &s->chn[0];
+ register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s);
+ qemu_register_reset(slavio_serial_reset, s);
+ slavio_serial_reset(s);
+ return s;
+}
+
+static const uint8_t keycodes[128] = {
+ 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
+ 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
+ 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
+ 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
+ 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
+};
+
+static void sunkbd_event(void *opaque, int ch)
+{
+ ChannelState *s = opaque;
+ int release = ch & 0x80;
+
+ ch = keycodes[ch & 0x7f];
+ KBD_DPRINTF("Keycode %d (%s)\n", ch, release? "release" : "press");
+ put_queue(s, ch | release);
+}
+
+static void handle_kbd_command(ChannelState *s, int val)
+{
+ KBD_DPRINTF("Command %d\n", val);
+ switch (val) {
+ case 1: // Reset, return type code
+ put_queue(s, 0xff);
+ put_queue(s, 5); // Type 5
+ break;
+ case 7: // Query layout
+ put_queue(s, 0xfe);
+ put_queue(s, 0x20); // XXX, layout?
+ break;
+ default:
+ break;
+ }
+}
+
+static void sunmouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ ChannelState *s = opaque;
+ int ch;
+
+ // XXX
+ ch = 0x42;
+ serial_receive_byte(s, ch);
+}
+
+void slavio_serial_ms_kbd_init(int base, int irq)
+{
+ int slavio_serial_io_memory, i;
+ SerialState *s;
+
+ s = qemu_mallocz(sizeof(SerialState));
+ if (!s)
+ return;
+ for (i = 0; i < 2; i++) {
+ s->chn[i].irq = irq;
+ s->chn[i].chn = 1 - i;
+ s->chn[i].chr = NULL;
+ }
+ s->chn[0].otherchn = &s->chn[1];
+ s->chn[1].otherchn = &s->chn[0];
+ s->chn[0].type = mouse;
+ s->chn[1].type = kbd;
+
+ slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s);
+ cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory);
+
+ qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0);
+ qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+ qemu_register_reset(slavio_serial_reset, s);
+ slavio_serial_reset(s);
+}
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
new file mode 100644
index 0000000..976f0d4
--- /dev/null
+++ b/hw/slavio_timer.c
@@ -0,0 +1,288 @@
+/*
+ * QEMU Sparc SLAVIO timer controller emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_TIMER
+
+#ifdef DEBUG_TIMER
+#define DPRINTF(fmt, args...) \
+do { printf("TIMER: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+/*
+ * Registers of hardware timer in sun4m.
+ *
+ * This is the timer/counter part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
+ * are zero. Bit 31 is 1 when count has been reached.
+ *
+ * Per-CPU timers interrupt local CPU, system timer uses normal
+ * interrupt routing.
+ *
+ */
+
+typedef struct SLAVIO_TIMERState {
+ uint32_t limit, count, counthigh;
+ int64_t count_load_time;
+ int64_t expire_time;
+ int64_t stop_time, tick_offset;
+ QEMUTimer *irq_timer;
+ int irq;
+ int reached, stopped;
+ int mode; // 0 = processor, 1 = user, 2 = system
+ unsigned int cpu;
+} SLAVIO_TIMERState;
+
+#define TIMER_MAXADDR 0x1f
+#define CNT_FREQ 2000000
+
+// Update count, set irq, update expire_time
+static void slavio_timer_get_out(SLAVIO_TIMERState *s)
+{
+ int out;
+ int64_t diff, ticks, count;
+ uint32_t limit;
+
+ // There are three clock tick units: CPU ticks, register units
+ // (nanoseconds), and counter ticks (500 ns).
+ if (s->mode == 1 && s->stopped)
+ ticks = s->stop_time;
+ else
+ ticks = qemu_get_clock(vm_clock) - s->tick_offset;
+
+ out = (ticks > s->expire_time);
+ if (out)
+ s->reached = 0x80000000;
+ if (!s->limit)
+ limit = 0x7fffffff;
+ else
+ limit = s->limit;
+
+ // Convert register units to counter ticks
+ limit = limit >> 9;
+
+ // Convert cpu ticks to counter ticks
+ diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec);
+
+ // Calculate what the counter should be, convert to register
+ // units
+ count = diff % limit;
+ s->count = count << 9;
+ s->counthigh = count >> 22;
+
+ // Expire time: CPU ticks left to next interrupt
+ // Convert remaining counter ticks to CPU ticks
+ s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ);
+
+ DPRINTF("irq %d limit %d reached %d d %" PRId64 " count %d s->c %x diff %" PRId64 " stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode);
+
+ if (s->mode != 1)
+ pic_set_irq_cpu(s->irq, out, s->cpu);
+}
+
+// timer callback
+static void slavio_timer_irq(void *opaque)
+{
+ SLAVIO_TIMERState *s = opaque;
+
+ if (!s->irq_timer)
+ return;
+ slavio_timer_get_out(s);
+ if (s->mode != 1)
+ qemu_mod_timer(s->irq_timer, s->expire_time);
+}
+
+static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLAVIO_TIMERState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & TIMER_MAXADDR) >> 2;
+ switch (saddr) {
+ case 0:
+ // read limit (system counter mode) or read most signifying
+ // part of counter (user mode)
+ if (s->mode != 1) {
+ // clear irq
+ pic_set_irq_cpu(s->irq, 0, s->cpu);
+ s->count_load_time = qemu_get_clock(vm_clock);
+ s->reached = 0;
+ return s->limit;
+ }
+ else {
+ slavio_timer_get_out(s);
+ return s->counthigh & 0x7fffffff;
+ }
+ case 1:
+ // read counter and reached bit (system mode) or read lsbits
+ // of counter (user mode)
+ slavio_timer_get_out(s);
+ if (s->mode != 1)
+ return (s->count & 0x7fffffff) | s->reached;
+ else
+ return s->count;
+ case 3:
+ // read start/stop status
+ return s->stopped;
+ case 4:
+ // read user/system mode
+ return s->mode & 1;
+ default:
+ return 0;
+ }
+}
+
+static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SLAVIO_TIMERState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & TIMER_MAXADDR) >> 2;
+ switch (saddr) {
+ case 0:
+ // set limit, reset counter
+ s->count_load_time = qemu_get_clock(vm_clock);
+ // fall through
+ case 2:
+ // set limit without resetting counter
+ if (!val)
+ s->limit = 0x7fffffff;
+ else
+ s->limit = val & 0x7fffffff;
+ slavio_timer_irq(s);
+ break;
+ case 3:
+ // start/stop user counter
+ if (s->mode == 1) {
+ if (val & 1) {
+ s->stop_time = qemu_get_clock(vm_clock);
+ s->stopped = 1;
+ }
+ else {
+ if (s->stopped)
+ s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time;
+ s->stopped = 0;
+ }
+ }
+ break;
+ case 4:
+ // bit 0: user (1) or system (0) counter mode
+ if (s->mode == 0 || s->mode == 1)
+ s->mode = val & 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc *slavio_timer_mem_read[3] = {
+ slavio_timer_mem_readl,
+ slavio_timer_mem_readl,
+ slavio_timer_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = {
+ slavio_timer_mem_writel,
+ slavio_timer_mem_writel,
+ slavio_timer_mem_writel,
+};
+
+static void slavio_timer_save(QEMUFile *f, void *opaque)
+{
+ SLAVIO_TIMERState *s = opaque;
+
+ qemu_put_be32s(f, &s->limit);
+ qemu_put_be32s(f, &s->count);
+ qemu_put_be32s(f, &s->counthigh);
+ qemu_put_be64s(f, &s->count_load_time);
+ qemu_put_be64s(f, &s->expire_time);
+ qemu_put_be64s(f, &s->stop_time);
+ qemu_put_be64s(f, &s->tick_offset);
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_be32s(f, &s->reached);
+ qemu_put_be32s(f, &s->stopped);
+ qemu_put_be32s(f, &s->mode);
+}
+
+static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SLAVIO_TIMERState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->limit);
+ qemu_get_be32s(f, &s->count);
+ qemu_get_be32s(f, &s->counthigh);
+ qemu_get_be64s(f, &s->count_load_time);
+ qemu_get_be64s(f, &s->expire_time);
+ qemu_get_be64s(f, &s->stop_time);
+ qemu_get_be64s(f, &s->tick_offset);
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_be32s(f, &s->reached);
+ qemu_get_be32s(f, &s->stopped);
+ qemu_get_be32s(f, &s->mode);
+ return 0;
+}
+
+static void slavio_timer_reset(void *opaque)
+{
+ SLAVIO_TIMERState *s = opaque;
+
+ s->limit = 0;
+ s->count = 0;
+ s->count_load_time = qemu_get_clock(vm_clock);;
+ s->stop_time = s->count_load_time;
+ s->tick_offset = 0;
+ s->reached = 0;
+ s->mode &= 2;
+ s->stopped = 1;
+ slavio_timer_get_out(s);
+}
+
+void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu)
+{
+ int slavio_timer_io_memory;
+ SLAVIO_TIMERState *s;
+
+ s = qemu_mallocz(sizeof(SLAVIO_TIMERState));
+ if (!s)
+ return;
+ s->irq = irq;
+ s->mode = mode;
+ s->cpu = cpu;
+ s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s);
+
+ slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read,
+ slavio_timer_mem_write, s);
+ cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory);
+ register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s);
+ qemu_register_reset(slavio_timer_reset, s);
+ slavio_timer_reset(s);
+}
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
new file mode 100644
index 0000000..214e92e
--- /dev/null
+++ b/hw/smc91c111.c
@@ -0,0 +1,714 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "vl.h"
+/* For crc32 */
+#include <zlib.h>
+
+/* Number of 2k memory pages available. */
+#define NUM_PACKETS 4
+
+typedef struct {
+ uint32_t base;
+ VLANClientState *vc;
+ uint16_t tcr;
+ uint16_t rcr;
+ uint16_t cr;
+ uint16_t ctr;
+ uint16_t gpr;
+ uint16_t ptr;
+ uint16_t ercv;
+ void *pic;
+ int irq;
+ int bank;
+ int packet_num;
+ int tx_alloc;
+ /* Bitmask of allocated packets. */
+ int allocated;
+ int tx_fifo_len;
+ int tx_fifo[NUM_PACKETS];
+ int rx_fifo_len;
+ int rx_fifo[NUM_PACKETS];
+ int tx_fifo_done_len;
+ int tx_fifo_done[NUM_PACKETS];
+ /* Packet buffer memory. */
+ uint8_t data[NUM_PACKETS][2048];
+ uint8_t int_level;
+ uint8_t int_mask;
+ uint8_t macaddr[6];
+} smc91c111_state;
+
+#define RCR_SOFT_RST 0x8000
+#define RCR_STRIP_CRC 0x0200
+#define RCR_RXEN 0x0100
+
+#define TCR_EPH_LOOP 0x2000
+#define TCR_NOCRC 0x0100
+#define TCR_PAD_EN 0x0080
+#define TCR_FORCOL 0x0004
+#define TCR_LOOP 0x0002
+#define TCR_TXEN 0x0001
+
+#define INT_MD 0x80
+#define INT_ERCV 0x40
+#define INT_EPH 0x20
+#define INT_RX_OVRN 0x10
+#define INT_ALLOC 0x08
+#define INT_TX_EMPTY 0x04
+#define INT_TX 0x02
+#define INT_RCV 0x01
+
+#define CTR_AUTO_RELEASE 0x0800
+#define CTR_RELOAD 0x0002
+#define CTR_STORE 0x0001
+
+#define RS_ALGNERR 0x8000
+#define RS_BRODCAST 0x4000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+
+/* Update interrupt status. */
+static void smc91c111_update(smc91c111_state *s)
+{
+ int level;
+
+ if (s->tx_fifo_len == 0)
+ s->int_level |= INT_TX_EMPTY;
+ if (s->tx_fifo_done_len != 0)
+ s->int_level |= INT_TX;
+ level = (s->int_level & s->int_mask) != 0;
+ pic_set_irq_new(s->pic, s->irq, level);
+}
+
+/* Try to allocate a packet. Returns 0x80 on failure. */
+static int smc91c111_allocate_packet(smc91c111_state *s)
+{
+ int i;
+ if (s->allocated == (1 << NUM_PACKETS) - 1) {
+ return 0x80;
+ }
+
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if ((s->allocated & (1 << i)) == 0)
+ break;
+ }
+ s->allocated |= 1 << i;
+ return i;
+}
+
+
+/* Process a pending TX allocate. */
+static void smc91c111_tx_alloc(smc91c111_state *s)
+{
+ s->tx_alloc = smc91c111_allocate_packet(s);
+ if (s->tx_alloc == 0x80)
+ return;
+ s->int_level |= INT_ALLOC;
+ smc91c111_update(s);
+}
+
+/* Remove and item from the RX FIFO. */
+static void smc91c111_pop_rx_fifo(smc91c111_state *s)
+{
+ int i;
+
+ s->rx_fifo_len--;
+ if (s->rx_fifo_len) {
+ for (i = 0; i < s->rx_fifo_len; i++)
+ s->rx_fifo[i] = s->rx_fifo[i + 1];
+ s->int_level |= INT_RCV;
+ } else {
+ s->int_level &= ~INT_RCV;
+ }
+ smc91c111_update(s);
+}
+
+/* Remove an item from the TX completion FIFO. */
+static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
+{
+ int i;
+
+ if (s->tx_fifo_done_len == 0)
+ return;
+ s->tx_fifo_done_len--;
+ for (i = 0; i < s->tx_fifo_done_len; i++)
+ s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
+}
+
+/* Release the memory allocated to a packet. */
+static void smc91c111_release_packet(smc91c111_state *s, int packet)
+{
+ s->allocated &= ~(1 << packet);
+ if (s->tx_alloc == 0x80)
+ smc91c111_tx_alloc(s);
+}
+
+/* Flush the TX FIFO. */
+static void smc91c111_do_tx(smc91c111_state *s)
+{
+ int i;
+ int len;
+ int control;
+ int add_crc;
+ uint32_t crc;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->tcr & TCR_TXEN) == 0)
+ return;
+ if (s->tx_fifo_len == 0)
+ return;
+ for (i = 0; i < s->tx_fifo_len; i++) {
+ packetnum = s->tx_fifo[i];
+ p = &s->data[packetnum][0];
+ /* Set status word. */
+ *(p++) = 0x01;
+ *(p++) = 0x40;
+ len = *(p++);
+ len |= ((int)*(p++)) << 8;
+ len -= 6;
+ control = p[len + 1];
+ if (control & 0x20)
+ len++;
+ /* ??? This overwrites the data following the buffer.
+ Don't know what real hardware does. */
+ if (len < 64 && (s->tcr & TCR_PAD_EN)) {
+ memset(p + len, 0, 64 - len);
+ len = 64;
+ }
+#if 0
+ /* The card is supposed to append the CRC to the frame. However
+ none of the other network traffic has the CRC appended.
+ Suspect this is low level ethernet detail we don't need to worry
+ about. */
+ add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
+ if (add_crc) {
+ crc = crc32(~0, p, len);
+ memcpy(p + len, &crc, 4);
+ len += 4;
+ }
+#else
+ add_crc = 0;
+#endif
+ if (s->ctr & CTR_AUTO_RELEASE)
+ /* Race? */
+ smc91c111_release_packet(s, packetnum);
+ else if (s->tx_fifo_done_len < NUM_PACKETS)
+ s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
+ qemu_send_packet(s->vc, p, len);
+ }
+ s->tx_fifo_len = 0;
+ smc91c111_update(s);
+}
+
+/* Add a packet to the TX FIFO. */
+static void smc91c111_queue_tx(smc91c111_state *s, int packet)
+{
+ if (s->tx_fifo_len == NUM_PACKETS)
+ return;
+ s->tx_fifo[s->tx_fifo_len++] = packet;
+ smc91c111_do_tx(s);
+}
+
+static void smc91c111_reset(smc91c111_state *s)
+{
+ s->bank = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->allocated = 0;
+ s->packet_num = 0;
+ s->tx_alloc = 0;
+ s->tcr = 0;
+ s->rcr = 0;
+ s->cr = 0xa0b1;
+ s->ctr = 0x1210;
+ s->ptr = 0;
+ s->ercv = 0x1f;
+ s->int_level = INT_TX_EMPTY;
+ s->int_mask = 0;
+ smc91c111_update(s);
+}
+
+#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
+#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
+
+static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset -= s->base;
+ if (offset == 14) {
+ s->bank = value;
+ return;
+ }
+ if (offset == 15)
+ return;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ SET_LOW(tcr, value);
+ return;
+ case 1:
+ SET_HIGH(tcr, value);
+ return;
+ case 4: /* RCR */
+ SET_LOW(rcr, value);
+ return;
+ case 5:
+ SET_HIGH(rcr, value);
+ if (s->rcr & RCR_SOFT_RST)
+ smc91c111_reset(s);
+ return;
+ case 10: case 11: /* RPCR */
+ /* Ignored */
+ return;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ SET_LOW(cr, value);
+ return;
+ case 1:
+ SET_HIGH(cr,value);
+ return;
+ case 2: case 3: /* BASE */
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ /* Not implemented. */
+ return;
+ case 10: /* Genral Purpose */
+ SET_LOW(gpr, value);
+ return;
+ case 11:
+ SET_HIGH(gpr, value);
+ return;
+ case 12: /* Control */
+ if (value & 1)
+ fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
+ if (value & 2)
+ fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
+ value &= ~3;
+ SET_LOW(ctr, value);
+ return;
+ case 13:
+ SET_HIGH(ctr, value);
+ return;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: /* MMU Command */
+ switch (value >> 5) {
+ case 0: /* no-op */
+ break;
+ case 1: /* Allocate for TX. */
+ s->tx_alloc = 0x80;
+ s->int_level &= ~INT_ALLOC;
+ smc91c111_update(s);
+ smc91c111_tx_alloc(s);
+ break;
+ case 2: /* Reset MMU. */
+ s->allocated = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->tx_alloc = 0;
+ break;
+ case 3: /* Remove from RX FIFO. */
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 4: /* Remove from RX FIFO and release. */
+ if (s->rx_fifo_len > 0) {
+ smc91c111_release_packet(s, s->rx_fifo[0]);
+ }
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 5: /* Release. */
+ smc91c111_release_packet(s, s->packet_num);
+ break;
+ case 6: /* Add to TX FIFO. */
+ smc91c111_queue_tx(s, s->packet_num);
+ break;
+ case 7: /* Reset TX FIFO. */
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ break;
+ }
+ return;
+ case 1:
+ /* Ignore. */
+ return;
+ case 2: /* Packet Number Register */
+ s->packet_num = value;
+ return;
+ case 3: case 4: case 5:
+ /* Should be readonly, but linux writes to them anyway. Ignore. */
+ return;
+ case 6: /* Pointer */
+ SET_LOW(ptr, value);
+ return;
+ case 7:
+ SET_HIGH(ptr, value);
+ return;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
+ } else {
+ p += (offset & 3);
+ }
+ s->data[n][p] = value;
+ }
+ return;
+ case 12: /* Interrupt ACK. */
+ s->int_level &= ~(value & 0xd6);
+ if (value & INT_TX)
+ smc91c111_pop_tx_fifo_done(s);
+ smc91c111_update(s);
+ return;
+ case 13: /* Interrupt mask. */
+ s->int_mask = value;
+ smc91c111_update(s);
+ return;
+ }
+ break;;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return;
+ case 8: case 9: /* Management Interface. */
+ /* Not implemented. */
+ return;
+ case 12: /* Early receive. */
+ s->ercv = value & 0x1f;
+ case 13:
+ /* Ignore. */
+ return;
+ }
+ break;
+ }
+ cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
+ s->bank, offset);
+}
+
+static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset -= s->base;
+ if (offset == 14) {
+ return s->bank;
+ }
+ if (offset == 15)
+ return 0x33;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ return s->tcr & 0xff;
+ case 1:
+ return s->tcr >> 8;
+ case 2: /* EPH Status */
+ return 0;
+ case 3:
+ return 0x40;
+ case 4: /* RCR */
+ return s->rcr & 0xff;
+ case 5:
+ return s->rcr >> 8;
+ case 6: /* Counter */
+ case 7:
+ /* Not implemented. */
+ return 0;
+ case 8: /* Free memory available. */
+ {
+ int i;
+ int n;
+ n = 0;
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if (s->allocated & (1 << i))
+ n++;
+ }
+ return n;
+ }
+ case 9: /* Memory size. */
+ return NUM_PACKETS;
+ case 10: case 11: /* RPCR */
+ /* Not implemented. */
+ return 0;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ return s->cr & 0xff;
+ case 1:
+ return s->cr >> 8;
+ case 2: case 3: /* BASE */
+ /* Not implemented. */
+ return 0;
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ return s->macaddr[offset - 4];
+ case 10: /* General Purpose */
+ return s->gpr & 0xff;
+ case 11:
+ return s->gpr >> 8;
+ case 12: /* Control */
+ return s->ctr & 0xff;
+ case 13:
+ return s->ctr >> 8;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: case 1: /* MMUCR Busy bit. */
+ return 0;
+ case 2: /* Packet Number. */
+ return s->packet_num;
+ case 3: /* Allocation Result. */
+ return s->tx_alloc;
+ case 4: /* TX FIFO */
+ if (s->tx_fifo_done_len == 0)
+ return 0x80;
+ else
+ return s->tx_fifo_done[0];
+ case 5: /* RX FIFO */
+ if (s->rx_fifo_len == 0)
+ return 0x80;
+ else
+ return s->rx_fifo[0];
+ case 6: /* Pointer */
+ return s->ptr & 0xff;
+ case 7:
+ return (s->ptr >> 8) & 0xf7;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
+ } else {
+ p += (offset & 3);
+ }
+ return s->data[n][p];
+ }
+ case 12: /* Interrupt status. */
+ return s->int_level;
+ case 13: /* Interrupt mask. */
+ return s->int_mask;
+ }
+ break;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return 0;
+ case 8: /* Management Interface. */
+ /* Not implemented. */
+ return 0x30;
+ case 9:
+ return 0x33;
+ case 10: /* Revision. */
+ return 0x91;
+ case 11:
+ return 0x33;
+ case 12:
+ return s->ercv;
+ case 13:
+ return 0;
+ }
+ break;
+ }
+ cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
+ s->bank, offset);
+ return 0;
+}
+
+static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_writeb(opaque, offset, value & 0xff);
+ smc91c111_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+ /* 32-bit writes to offset 0xc only actually write to the bank select
+ register (offset 0xe) */
+ if (offset != s->base + 0xc)
+ smc91c111_writew(opaque, offset, value & 0xffff);
+ smc91c111_writew(opaque, offset + 2, value >> 16);
+}
+
+static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readb(opaque, offset);
+ val |= smc91c111_readb(opaque, offset + 1) << 8;
+ return val;
+}
+
+static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readw(opaque, offset);
+ val |= smc91c111_readw(opaque, offset + 2) << 16;
+ return val;
+}
+
+static int smc91c111_can_receive(void *opaque)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return 1;
+ if (s->allocated == (1 << NUM_PACKETS) - 1)
+ return 0;
+ return 1;
+}
+
+static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+ int status;
+ int packetsize;
+ uint32_t crc;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return;
+ /* Short packets are padded with zeros. Recieveing a packet
+ < 64 bytes long is considered an error condition. */
+ if (size < 64)
+ packetsize = 64;
+ else
+ packetsize = (size & ~1);
+ packetsize += 6;
+ crc = (s->rcr & RCR_STRIP_CRC) == 0;
+ if (crc)
+ packetsize += 4;
+ /* TODO: Flag overrun and receive errors. */
+ if (packetsize > 2048)
+ return;
+ packetnum = smc91c111_allocate_packet(s);
+ if (packetnum == 0x80)
+ return;
+ s->rx_fifo[s->rx_fifo_len++] = packetnum;
+
+ p = &s->data[packetnum][0];
+ /* ??? Multicast packets? */
+ status = 0;
+ if (size > 1518)
+ status |= RS_TOOLONG;
+ if (size & 1)
+ status |= RS_ODDFRAME;
+ *(p++) = status & 0xff;
+ *(p++) = status >> 8;
+ *(p++) = packetsize & 0xff;
+ *(p++) = packetsize >> 8;
+ memcpy(p, buf, size & ~1);
+ p += (size & ~1);
+ /* Pad short packets. */
+ if (size < 64) {
+ int pad;
+
+ if (size & 1)
+ *(p++) = buf[size - 1];
+ pad = 64 - size;
+ memset(p, 0, pad);
+ p += pad;
+ size = 64;
+ }
+ /* It's not clear if the CRC should go before or after the last byte in
+ odd sized packets. Linux disables the CRC, so that's no help.
+ The pictures in the documentation show the CRC aligned on a 16-bit
+ boundary before the last odd byte, so that's what we do. */
+ if (crc) {
+ crc = crc32(~0, buf, size);
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ }
+ if (size & 1) {
+ *(p++) = buf[size - 1];
+ *(p++) = 0x60;
+ } else {
+ *(p++) = 0;
+ *(p++) = 0x40;
+ }
+ /* TODO: Raise early RX interrupt? */
+ s->int_level |= INT_RCV;
+ smc91c111_update(s);
+}
+
+static CPUReadMemoryFunc *smc91c111_readfn[] = {
+ smc91c111_readb,
+ smc91c111_readw,
+ smc91c111_readl
+};
+
+static CPUWriteMemoryFunc *smc91c111_writefn[] = {
+ smc91c111_writeb,
+ smc91c111_writew,
+ smc91c111_writel
+};
+
+void smc91c111_init(NICInfo *nd, uint32_t base, void *pic, int irq)
+{
+ smc91c111_state *s;
+ int iomemtype;
+
+ s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
+ iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
+ smc91c111_writefn, s);
+ cpu_register_physical_memory(base, 16, iomemtype);
+ s->base = base;
+ s->pic = pic;
+ s->irq = irq;
+ memcpy(s->macaddr, nd->macaddr, 6);
+
+ smc91c111_reset(s);
+
+ s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive,
+ smc91c111_can_receive, s);
+ /* ??? Save/restore. */
+}
diff --git a/hw/sun4m.c b/hw/sun4m.c
new file mode 100644
index 0000000..203732f
--- /dev/null
+++ b/hw/sun4m.c
@@ -0,0 +1,324 @@
+/*
+ * QEMU Sun4m System Emulator
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define KERNEL_LOAD_ADDR 0x00004000
+#define CMDLINE_ADDR 0x007ff000
+#define INITRD_LOAD_ADDR 0x00800000
+#define PROM_SIZE_MAX (256 * 1024)
+#define PROM_ADDR 0xffd00000
+#define PROM_FILENAME "openbios-sparc32"
+#define PHYS_JJ_EEPROM 0x71200000 /* m48t08 */
+#define PHYS_JJ_IDPROM_OFF 0x1FD8
+#define PHYS_JJ_EEPROM_SIZE 0x2000
+// IRQs are not PIL ones, but master interrupt controller register
+// bits
+#define PHYS_JJ_IOMMU 0x10000000 /* I/O MMU */
+#define PHYS_JJ_TCX_FB 0x50000000 /* TCX frame buffer */
+#define PHYS_JJ_SLAVIO 0x70000000 /* Slavio base */
+#define PHYS_JJ_ESPDMA 0x78400000 /* ESP DMA controller */
+#define PHYS_JJ_ESP 0x78800000 /* ESP SCSI */
+#define PHYS_JJ_ESP_IRQ 18
+#define PHYS_JJ_LEDMA 0x78400010 /* Lance DMA controller */
+#define PHYS_JJ_LE 0x78C00000 /* Lance ethernet */
+#define PHYS_JJ_LE_IRQ 16
+#define PHYS_JJ_CLOCK 0x71D00000 /* Per-CPU timer/counter, L14 */
+#define PHYS_JJ_CLOCK_IRQ 7
+#define PHYS_JJ_CLOCK1 0x71D10000 /* System timer/counter, L10 */
+#define PHYS_JJ_CLOCK1_IRQ 19
+#define PHYS_JJ_INTR0 0x71E00000 /* Per-CPU interrupt control registers */
+#define PHYS_JJ_INTR_G 0x71E10000 /* Master interrupt control registers */
+#define PHYS_JJ_MS_KBD 0x71000000 /* Mouse and keyboard */
+#define PHYS_JJ_MS_KBD_IRQ 14
+#define PHYS_JJ_SER 0x71100000 /* Serial */
+#define PHYS_JJ_SER_IRQ 15
+#define PHYS_JJ_FDC 0x71400000 /* Floppy */
+#define PHYS_JJ_FLOPPY_IRQ 22
+#define PHYS_JJ_ME_IRQ 30 /* Module error, power fail */
+#define MAX_CPUS 16
+
+/* TSC handling */
+
+uint64_t cpu_get_tsc()
+{
+ return qemu_get_clock(vm_clock);
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+ return 0;
+}
+int DMA_read_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+int DMA_write_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+void DMA_hold_DREQ (int nchan) {}
+void DMA_release_DREQ (int nchan) {}
+void DMA_schedule(int nchan) {}
+void DMA_run (void) {}
+void DMA_init (int high_page_enable) {}
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+}
+
+static void nvram_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
+{
+ m48t59_write(nvram, addr++, (value >> 8) & 0xff);
+ m48t59_write(nvram, addr++, value & 0xff);
+}
+
+static void nvram_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
+{
+ m48t59_write(nvram, addr++, value >> 24);
+ m48t59_write(nvram, addr++, (value >> 16) & 0xff);
+ m48t59_write(nvram, addr++, (value >> 8) & 0xff);
+ m48t59_write(nvram, addr++, value & 0xff);
+}
+
+static void nvram_set_string (m48t59_t *nvram, uint32_t addr,
+ const unsigned char *str, uint32_t max)
+{
+ unsigned int i;
+
+ for (i = 0; i < max && str[i] != '\0'; i++) {
+ m48t59_write(nvram, addr + i, str[i]);
+ }
+ m48t59_write(nvram, addr + max - 1, '\0');
+}
+
+static m48t59_t *nvram;
+
+extern int nographic;
+
+static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline,
+ int boot_device, uint32_t RAM_size,
+ uint32_t kernel_size,
+ int width, int height, int depth)
+{
+ unsigned char tmp = 0;
+ int i, j;
+
+ // Try to match PPC NVRAM
+ nvram_set_string(nvram, 0x00, "QEMU_BIOS", 16);
+ nvram_set_lword(nvram, 0x10, 0x00000001); /* structure v1 */
+ // NVRAM_size, arch not applicable
+ m48t59_write(nvram, 0x2D, smp_cpus & 0xff);
+ m48t59_write(nvram, 0x2E, 0);
+ m48t59_write(nvram, 0x2F, nographic & 0xff);
+ nvram_set_lword(nvram, 0x30, RAM_size);
+ m48t59_write(nvram, 0x34, boot_device & 0xff);
+ nvram_set_lword(nvram, 0x38, KERNEL_LOAD_ADDR);
+ nvram_set_lword(nvram, 0x3C, kernel_size);
+ if (cmdline) {
+ strcpy(phys_ram_base + CMDLINE_ADDR, cmdline);
+ nvram_set_lword(nvram, 0x40, CMDLINE_ADDR);
+ nvram_set_lword(nvram, 0x44, strlen(cmdline));
+ }
+ // initrd_image, initrd_size passed differently
+ nvram_set_word(nvram, 0x54, width);
+ nvram_set_word(nvram, 0x56, height);
+ nvram_set_word(nvram, 0x58, depth);
+
+ // Sun4m specific use
+ i = 0x1fd8;
+ m48t59_write(nvram, i++, 0x01);
+ m48t59_write(nvram, i++, 0x80); /* Sun4m OBP */
+ j = 0;
+ m48t59_write(nvram, i++, macaddr[j++]);
+ m48t59_write(nvram, i++, macaddr[j++]);
+ m48t59_write(nvram, i++, macaddr[j++]);
+ m48t59_write(nvram, i++, macaddr[j++]);
+ m48t59_write(nvram, i++, macaddr[j++]);
+ m48t59_write(nvram, i, macaddr[j]);
+
+ /* Calculate checksum */
+ for (i = 0x1fd8; i < 0x1fe7; i++) {
+ tmp ^= m48t59_read(nvram, i);
+ }
+ m48t59_write(nvram, 0x1fe7, tmp);
+}
+
+static void *slavio_intctl;
+
+void pic_info()
+{
+ slavio_pic_info(slavio_intctl);
+}
+
+void irq_info()
+{
+ slavio_irq_info(slavio_intctl);
+}
+
+void pic_set_irq(int irq, int level)
+{
+ slavio_pic_set_irq(slavio_intctl, irq, level);
+}
+
+void pic_set_irq_new(void *opaque, int irq, int level)
+{
+ pic_set_irq(irq, level);
+}
+
+void pic_set_irq_cpu(int irq, int level, unsigned int cpu)
+{
+ slavio_pic_set_irq_cpu(slavio_intctl, irq, level, cpu);
+}
+
+static void *iommu;
+
+uint32_t iommu_translate(uint32_t addr)
+{
+ return iommu_translate_local(iommu, addr);
+}
+
+static void *slavio_misc;
+
+void qemu_system_powerdown(void)
+{
+ slavio_set_power_fail(slavio_misc, 1);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+/* Sun4m hardware initialisation */
+static void sun4m_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ CPUState *env, *envs[MAX_CPUS];
+ char buf[1024];
+ int ret, linux_boot;
+ unsigned int i;
+ long vram_size = 0x100000, prom_offset, initrd_size, kernel_size;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ for(i = 0; i < smp_cpus; i++) {
+ env = cpu_init();
+ envs[i] = env;
+ if (i != 0)
+ env->halted = 1;
+ register_savevm("cpu", i, 3, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+ }
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, 0);
+
+ iommu = iommu_init(PHYS_JJ_IOMMU);
+ slavio_intctl = slavio_intctl_init(PHYS_JJ_INTR0, PHYS_JJ_INTR_G);
+ for(i = 0; i < smp_cpus; i++) {
+ slavio_intctl_set_cpu(slavio_intctl, i, envs[i]);
+ }
+
+ tcx_init(ds, PHYS_JJ_TCX_FB, phys_ram_base + ram_size, ram_size, vram_size, graphic_width, graphic_height);
+ if (nd_table[0].vlan) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "lance") == 0) {
+ lance_init(&nd_table[0], PHYS_JJ_LE_IRQ, PHYS_JJ_LE, PHYS_JJ_LEDMA);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+ nvram = m48t59_init(0, PHYS_JJ_EEPROM, 0, PHYS_JJ_EEPROM_SIZE, 8);
+ for (i = 0; i < MAX_CPUS; i++) {
+ slavio_timer_init(PHYS_JJ_CLOCK + i * TARGET_PAGE_SIZE, PHYS_JJ_CLOCK_IRQ, 0, i);
+ }
+ slavio_timer_init(PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ, 2, (unsigned int)-1);
+ slavio_serial_ms_kbd_init(PHYS_JJ_MS_KBD, PHYS_JJ_MS_KBD_IRQ);
+ // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device
+ // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device
+ slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[1], serial_hds[0]);
+ fdctrl_init(PHYS_JJ_FLOPPY_IRQ, 0, 1, PHYS_JJ_FDC, fd_table);
+ esp_init(bs_table, PHYS_JJ_ESP_IRQ, PHYS_JJ_ESP, PHYS_JJ_ESPDMA);
+ slavio_misc = slavio_misc_init(PHYS_JJ_SLAVIO, PHYS_JJ_ME_IRQ);
+
+ prom_offset = ram_size + vram_size;
+ cpu_register_physical_memory(PROM_ADDR,
+ (PROM_SIZE_MAX + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK,
+ prom_offset | IO_MEM_ROM);
+
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME);
+ ret = load_elf(buf, 0, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: could not load prom '%s'\n",
+ buf);
+ exit(1);
+ }
+
+ kernel_size = 0;
+ if (linux_boot) {
+ kernel_size = load_elf(kernel_filename, -0xf0000000, NULL);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0)
+ kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ if (initrd_filename) {
+ initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+ if (initrd_size > 0) {
+ for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) {
+ if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i)
+ == 0x48647253) { // HdrS
+ stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR);
+ stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size);
+ break;
+ }
+ }
+ }
+ }
+ nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, boot_device, ram_size, kernel_size, graphic_width, graphic_height, graphic_depth);
+}
+
+QEMUMachine sun4m_machine = {
+ "sun4m",
+ "Sun4m platform",
+ sun4m_init,
+};
diff --git a/hw/sun4u.c b/hw/sun4u.c
new file mode 100644
index 0000000..6d41369
--- /dev/null
+++ b/hw/sun4u.c
@@ -0,0 +1,368 @@
+/*
+ * QEMU Sun4u System Emulator
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "m48t59.h"
+
+#define KERNEL_LOAD_ADDR 0x00404000
+#define CMDLINE_ADDR 0x003ff000
+#define INITRD_LOAD_ADDR 0x00300000
+#define PROM_SIZE_MAX (512 * 1024)
+#define PROM_ADDR 0x1fff0000000ULL
+#define APB_SPECIAL_BASE 0x1fe00000000ULL
+#define APB_MEM_BASE 0x1ff00000000ULL
+#define VGA_BASE (APB_MEM_BASE + 0x400000ULL)
+#define PROM_FILENAME "openbios-sparc64"
+#define NVRAM_SIZE 0x2000
+
+/* TSC handling */
+
+uint64_t cpu_get_tsc()
+{
+ return qemu_get_clock(vm_clock);
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+ return 0;
+}
+int DMA_read_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+int DMA_write_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+void DMA_hold_DREQ (int nchan) {}
+void DMA_release_DREQ (int nchan) {}
+void DMA_schedule(int nchan) {}
+void DMA_run (void) {}
+void DMA_init (int high_page_enable) {}
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+}
+
+/* NVRAM helpers */
+void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value)
+{
+ m48t59_write(nvram, addr, value);
+}
+
+uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr)
+{
+ return m48t59_read(nvram, addr);
+}
+
+void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
+{
+ m48t59_write(nvram, addr, value >> 8);
+ m48t59_write(nvram, addr + 1, value & 0xFF);
+}
+
+uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr)
+{
+ uint16_t tmp;
+
+ tmp = m48t59_read(nvram, addr) << 8;
+ tmp |= m48t59_read(nvram, addr + 1);
+
+ return tmp;
+}
+
+void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
+{
+ m48t59_write(nvram, addr, value >> 24);
+ m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF);
+ m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF);
+ m48t59_write(nvram, addr + 3, value & 0xFF);
+}
+
+uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr)
+{
+ uint32_t tmp;
+
+ tmp = m48t59_read(nvram, addr) << 24;
+ tmp |= m48t59_read(nvram, addr + 1) << 16;
+ tmp |= m48t59_read(nvram, addr + 2) << 8;
+ tmp |= m48t59_read(nvram, addr + 3);
+
+ return tmp;
+}
+
+void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
+ const unsigned char *str, uint32_t max)
+{
+ int i;
+
+ for (i = 0; i < max && str[i] != '\0'; i++) {
+ m48t59_write(nvram, addr + i, str[i]);
+ }
+ m48t59_write(nvram, addr + max - 1, '\0');
+}
+
+int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max)
+{
+ int i;
+
+ memset(dst, 0, max);
+ for (i = 0; i < max; i++) {
+ dst[i] = NVRAM_get_byte(nvram, addr + i);
+ if (dst[i] == '\0')
+ break;
+ }
+
+ return i;
+}
+
+static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
+{
+ uint16_t tmp;
+ uint16_t pd, pd1, pd2;
+
+ tmp = prev >> 8;
+ pd = prev ^ value;
+ pd1 = pd & 0x000F;
+ pd2 = ((pd >> 4) & 0x000F) ^ pd1;
+ tmp ^= (pd1 << 3) | (pd1 << 8);
+ tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
+
+ return tmp;
+}
+
+uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count)
+{
+ uint32_t i;
+ uint16_t crc = 0xFFFF;
+ int odd;
+
+ odd = count & 1;
+ count &= ~1;
+ for (i = 0; i != count; i++) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
+ }
+ if (odd) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
+ }
+
+ return crc;
+}
+
+extern int nographic;
+
+int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
+ const unsigned char *arch,
+ uint32_t RAM_size, int boot_device,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth)
+{
+ uint16_t crc;
+
+ /* Set parameters for Open Hack'Ware BIOS */
+ NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
+ NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */
+ NVRAM_set_word(nvram, 0x14, NVRAM_size);
+ NVRAM_set_string(nvram, 0x20, arch, 16);
+ NVRAM_set_byte(nvram, 0x2f, nographic & 0xff);
+ NVRAM_set_lword(nvram, 0x30, RAM_size);
+ NVRAM_set_byte(nvram, 0x34, boot_device);
+ NVRAM_set_lword(nvram, 0x38, kernel_image);
+ NVRAM_set_lword(nvram, 0x3C, kernel_size);
+ if (cmdline) {
+ /* XXX: put the cmdline in NVRAM too ? */
+ strcpy(phys_ram_base + CMDLINE_ADDR, cmdline);
+ NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR);
+ NVRAM_set_lword(nvram, 0x44, strlen(cmdline));
+ } else {
+ NVRAM_set_lword(nvram, 0x40, 0);
+ NVRAM_set_lword(nvram, 0x44, 0);
+ }
+ NVRAM_set_lword(nvram, 0x48, initrd_image);
+ NVRAM_set_lword(nvram, 0x4C, initrd_size);
+ NVRAM_set_lword(nvram, 0x50, NVRAM_image);
+
+ NVRAM_set_word(nvram, 0x54, width);
+ NVRAM_set_word(nvram, 0x56, height);
+ NVRAM_set_word(nvram, 0x58, depth);
+ crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
+ NVRAM_set_word(nvram, 0xFC, crc);
+
+ return 0;
+}
+
+void pic_info()
+{
+}
+
+void irq_info()
+{
+}
+
+void pic_set_irq(int irq, int level)
+{
+}
+
+void pic_set_irq_new(void *opaque, int irq, int level)
+{
+}
+
+void qemu_system_powerdown(void)
+{
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+static const int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static const int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+static fdctrl_t *floppy_controller;
+
+/* Sun4u hardware initialisation */
+static void sun4u_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ CPUState *env;
+ char buf[1024];
+ m48t59_t *nvram;
+ int ret, linux_boot;
+ unsigned int i;
+ long prom_offset, initrd_size, kernel_size;
+ PCIBus *pci_bus;
+
+ linux_boot = (kernel_filename != NULL);
+
+ env = cpu_init();
+ register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* allocate RAM */
+ cpu_register_physical_memory(0, ram_size, 0);
+
+ prom_offset = ram_size + vga_ram_size;
+ cpu_register_physical_memory(PROM_ADDR,
+ (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK,
+ prom_offset | IO_MEM_ROM);
+
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME);
+ ret = load_elf(buf, 0, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: could not load prom '%s'\n",
+ buf);
+ exit(1);
+ }
+
+ kernel_size = 0;
+ initrd_size = 0;
+ if (linux_boot) {
+ /* XXX: put correct offset */
+ kernel_size = load_elf(kernel_filename, 0, NULL);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0)
+ kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+ if (initrd_size > 0) {
+ for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) {
+ if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i)
+ == 0x48647253) { // HdrS
+ stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR);
+ stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size);
+ break;
+ }
+ }
+ }
+ }
+ pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, NULL);
+ isa_mem_base = VGA_BASE;
+ pci_cirrus_vga_init(pci_bus, ds, phys_ram_base + ram_size, ram_size, vga_ram_size);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_init(&pic_set_irq_new, NULL,
+ serial_io[i], serial_irq[i], serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]);
+ }
+ }
+
+ for(i = 0; i < nb_nics; i++) {
+ if (!nd_table[i].model)
+ nd_table[i].model = "ne2k_pci";
+ pci_nic_init(pci_bus, &nd_table[i]);
+ }
+
+ pci_cmd646_ide_init(pci_bus, bs_table, 1);
+ kbd_init();
+ floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
+ nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59);
+ sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", ram_size, boot_device,
+ KERNEL_LOAD_ADDR, kernel_size,
+ kernel_cmdline,
+ INITRD_LOAD_ADDR, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth);
+
+}
+
+QEMUMachine sun4u_machine = {
+ "sun4u",
+ "Sun4u platform",
+ sun4u_init,
+};
diff --git a/hw/tc58128.c b/hw/tc58128.c
new file mode 100644
index 0000000..a8b26f7
--- /dev/null
+++ b/hw/tc58128.c
@@ -0,0 +1,181 @@
+#include <assert.h>
+#include "vl.h"
+
+#define CE1 0x0100
+#define CE2 0x0200
+#define RE 0x0400
+#define WE 0x0800
+#define ALE 0x1000
+#define CLE 0x2000
+#define RDY1 0x4000
+#define RDY2 0x8000
+#define RDY(n) ((n) == 0 ? RDY1 : RDY2)
+
+typedef enum { WAIT, READ1, READ2, READ3 } state_t;
+
+typedef struct {
+ uint8_t *flash_contents;
+ state_t state;
+ uint32_t address;
+ uint8_t address_cycle;
+} tc58128_dev;
+
+static tc58128_dev tc58128_devs[2];
+
+#define FLASH_SIZE (16*1024*1024)
+
+void init_dev(tc58128_dev * dev, char *filename)
+{
+ int ret, blocks;
+
+ dev->state = WAIT;
+ dev->flash_contents = qemu_mallocz(FLASH_SIZE);
+ memset(dev->flash_contents, 0xff, FLASH_SIZE);
+ if (!dev->flash_contents) {
+ fprintf(stderr, "could not alloc memory for flash\n");
+ exit(1);
+ }
+ if (filename) {
+ /* Load flash image skipping the first block */
+ ret = load_image(filename, dev->flash_contents + 528 * 32);
+ if (ret < 0) {
+ fprintf(stderr, "ret=%d\n", ret);
+ fprintf(stderr, "qemu: could not load flash image %s\n",
+ filename);
+ exit(1);
+ } else {
+ /* Build first block with number of blocks */
+ blocks = (ret + 528 * 32 - 1) / (528 * 32);
+ dev->flash_contents[0] = blocks & 0xff;
+ dev->flash_contents[1] = (blocks >> 8) & 0xff;
+ dev->flash_contents[2] = (blocks >> 16) & 0xff;
+ dev->flash_contents[3] = (blocks >> 24) & 0xff;
+ fprintf(stderr, "loaded %d bytes for %s into flash\n", ret,
+ filename);
+ }
+ }
+}
+
+void handle_command(tc58128_dev * dev, uint8_t command)
+{
+ switch (command) {
+ case 0xff:
+ fprintf(stderr, "reset flash device\n");
+ dev->state = WAIT;
+ break;
+ case 0x00:
+ fprintf(stderr, "read mode 1\n");
+ dev->state = READ1;
+ dev->address_cycle = 0;
+ break;
+ case 0x01:
+ fprintf(stderr, "read mode 2\n");
+ dev->state = READ2;
+ dev->address_cycle = 0;
+ break;
+ case 0x50:
+ fprintf(stderr, "read mode 3\n");
+ dev->state = READ3;
+ dev->address_cycle = 0;
+ break;
+ default:
+ fprintf(stderr, "unknown flash command 0x%02x\n", command);
+ assert(0);
+ }
+}
+
+void handle_address(tc58128_dev * dev, uint8_t data)
+{
+ switch (dev->state) {
+ case READ1:
+ case READ2:
+ case READ3:
+ switch (dev->address_cycle) {
+ case 0:
+ dev->address = data;
+ if (dev->state == READ2)
+ dev->address |= 0x100;
+ else if (dev->state == READ3)
+ dev->address |= 0x200;
+ break;
+ case 1:
+ dev->address += data * 528 * 0x100;
+ break;
+ case 2:
+ dev->address += data * 528;
+ fprintf(stderr, "address pointer in flash: 0x%08x\n",
+ dev->address);
+ break;
+ default:
+ /* Invalid data */
+ assert(0);
+ }
+ dev->address_cycle++;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+uint8_t handle_read(tc58128_dev * dev)
+{
+#if 0
+ if (dev->address % 0x100000 == 0)
+ fprintf(stderr, "reading flash at address 0x%08x\n", dev->address);
+#endif
+ return dev->flash_contents[dev->address++];
+}
+
+/* We never mark the device as busy, so interrupts cannot be triggered
+ XXXXX */
+
+int tc58128_cb(uint16_t porta, uint16_t portb,
+ uint16_t * periph_pdtra, uint16_t * periph_portadir,
+ uint16_t * periph_pdtrb, uint16_t * periph_portbdir)
+{
+ int dev;
+
+ if ((porta & CE1) == 0)
+ dev = 0;
+ else if ((porta & CE2) == 0)
+ dev = 1;
+ else
+ return 0; /* No device selected */
+
+ if ((porta & RE) && (porta & WE)) {
+ /* Nothing to do, assert ready and return to input state */
+ *periph_portadir &= 0xff00;
+ *periph_portadir |= RDY(dev);
+ *periph_pdtra |= RDY(dev);
+ return 1;
+ }
+
+ if (porta & CLE) {
+ /* Command */
+ assert((porta & WE) == 0);
+ handle_command(&tc58128_devs[dev], porta & 0x00ff);
+ } else if (porta & ALE) {
+ assert((porta & WE) == 0);
+ handle_address(&tc58128_devs[dev], porta & 0x00ff);
+ } else if ((porta & RE) == 0) {
+ *periph_portadir |= 0x00ff;
+ *periph_pdtra &= 0xff00;
+ *periph_pdtra |= handle_read(&tc58128_devs[dev]);
+ } else {
+ assert(0);
+ }
+ return 1;
+}
+
+static sh7750_io_device tc58128 = {
+ RE | WE, /* Port A triggers */
+ 0, /* Port B triggers */
+ tc58128_cb /* Callback */
+};
+
+int tc58128_init(struct SH7750State *s, char *zone1, char *zone2)
+{
+ init_dev(&tc58128_devs[0], zone1);
+ init_dev(&tc58128_devs[1], zone2);
+ return sh7750_register_io_device(s, &tc58128);
+}
diff --git a/hw/tcx.c b/hw/tcx.c
new file mode 100644
index 0000000..a3a2114
--- /dev/null
+++ b/hw/tcx.c
@@ -0,0 +1,330 @@
+/*
+ * QEMU TCX Frame buffer
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define MAXX 1024
+#define MAXY 768
+#define TCX_DAC_NREGS 16
+
+typedef struct TCXState {
+ uint32_t addr;
+ DisplayState *ds;
+ uint8_t *vram;
+ unsigned long vram_offset;
+ uint16_t width, height;
+ uint8_t r[256], g[256], b[256];
+ uint8_t dac_index, dac_state;
+} TCXState;
+
+static void tcx_screen_dump(void *opaque, const char *filename);
+
+static void tcx_draw_line32(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ *d++ = s1->b[val];
+ *d++ = s1->g[val];
+ *d++ = s1->r[val];
+ d++;
+ }
+}
+
+static void tcx_draw_line24(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ *d++ = s1->b[val];
+ *d++ = s1->g[val];
+ *d++ = s1->r[val];
+ }
+}
+
+static void tcx_draw_line8(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ /* XXX translate between palettes? */
+ *d++ = val;
+ }
+}
+
+/* Fixed line length 1024 allows us to do nice tricks not possible on
+ VGA... */
+static void tcx_update_display(void *opaque)
+{
+ TCXState *ts = opaque;
+ uint32_t page;
+ int y, page_min, page_max, y_start, dd, ds;
+ uint8_t *d, *s;
+ void (*f)(TCXState *s1, uint8_t *d, const uint8_t *s, int width);
+
+ if (ts->ds->depth == 0)
+ return;
+ page = ts->vram_offset;
+ y_start = -1;
+ page_min = 0x7fffffff;
+ page_max = -1;
+ d = ts->ds->data;
+ s = ts->vram;
+ dd = ts->ds->linesize;
+ ds = 1024;
+
+ switch (ts->ds->depth) {
+ case 32:
+ f = tcx_draw_line32;
+ break;
+ case 24:
+ f = tcx_draw_line24;
+ break;
+ default:
+ case 8:
+ f = tcx_draw_line8;
+ break;
+ case 0:
+ return;
+ }
+
+ for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
+ if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
+ if (y_start < 0)
+ y_start = y;
+ if (page < page_min)
+ page_min = page;
+ if (page > page_max)
+ page_max = page;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ y_start = -1;
+ }
+ d += dd * 4;
+ s += ds * 4;
+ }
+ }
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ }
+ /* reset modified pages */
+ if (page_max != -1) {
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+}
+
+static void tcx_invalidate_display(void *opaque)
+{
+ TCXState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
+ cpu_physical_memory_set_dirty(s->vram_offset + i);
+ }
+}
+
+static void tcx_save(QEMUFile *f, void *opaque)
+{
+ TCXState *s = opaque;
+
+ qemu_put_be32s(f, (uint32_t *)&s->addr);
+ qemu_put_be32s(f, (uint32_t *)&s->vram);
+ qemu_put_be16s(f, (uint16_t *)&s->height);
+ qemu_put_be16s(f, (uint16_t *)&s->width);
+ qemu_put_buffer(f, s->r, 256);
+ qemu_put_buffer(f, s->g, 256);
+ qemu_put_buffer(f, s->b, 256);
+ qemu_put_8s(f, &s->dac_index);
+ qemu_put_8s(f, &s->dac_state);
+}
+
+static int tcx_load(QEMUFile *f, void *opaque, int version_id)
+{
+ TCXState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, (uint32_t *)&s->addr);
+ qemu_get_be32s(f, (uint32_t *)&s->vram);
+ qemu_get_be16s(f, (uint16_t *)&s->height);
+ qemu_get_be16s(f, (uint16_t *)&s->width);
+ qemu_get_buffer(f, s->r, 256);
+ qemu_get_buffer(f, s->g, 256);
+ qemu_get_buffer(f, s->b, 256);
+ qemu_get_8s(f, &s->dac_index);
+ qemu_get_8s(f, &s->dac_state);
+ return 0;
+}
+
+static void tcx_reset(void *opaque)
+{
+ TCXState *s = opaque;
+
+ /* Initialize palette */
+ memset(s->r, 0, 256);
+ memset(s->g, 0, 256);
+ memset(s->b, 0, 256);
+ s->r[255] = s->g[255] = s->b[255] = 255;
+ memset(s->vram, 0, MAXX*MAXY);
+ cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset + MAXX*MAXY,
+ VGA_DIRTY_FLAG);
+ s->dac_index = 0;
+ s->dac_state = 0;
+}
+
+static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ TCXState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
+ switch (saddr) {
+ case 0:
+ s->dac_index = val >> 24;
+ s->dac_state = 0;
+ break;
+ case 1:
+ switch (s->dac_state) {
+ case 0:
+ s->r[s->dac_index] = val >> 24;
+ s->dac_state++;
+ break;
+ case 1:
+ s->g[s->dac_index] = val >> 24;
+ s->dac_state++;
+ break;
+ case 2:
+ s->b[s->dac_index] = val >> 24;
+ default:
+ s->dac_state = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static CPUReadMemoryFunc *tcx_dac_read[3] = {
+ tcx_dac_readl,
+ tcx_dac_readl,
+ tcx_dac_readl,
+};
+
+static CPUWriteMemoryFunc *tcx_dac_write[3] = {
+ tcx_dac_writel,
+ tcx_dac_writel,
+ tcx_dac_writel,
+};
+
+void tcx_init(DisplayState *ds, uint32_t addr, uint8_t *vram_base,
+ unsigned long vram_offset, int vram_size, int width, int height)
+{
+ TCXState *s;
+ int io_memory;
+
+ s = qemu_mallocz(sizeof(TCXState));
+ if (!s)
+ return;
+ s->ds = ds;
+ s->addr = addr;
+ s->vram = vram_base;
+ s->vram_offset = vram_offset;
+ s->width = width;
+ s->height = height;
+
+ cpu_register_physical_memory(addr + 0x800000, vram_size, vram_offset);
+ io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
+ cpu_register_physical_memory(addr + 0x200000, TCX_DAC_NREGS, io_memory);
+
+ graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
+ tcx_screen_dump, s);
+ register_savevm("tcx", addr, 1, tcx_save, tcx_load, s);
+ qemu_register_reset(tcx_reset, s);
+ tcx_reset(s);
+ dpy_resize(s->ds, width, height);
+}
+
+static void tcx_screen_dump(void *opaque, const char *filename)
+{
+ TCXState *s = opaque;
+ FILE *f;
+ uint8_t *d, *d1, v;
+ int y, x;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return;
+ fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+ d1 = s->vram;
+ for(y = 0; y < s->height; y++) {
+ d = d1;
+ for(x = 0; x < s->width; x++) {
+ v = *d;
+ fputc(s->r[v], f);
+ fputc(s->g[v], f);
+ fputc(s->b[v], f);
+ d++;
+ }
+ d1 += MAXX;
+ }
+ fclose(f);
+ return;
+}
+
+
+
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
new file mode 100644
index 0000000..a7e3600
--- /dev/null
+++ b/hw/unin_pci.c
@@ -0,0 +1,261 @@
+/*
+ * QEMU Uninorth PCI host (for all Mac99 and newer machines)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+typedef target_phys_addr_t pci_addr_t;
+#include "pci_host.h"
+
+typedef PCIHostState UNINState;
+
+static void pci_unin_main_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ UNINState *s = opaque;
+ int i;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+
+ for (i = 11; i < 32; i++) {
+ if ((val & (1 << i)) != 0)
+ break;
+ }
+#if 0
+ s->config_reg = 0x80000000 | (1 << 16) | (val & 0x7FC) | (i << 11);
+#else
+ s->config_reg = 0x80000000 | (0 << 16) | (val & 0x7FC) | (i << 11);
+#endif
+}
+
+static uint32_t pci_unin_main_config_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ UNINState *s = opaque;
+ uint32_t val;
+ int devfn;
+
+ devfn = (s->config_reg >> 8) & 0xFF;
+ val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_unin_main_config_write[] = {
+ &pci_unin_main_config_writel,
+ &pci_unin_main_config_writel,
+ &pci_unin_main_config_writel,
+};
+
+static CPUReadMemoryFunc *pci_unin_main_config_read[] = {
+ &pci_unin_main_config_readl,
+ &pci_unin_main_config_readl,
+ &pci_unin_main_config_readl,
+};
+
+static CPUWriteMemoryFunc *pci_unin_main_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static CPUReadMemoryFunc *pci_unin_main_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+#if 0
+
+static void pci_unin_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ UNINState *s = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ s->config_reg = 0x80000000 | (val & ~0x00000001);
+}
+
+static uint32_t pci_unin_config_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ UNINState *s = opaque;
+ uint32_t val;
+
+ val = (s->config_reg | 0x00000001) & ~0x80000000;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_unin_config_write[] = {
+ &pci_unin_config_writel,
+ &pci_unin_config_writel,
+ &pci_unin_config_writel,
+};
+
+static CPUReadMemoryFunc *pci_unin_config_read[] = {
+ &pci_unin_config_readl,
+ &pci_unin_config_readl,
+ &pci_unin_config_readl,
+};
+
+static CPUWriteMemoryFunc *pci_unin_write[] = {
+ &pci_host_pci_writeb,
+ &pci_host_pci_writew,
+ &pci_host_pci_writel,
+};
+
+static CPUReadMemoryFunc *pci_unin_read[] = {
+ &pci_host_pci_readb,
+ &pci_host_pci_readw,
+ &pci_host_pci_readl,
+};
+#endif
+
+static void pci_unin_set_irq(PCIDevice *d, void *pic, int irq_num, int level)
+{
+ openpic_set_irq(pic, d->config[PCI_INTERRUPT_LINE], level);
+}
+
+PCIBus *pci_pmac_init(void *pic)
+{
+ UNINState *s;
+ PCIDevice *d;
+ int pci_mem_config, pci_mem_data;
+
+ /* Use values found on a real PowerMac */
+ /* Uninorth main bus */
+ s = qemu_mallocz(sizeof(UNINState));
+ s->bus = pci_register_bus(pci_unin_set_irq, NULL, 11 << 3);
+
+ pci_mem_config = cpu_register_io_memory(0, pci_unin_main_config_read,
+ pci_unin_main_config_write, s);
+ pci_mem_data = cpu_register_io_memory(0, pci_unin_main_read,
+ pci_unin_main_write, s);
+ cpu_register_physical_memory(0xf2800000, 0x1000, pci_mem_config);
+ cpu_register_physical_memory(0xf2c00000, 0x1000, pci_mem_data);
+ d = pci_register_device(s->bus, "Uni-north main", sizeof(PCIDevice),
+ 11 << 3, NULL, NULL);
+ d->config[0x00] = 0x6b; // vendor_id : Apple
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x1F; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x0A] = 0x00; // class_sub = pci host
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x0E] = 0x00; // header_type
+ d->config[0x34] = 0x00; // capabilities_pointer
+
+#if 0 // XXX: not activated as PPC BIOS doesn't handle mutiple buses properly
+ /* pci-to-pci bridge */
+ d = pci_register_device("Uni-north bridge", sizeof(PCIDevice), 0, 13 << 3,
+ NULL, NULL);
+ d->config[0x00] = 0x11; // vendor_id : TI
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x26; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x05; // revision
+ d->config[0x0A] = 0x04; // class_sub = pci2pci
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x20; // latency_timer
+ d->config[0x0E] = 0x01; // header_type
+
+ d->config[0x18] = 0x01; // primary_bus
+ d->config[0x19] = 0x02; // secondary_bus
+ d->config[0x1A] = 0x02; // subordinate_bus
+ d->config[0x1B] = 0x20; // secondary_latency_timer
+ d->config[0x1C] = 0x11; // io_base
+ d->config[0x1D] = 0x01; // io_limit
+ d->config[0x20] = 0x00; // memory_base
+ d->config[0x21] = 0x80;
+ d->config[0x22] = 0x00; // memory_limit
+ d->config[0x23] = 0x80;
+ d->config[0x24] = 0x01; // prefetchable_memory_base
+ d->config[0x25] = 0x80;
+ d->config[0x26] = 0xF1; // prefectchable_memory_limit
+ d->config[0x27] = 0x7F;
+ // d->config[0x34] = 0xdc // capabilities_pointer
+#endif
+#if 0 // XXX: not needed for now
+ /* Uninorth AGP bus */
+ s = &pci_bridge[1];
+ pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read,
+ pci_unin_config_write, s);
+ pci_mem_data = cpu_register_io_memory(0, pci_unin_read,
+ pci_unin_write, s);
+ cpu_register_physical_memory(0xf0800000, 0x1000, pci_mem_config);
+ cpu_register_physical_memory(0xf0c00000, 0x1000, pci_mem_data);
+
+ d = pci_register_device("Uni-north AGP", sizeof(PCIDevice), 0, 11 << 3,
+ NULL, NULL);
+ d->config[0x00] = 0x6b; // vendor_id : Apple
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x20; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x0A] = 0x00; // class_sub = pci host
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x0E] = 0x00; // header_type
+ // d->config[0x34] = 0x80; // capabilities_pointer
+#endif
+
+#if 0 // XXX: not needed for now
+ /* Uninorth internal bus */
+ s = &pci_bridge[2];
+ pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read,
+ pci_unin_config_write, s);
+ pci_mem_data = cpu_register_io_memory(0, pci_unin_read,
+ pci_unin_write, s);
+ cpu_register_physical_memory(0xf4800000, 0x1000, pci_mem_config);
+ cpu_register_physical_memory(0xf4c00000, 0x1000, pci_mem_data);
+
+ d = pci_register_device("Uni-north internal", sizeof(PCIDevice),
+ 3, 11 << 3, NULL, NULL);
+ d->config[0x00] = 0x6b; // vendor_id : Apple
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x1E; // device_id
+ d->config[0x03] = 0x00;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x0A] = 0x00; // class_sub = pci host
+ d->config[0x0B] = 0x06; // class_base = PCI_bridge
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x0E] = 0x00; // header_type
+ d->config[0x34] = 0x00; // capabilities_pointer
+#endif
+ return s->bus;
+}
+
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
new file mode 100644
index 0000000..8fc0b74
--- /dev/null
+++ b/hw/usb-hid.c
@@ -0,0 +1,551 @@
+/*
+ * QEMU USB HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+/* HID interface requests */
+#define GET_REPORT 0xa101
+#define GET_IDLE 0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_IDLE 0x210a
+#define SET_PROTOCOL 0x210b
+
+#define USB_MOUSE 1
+#define USB_TABLET 2
+
+typedef struct USBMouseState {
+ USBDevice dev;
+ int dx, dy, dz, buttons_state;
+ int x, y;
+ int kind;
+ int mouse_grabbed;
+} USBMouseState;
+
+/* mostly the same values as the Bochs USB Mouse device */
+static const uint8_t qemu_mouse_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x10, 0x00, /* u16 bcdUSB; v1.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x27, 0x06, /* u16 idVendor; */
+ 0x01, 0x00, /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+
+ 0x03, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x01, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_mouse_config_descriptor[] = {
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x22, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x04, /* u8 iConfiguration; */
+ 0xa0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 50, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x03, /* u8 if_bInterfaceClass; */
+ 0x01, /* u8 if_bInterfaceSubClass; */
+ 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x05, /* u8 if_iInterface; */
+
+ /* HID descriptor */
+ 0x09, /* u8 bLength; */
+ 0x21, /* u8 bDescriptorType; */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ 0x22, /* u8 type; Report */
+ 50, 0, /* u16 len */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x03, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_tablet_config_descriptor[] = {
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x22, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x04, /* u8 iConfiguration; */
+ 0xa0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 50, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x03, /* u8 if_bInterfaceClass; */
+ 0x01, /* u8 if_bInterfaceSubClass; */
+ 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x05, /* u8 if_iInterface; */
+
+ /* HID descriptor */
+ 0x09, /* u8 bLength; */
+ 0x21, /* u8 bDescriptorType; */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ 0x22, /* u8 type; Report */
+ 74, 0, /* u16 len */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x08, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_mouse_hid_report_descriptor[] = {
+ 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
+ 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
+ 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
+ 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
+ 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81,
+ 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
+ 0xC0, 0xC0,
+};
+
+static const uint8_t qemu_tablet_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page Generic Desktop */
+ 0x09, 0x01, /* Usage Mouse */
+ 0xA1, 0x01, /* Collection Application */
+ 0x09, 0x01, /* Usage Pointer */
+ 0xA1, 0x00, /* Collection Physical */
+ 0x05, 0x09, /* Usage Page Button */
+ 0x19, 0x01, /* Usage Minimum Button 1 */
+ 0x29, 0x03, /* Usage Maximum Button 3 */
+ 0x15, 0x00, /* Logical Minimum 0 */
+ 0x25, 0x01, /* Logical Maximum 1 */
+ 0x95, 0x03, /* Report Count 3 */
+ 0x75, 0x01, /* Report Size 1 */
+ 0x81, 0x02, /* Input (Data, Var, Abs) */
+ 0x95, 0x01, /* Report Count 1 */
+ 0x75, 0x05, /* Report Size 5 */
+ 0x81, 0x01, /* Input (Cnst, Var, Abs) */
+ 0x05, 0x01, /* Usage Page Generic Desktop */
+ 0x09, 0x30, /* Usage X */
+ 0x09, 0x31, /* Usage Y */
+ 0x15, 0x00, /* Logical Minimum 0 */
+ 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */
+ 0x35, 0x00, /* Physical Minimum 0 */
+ 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */
+ 0x75, 0x10, /* Report Size 16 */
+ 0x95, 0x02, /* Report Count 2 */
+ 0x81, 0x02, /* Input (Data, Var, Abs) */
+ 0x05, 0x01, /* Usage Page Generic Desktop */
+ 0x09, 0x38, /* Usage Wheel */
+ 0x15, 0x81, /* Logical Minimum -127 */
+ 0x25, 0x7F, /* Logical Maximum 127 */
+ 0x35, 0x00, /* Physical Minimum 0 (same as logical) */
+ 0x45, 0x00, /* Physical Maximum 0 (same as logical) */
+ 0x75, 0x08, /* Report Size 8 */
+ 0x95, 0x01, /* Report Count 1 */
+ 0x81, 0x02, /* Input (Data, Var, Rel) */
+ 0xC0, /* End Collection */
+ 0xC0, /* End Collection */
+};
+
+static void usb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ USBMouseState *s = opaque;
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ s->buttons_state = buttons_state;
+}
+
+static void usb_tablet_event(void *opaque,
+ int x, int y, int dz, int buttons_state)
+{
+ USBMouseState *s = opaque;
+
+ s->x = x;
+ s->y = y;
+ s->dz += dz;
+ s->buttons_state = buttons_state;
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+ if (val < vmin)
+ return vmin;
+ else if (val > vmax)
+ return vmax;
+ else
+ return val;
+}
+
+static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len)
+{
+ int dx, dy, dz, b, l;
+
+ if (!s->mouse_grabbed) {
+ qemu_add_mouse_event_handler(usb_mouse_event, s, 0);
+ s->mouse_grabbed = 1;
+ }
+
+ dx = int_clamp(s->dx, -128, 127);
+ dy = int_clamp(s->dy, -128, 127);
+ dz = int_clamp(s->dz, -128, 127);
+
+ s->dx -= dx;
+ s->dy -= dy;
+ s->dz -= dz;
+
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ buf[0] = b;
+ buf[1] = dx;
+ buf[2] = dy;
+ l = 3;
+ if (len >= 4) {
+ buf[3] = dz;
+ l = 4;
+ }
+ return l;
+}
+
+static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len)
+{
+ int dz, b, l;
+
+ if (!s->mouse_grabbed) {
+ qemu_add_mouse_event_handler(usb_tablet_event, s, 1);
+ s->mouse_grabbed = 1;
+ }
+
+ dz = int_clamp(s->dz, -128, 127);
+ s->dz -= dz;
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ buf[0] = b;
+ buf[1] = s->x & 0xff;
+ buf[2] = s->x >> 8;
+ buf[3] = s->y & 0xff;
+ buf[4] = s->y >> 8;
+ buf[5] = dz;
+ l = 6;
+
+ return l;
+}
+
+static void usb_mouse_handle_reset(USBDevice *dev)
+{
+ USBMouseState *s = (USBMouseState *)dev;
+
+ s->dx = 0;
+ s->dy = 0;
+ s->dz = 0;
+ s->x = 0;
+ s->y = 0;
+ s->buttons_state = 0;
+}
+
+static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBMouseState *s = (USBMouseState *)dev;
+ int ret = 0;
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_mouse_dev_descriptor,
+ sizeof(qemu_mouse_dev_descriptor));
+ ret = sizeof(qemu_mouse_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ if (s->kind == USB_MOUSE) {
+ memcpy(data, qemu_mouse_config_descriptor,
+ sizeof(qemu_mouse_config_descriptor));
+ ret = sizeof(qemu_mouse_config_descriptor);
+ } else if (s->kind == USB_TABLET) {
+ memcpy(data, qemu_tablet_config_descriptor,
+ sizeof(qemu_tablet_config_descriptor));
+ ret = sizeof(qemu_tablet_config_descriptor);
+ }
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ break;
+ case 2:
+ /* product description */
+ if (s->kind == USB_MOUSE)
+ ret = set_usb_string(data, "QEMU USB Mouse");
+ else if (s->kind == USB_TABLET)
+ ret = set_usb_string(data, "QEMU USB Tablet");
+ break;
+ case 3:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 4:
+ ret = set_usb_string(data, "HID Mouse");
+ break;
+ case 5:
+ ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* hid specific requests */
+ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case 0x22:
+ if (s->kind == USB_MOUSE) {
+ memcpy(data, qemu_mouse_hid_report_descriptor,
+ sizeof(qemu_mouse_hid_report_descriptor));
+ ret = sizeof(qemu_mouse_hid_report_descriptor);
+ } else if (s->kind == USB_TABLET) {
+ memcpy(data, qemu_tablet_hid_report_descriptor,
+ sizeof(qemu_tablet_hid_report_descriptor));
+ ret = sizeof(qemu_tablet_hid_report_descriptor);
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case GET_REPORT:
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, data, length);
+ else if (s->kind == USB_TABLET)
+ ret = usb_tablet_poll(s, data, length);
+ break;
+ case SET_IDLE:
+ ret = 0;
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_mouse_handle_data(USBDevice *dev, int pid,
+ uint8_t devep, uint8_t *data, int len)
+{
+ USBMouseState *s = (USBMouseState *)dev;
+ int ret = 0;
+
+ switch(pid) {
+ case USB_TOKEN_IN:
+ if (devep == 1) {
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, data, len);
+ else if (s->kind == USB_TABLET)
+ ret = usb_tablet_poll(s, data, len);
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_mouse_handle_destroy(USBDevice *dev)
+{
+ USBMouseState *s = (USBMouseState *)dev;
+
+ qemu_add_mouse_event_handler(NULL, NULL, 0);
+ qemu_free(s);
+}
+
+USBDevice *usb_tablet_init(void)
+{
+ USBMouseState *s;
+
+ s = qemu_mallocz(sizeof(USBMouseState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_control = usb_mouse_handle_control;
+ s->dev.handle_data = usb_mouse_handle_data;
+ s->dev.handle_destroy = usb_mouse_handle_destroy;
+ s->kind = USB_TABLET;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
+
+ return (USBDevice *)s;
+}
+
+USBDevice *usb_mouse_init(void)
+{
+ USBMouseState *s;
+
+ s = qemu_mallocz(sizeof(USBMouseState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_control = usb_mouse_handle_control;
+ s->dev.handle_data = usb_mouse_handle_data;
+ s->dev.handle_destroy = usb_mouse_handle_destroy;
+ s->kind = USB_MOUSE;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+
+ return (USBDevice *)s;
+}
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
new file mode 100644
index 0000000..8350931
--- /dev/null
+++ b/hw/usb-hub.c
@@ -0,0 +1,563 @@
+/*
+ * QEMU USB HUB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG
+
+#define MAX_PORTS 8
+
+typedef struct USBHubPort {
+ USBPort port;
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+} USBHubPort;
+
+typedef struct USBHubState {
+ USBDevice dev;
+ int nb_ports;
+ USBHubPort ports[MAX_PORTS];
+} USBHubState;
+
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+#define PORT_STAT_CONNECTION 0x0001
+#define PORT_STAT_ENABLE 0x0002
+#define PORT_STAT_SUSPEND 0x0004
+#define PORT_STAT_OVERCURRENT 0x0008
+#define PORT_STAT_RESET 0x0010
+#define PORT_STAT_POWER 0x0100
+#define PORT_STAT_LOW_SPEED 0x0200
+#define PORT_STAT_HIGH_SPEED 0x0400
+#define PORT_STAT_TEST 0x0800
+#define PORT_STAT_INDICATOR 0x1000
+
+#define PORT_STAT_C_CONNECTION 0x0001
+#define PORT_STAT_C_ENABLE 0x0002
+#define PORT_STAT_C_SUSPEND 0x0004
+#define PORT_STAT_C_OVERCURRENT 0x0008
+#define PORT_STAT_C_RESET 0x0010
+
+#define PORT_CONNECTION 0
+#define PORT_ENABLE 1
+#define PORT_SUSPEND 2
+#define PORT_OVERCURRENT 3
+#define PORT_RESET 4
+#define PORT_POWER 8
+#define PORT_LOWSPEED 9
+#define PORT_HIGHSPEED 10
+#define PORT_C_CONNECTION 16
+#define PORT_C_ENABLE 17
+#define PORT_C_SUSPEND 18
+#define PORT_C_OVERCURRENT 19
+#define PORT_C_RESET 20
+#define PORT_TEST 21
+#define PORT_INDICATOR 22
+
+/* same as Linux kernel root hubs */
+
+static const uint8_t qemu_hub_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x10, 0x01, /* u16 bcdUSB; v1.1 */
+
+ 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* u16 idVendor; */
+ 0x00, 0x00, /* u16 idProduct; */
+ 0x01, 0x01, /* u16 bcdDevice */
+
+ 0x03, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x01, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+/* XXX: patch interrupt size */
+static const uint8_t qemu_hub_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0xc0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x00, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* u8 if_bInterfaceSubClass; */
+ 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x00, /* u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_hub_hub_descriptor[] =
+{
+ 0x00, /* u8 bLength; patched in later */
+ 0x29, /* u8 bDescriptorType; Hub-descriptor */
+ 0x00, /* u8 bNbrPorts; (patched later) */
+ 0x0a, /* u16 wHubCharacteristics; */
+ 0x00, /* (per-port OC, no power switching) */
+ 0x01, /* u8 bPwrOn2pwrGood; 2ms */
+ 0x00 /* u8 bHubContrCurrent; 0 mA */
+
+ /* DeviceRemovable and PortPwrCtrlMask patched in later */
+};
+
+static void usb_hub_attach(USBPort *port1, USBDevice *dev)
+{
+ USBHubState *s = port1->opaque;
+ USBHubPort *port = &s->ports[port1->index];
+
+ if (dev) {
+ if (port->port.dev)
+ usb_attach(port1, NULL);
+
+ port->wPortStatus |= PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (dev->speed == USB_SPEED_LOW)
+ port->wPortStatus |= PORT_STAT_LOW_SPEED;
+ else
+ port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
+ port->port.dev = dev;
+ /* send the attach message */
+ dev->handle_packet(dev,
+ USB_MSG_ATTACH, 0, 0, NULL, 0);
+ } else {
+ dev = port->port.dev;
+ if (dev) {
+ port->wPortStatus &= ~PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (port->wPortStatus & PORT_STAT_ENABLE) {
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ port->wPortChange |= PORT_STAT_C_ENABLE;
+ }
+ /* send the detach message */
+ dev->handle_packet(dev,
+ USB_MSG_DETACH, 0, 0, NULL, 0);
+ port->port.dev = NULL;
+ }
+ }
+}
+
+static void usb_hub_handle_reset(USBDevice *dev)
+{
+ /* XXX: do it */
+}
+
+static int usb_hub_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_hub_dev_descriptor,
+ sizeof(qemu_hub_dev_descriptor));
+ ret = sizeof(qemu_hub_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_hub_config_descriptor,
+ sizeof(qemu_hub_config_descriptor));
+
+ /* status change endpoint size based on number
+ * of ports */
+ data[22] = (s->nb_ports + 1 + 7) / 8;
+
+ ret = sizeof(qemu_hub_config_descriptor);
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* serial number */
+ ret = set_usb_string(data, "314159");
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB Hub");
+ break;
+ case 3:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* usb specific requests */
+ case GetHubStatus:
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ ret = 4;
+ break;
+ case GetPortStatus:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ data[0] = port->wPortStatus;
+ data[1] = port->wPortStatus >> 8;
+ data[2] = port->wPortChange;
+ data[3] = port->wPortChange >> 8;
+ ret = 4;
+ }
+ break;
+ case SetHubFeature:
+ case ClearHubFeature:
+ if (value == 0 || value == 1) {
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case SetPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ USBDevice *dev;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ switch(value) {
+ case PORT_SUSPEND:
+ port->wPortStatus |= PORT_STAT_SUSPEND;
+ break;
+ case PORT_RESET:
+ if (dev) {
+ dev->handle_packet(dev,
+ USB_MSG_RESET, 0, 0, NULL, 0);
+ port->wPortChange |= PORT_STAT_C_RESET;
+ /* set enable bit */
+ port->wPortStatus |= PORT_STAT_ENABLE;
+ }
+ break;
+ case PORT_POWER:
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case ClearPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ USBDevice *dev;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ switch(value) {
+ case PORT_ENABLE:
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ break;
+ case PORT_C_ENABLE:
+ port->wPortChange &= ~PORT_STAT_C_ENABLE;
+ break;
+ case PORT_SUSPEND:
+ port->wPortStatus &= ~PORT_STAT_SUSPEND;
+ break;
+ case PORT_C_SUSPEND:
+ port->wPortChange &= ~PORT_STAT_C_SUSPEND;
+ break;
+ case PORT_C_CONNECTION:
+ port->wPortChange &= ~PORT_STAT_C_CONNECTION;
+ break;
+ case PORT_C_OVERCURRENT:
+ port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
+ break;
+ case PORT_C_RESET:
+ port->wPortChange &= ~PORT_STAT_C_RESET;
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case GetHubDescriptor:
+ {
+ unsigned int n, limit, var_hub_size = 0;
+ memcpy(data, qemu_hub_hub_descriptor,
+ sizeof(qemu_hub_hub_descriptor));
+ data[2] = s->nb_ports;
+
+ /* fill DeviceRemovable bits */
+ limit = ((s->nb_ports + 1 + 7) / 8) + 7;
+ for (n = 7; n < limit; n++) {
+ data[n] = 0x00;
+ var_hub_size++;
+ }
+
+ /* fill PortPwrCtrlMask bits */
+ limit = limit + ((s->nb_ports + 7) / 8);
+ for (;n < limit; n++) {
+ data[n] = 0xff;
+ var_hub_size++;
+ }
+
+ ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+ data[0] = ret;
+ break;
+ }
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_handle_data(USBDevice *dev, int pid,
+ uint8_t devep, uint8_t *data, int len)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ switch(pid) {
+ case USB_TOKEN_IN:
+ if (devep == 1) {
+ USBHubPort *port;
+ unsigned int status;
+ int i, n;
+ n = (s->nb_ports + 1 + 7) / 8;
+ if (len == 1) { /* FreeBSD workaround */
+ n = 1;
+ } else if (n > len) {
+ return USB_RET_BABBLE;
+ }
+ status = 0;
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ if (port->wPortChange)
+ status |= (1 << (i + 1));
+ }
+ if (status != 0) {
+ for(i = 0; i < n; i++) {
+ data[i] = status >> (8 * i);
+ }
+ ret = n;
+ } else {
+ ret = USB_RET_NAK; /* usb11 11.13.1 */
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_broadcast_packet(USBHubState *s, int pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len)
+{
+ USBHubPort *port;
+ USBDevice *dev;
+ int i, ret;
+
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
+ ret = dev->handle_packet(dev, pid,
+ devaddr, devep,
+ data, len);
+ if (ret != USB_RET_NODEV) {
+ return ret;
+ }
+ }
+ }
+ return USB_RET_NODEV;
+}
+
+static int usb_hub_handle_packet(USBDevice *dev, int pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len)
+{
+ USBHubState *s = (USBHubState *)dev;
+
+#if defined(DEBUG) && 0
+ printf("usb_hub: pid=0x%x\n", pid);
+#endif
+ if (dev->state == USB_STATE_DEFAULT &&
+ dev->addr != 0 &&
+ devaddr != dev->addr &&
+ (pid == USB_TOKEN_SETUP ||
+ pid == USB_TOKEN_OUT ||
+ pid == USB_TOKEN_IN)) {
+ /* broadcast the packet to the devices */
+ return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len);
+ }
+ return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
+}
+
+static void usb_hub_handle_destroy(USBDevice *dev)
+{
+ USBHubState *s = (USBHubState *)dev;
+
+ qemu_free(s);
+}
+
+USBDevice *usb_hub_init(int nb_ports)
+{
+ USBHubState *s;
+ USBHubPort *port;
+ int i;
+
+ if (nb_ports > MAX_PORTS)
+ return NULL;
+ s = qemu_mallocz(sizeof(USBHubState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_hub_handle_packet;
+
+ /* generic USB device init */
+ s->dev.handle_reset = usb_hub_handle_reset;
+ s->dev.handle_control = usb_hub_handle_control;
+ s->dev.handle_data = usb_hub_handle_data;
+ s->dev.handle_destroy = usb_hub_handle_destroy;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Hub");
+
+ s->nb_ports = nb_ports;
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ qemu_register_usb_port(&port->port, s, i, usb_hub_attach);
+ port->wPortStatus = PORT_STAT_POWER;
+ port->wPortChange = 0;
+ }
+ return (USBDevice *)s;
+}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
new file mode 100644
index 0000000..ff2047d
--- /dev/null
+++ b/hw/usb-msd.c
@@ -0,0 +1,402 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_MSD
+
+#ifdef DEBUG_MSD
+#define DPRINTF(fmt, args...) \
+do { printf("usb-msd: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+/* USB requests. */
+#define MassStorageReset 0xff
+#define GetMaxLun 0xfe
+
+enum USBMSDMode {
+ USB_MSDM_CBW, /* Command Block. */
+ USB_MSDM_DATAOUT, /* Tranfer data to device. */
+ USB_MSDM_DATAIN, /* Transfer data from device. */
+ USB_MSDM_CSW /* Command Status. */
+};
+
+typedef struct {
+ USBDevice dev;
+ enum USBMSDMode mode;
+ uint32_t data_len;
+ uint32_t tag;
+ SCSIDevice *scsi_dev;
+ int result;
+} MSDState;
+
+static const uint8_t qemu_msd_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x10, 0x00, /* u16 bcdUSB; v1.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ /* Vendor and product id are arbitrary. */
+ 0x00, 0x00, /* u16 idVendor; */
+ 0x00, 0x00, /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+
+ 0x01, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x03, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_msd_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x20, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0xc0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x00, /* u8 MaxPower; */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x02, /* u8 if_bNumEndpoints; */
+ 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
+ 0x06, /* u8 if_bInterfaceSubClass; SCSI */
+ 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
+ 0x00, /* u8 if_iInterface; */
+
+ /* Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00 /* u8 ep_bInterval; */
+};
+
+static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail)
+{
+ MSDState *s = (MSDState *)opaque;
+
+ DPRINTF("Command complete\n");
+ s->result = fail;
+ s->mode = USB_MSDM_CSW;
+}
+
+static void usb_msd_handle_reset(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ DPRINTF("Reset\n");
+ s->mode = USB_MSDM_CBW;
+}
+
+static int usb_msd_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ MSDState *s = (MSDState *)dev;
+ int ret = 0;
+
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_msd_dev_descriptor,
+ sizeof(qemu_msd_dev_descriptor));
+ ret = sizeof(qemu_msd_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_msd_config_descriptor,
+ sizeof(qemu_msd_config_descriptor));
+ ret = sizeof(qemu_msd_config_descriptor);
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB HARDDRIVE");
+ break;
+ case 3:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+ /* Class specific requests. */
+ case MassStorageReset:
+ /* Reset state ready for the next CBW. */
+ s->mode = USB_MSDM_CBW;
+ ret = 0;
+ break;
+ case GetMaxLun:
+ data[0] = 0;
+ ret = 1;
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+struct usb_msd_cbw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t data_len;
+ uint8_t flags;
+ uint8_t lun;
+ uint8_t cmd_len;
+ uint8_t cmd[16];
+};
+
+struct usb_msd_csw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t residue;
+ uint8_t status;
+};
+
+static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
+ uint8_t *data, int len)
+{
+ MSDState *s = (MSDState *)dev;
+ int ret = 0;
+ struct usb_msd_cbw cbw;
+ struct usb_msd_csw csw;
+
+ switch (pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_CBW:
+ if (len != 31) {
+ fprintf(stderr, "usb-msd: Bad CBW size");
+ goto fail;
+ }
+ memcpy(&cbw, data, 31);
+ if (le32_to_cpu(cbw.sig) != 0x43425355) {
+ fprintf(stderr, "usb-msd: Bad signature %08x\n",
+ le32_to_cpu(cbw.sig));
+ goto fail;
+ }
+ DPRINTF("Command on LUN %d\n", cbw.lun);
+ if (cbw.lun != 0) {
+ fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
+ goto fail;
+ }
+ s->tag = le32_to_cpu(cbw.tag);
+ s->data_len = le32_to_cpu(cbw.data_len);
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ } else if (cbw.flags & 0x80) {
+ s->mode = USB_MSDM_DATAIN;
+ } else {
+ s->mode = USB_MSDM_DATAOUT;
+ }
+ DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
+ s->tag, cbw.flags, cbw.cmd_len, s->data_len);
+ scsi_send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ ret = len;
+ break;
+
+ case USB_MSDM_DATAOUT:
+ DPRINTF("Data out %d/%d\n", len, s->data_len);
+ if (len > s->data_len)
+ goto fail;
+
+ if (scsi_write_data(s->scsi_dev, data, len))
+ goto fail;
+
+ s->data_len -= len;
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ ret = len;
+ break;
+
+ default:
+ DPRINTF("Unexpected write (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_CSW:
+ DPRINTF("Command status %d tag 0x%x, len %d\n",
+ s->result, s->tag, len);
+ if (len < 13)
+ goto fail;
+
+ csw.sig = cpu_to_le32(0x53425355);
+ csw.tag = cpu_to_le32(s->tag);
+ csw.residue = 0;
+ csw.status = s->result;
+ memcpy(data, &csw, 13);
+ ret = 13;
+ s->mode = USB_MSDM_CBW;
+ break;
+
+ case USB_MSDM_DATAIN:
+ DPRINTF("Data in %d/%d\n", len, s->data_len);
+ if (len > s->data_len)
+ len = s->data_len;
+
+ if (scsi_read_data(s->scsi_dev, data, len))
+ goto fail;
+
+ s->data_len -= len;
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ ret = len;
+ break;
+
+ default:
+ DPRINTF("Unexpected read (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_msd_handle_destroy(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ scsi_disk_destroy(s->scsi_dev);
+ qemu_free(s);
+}
+
+USBDevice *usb_msd_init(const char *filename)
+{
+ MSDState *s;
+ BlockDriverState *bdrv;
+
+ s = qemu_mallocz(sizeof(MSDState));
+ if (!s)
+ return NULL;
+
+ bdrv = bdrv_new("usb");
+ bdrv_open(bdrv, filename, 0);
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_msd_handle_reset;
+ s->dev.handle_control = usb_msd_handle_control;
+ s->dev.handle_data = usb_msd_handle_data;
+ s->dev.handle_destroy = usb_msd_handle_destroy;
+
+ snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
+ filename);
+
+ s->scsi_dev = scsi_disk_init(bdrv, usb_msd_command_complete, s);
+ usb_msd_handle_reset((USBDevice *)s);
+ return (USBDevice *)s;
+}
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
new file mode 100644
index 0000000..e87d9da
--- /dev/null
+++ b/hw/usb-ohci.c
@@ -0,0 +1,1190 @@
+/*
+ * QEMU USB OHCI Emulation
+ * Copyright (c) 2004 Gianni Tedesco
+ * Copyright (c) 2006 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO:
+ * o Isochronous transfers
+ * o Allocate bandwidth in frames properly
+ * o Disable timers when nothing needs to be done, or remove timer usage
+ * all together.
+ * o Handle unrecoverable errors properly
+ * o BIOS work to boot from USB storage
+*/
+
+#include "vl.h"
+
+//#define DEBUG_OHCI
+/* Dump packet contents. */
+//#define DEBUG_PACKET
+/* This causes frames to occur 1000x slower */
+//#define OHCI_TIME_WARP 1
+
+#ifdef DEBUG_OHCI
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+/* Number of Downstream Ports on the root hub. */
+
+#define OHCI_MAX_PORTS 15
+
+static int64_t usb_frame_time;
+static int64_t usb_bit_time;
+
+typedef struct OHCIPort {
+ USBPort port;
+ uint32_t ctrl;
+} OHCIPort;
+
+typedef struct {
+ struct PCIDevice pci_dev;
+ target_phys_addr_t mem_base;
+ int mem;
+ int num_ports;
+
+ QEMUTimer *eof_timer;
+ int64_t sof_time;
+
+ /* OHCI state */
+ /* Control partition */
+ uint32_t ctl, status;
+ uint32_t intr_status;
+ uint32_t intr;
+
+ /* memory pointer partition */
+ uint32_t hcca;
+ uint32_t ctrl_head, ctrl_cur;
+ uint32_t bulk_head, bulk_cur;
+ uint32_t per_cur;
+ uint32_t done;
+ int done_count;
+
+ /* Frame counter partition */
+ uint32_t fsmps:15;
+ uint32_t fit:1;
+ uint32_t fi:14;
+ uint32_t frt:1;
+ uint16_t frame_number;
+ uint16_t padding;
+ uint32_t pstart;
+ uint32_t lst;
+
+ /* Root Hub partition */
+ uint32_t rhdesc_a, rhdesc_b;
+ uint32_t rhstatus;
+ OHCIPort rhport[OHCI_MAX_PORTS];
+} OHCIState;
+
+/* Host Controller Communications Area */
+struct ohci_hcca {
+ uint32_t intr[32];
+ uint16_t frame, pad;
+ uint32_t done;
+};
+
+/* Bitfields for the first word of an Endpoint Desciptor. */
+#define OHCI_ED_FA_SHIFT 0
+#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_EN_SHIFT 7
+#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT)
+#define OHCI_ED_D_SHIFT 11
+#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT)
+#define OHCI_ED_S (1<<13)
+#define OHCI_ED_K (1<<14)
+#define OHCI_ED_F (1<<15)
+#define OHCI_ED_MPS_SHIFT 7
+#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT)
+
+/* Flags in the head field of an Endpoint Desciptor. */
+#define OHCI_ED_H 1
+#define OHCI_ED_C 2
+
+/* Bitfields for the first word of a Transfer Desciptor. */
+#define OHCI_TD_R (1<<18)
+#define OHCI_TD_DP_SHIFT 19
+#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT)
+#define OHCI_TD_DI_SHIFT 21
+#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
+#define OHCI_TD_T0 (1<<24)
+#define OHCI_TD_T1 (1<<24)
+#define OHCI_TD_EC_SHIFT 26
+#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
+#define OHCI_TD_CC_SHIFT 28
+#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT)
+
+#define OHCI_DPTR_MASK 0xfffffff0
+
+#define OHCI_BM(val, field) \
+ (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
+
+#define OHCI_SET_BM(val, field, newval) do { \
+ val &= ~OHCI_##field##_MASK; \
+ val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
+ } while(0)
+
+/* endpoint descriptor */
+struct ohci_ed {
+ uint32_t flags;
+ uint32_t tail;
+ uint32_t head;
+ uint32_t next;
+};
+
+/* General transfer descriptor */
+struct ohci_td {
+ uint32_t flags;
+ uint32_t cbp;
+ uint32_t next;
+ uint32_t be;
+};
+
+#define USB_HZ 12000000
+
+/* OHCI Local stuff */
+#define OHCI_CTL_CBSR ((1<<0)|(1<<1))
+#define OHCI_CTL_PLE (1<<2)
+#define OHCI_CTL_IE (1<<3)
+#define OHCI_CTL_CLE (1<<4)
+#define OHCI_CTL_BLE (1<<5)
+#define OHCI_CTL_HCFS ((1<<6)|(1<<7))
+#define OHCI_USB_RESET 0x00
+#define OHCI_USB_RESUME 0x40
+#define OHCI_USB_OPERATIONAL 0x80
+#define OHCI_USB_SUSPEND 0xc0
+#define OHCI_CTL_IR (1<<8)
+#define OHCI_CTL_RWC (1<<9)
+#define OHCI_CTL_RWE (1<<10)
+
+#define OHCI_STATUS_HCR (1<<0)
+#define OHCI_STATUS_CLF (1<<1)
+#define OHCI_STATUS_BLF (1<<2)
+#define OHCI_STATUS_OCR (1<<3)
+#define OHCI_STATUS_SOC ((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF (1<<2) /* Start of frame */
+#define OHCI_INTR_RD (1<<3) /* Resume detect */
+#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */
+#define OHCI_INTR_OC (1<<30) /* Ownership change */
+#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */
+
+#define OHCI_HCCA_SIZE 0x100
+#define OHCI_HCCA_MASK 0xffffff00
+
+#define OHCI_EDPTR_MASK 0xfffffff0
+
+#define OHCI_FMI_FI 0x00003fff
+#define OHCI_FMI_FSMPS 0xffff0000
+#define OHCI_FMI_FIT 0x80000000
+
+#define OHCI_FR_RT (1<<31)
+
+#define OHCI_LS_THRESH 0x628
+
+#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */
+#define OHCI_RHA_PSM (1<<8)
+#define OHCI_RHA_NPS (1<<9)
+#define OHCI_RHA_DT (1<<10)
+#define OHCI_RHA_OCPM (1<<11)
+#define OHCI_RHA_NOCP (1<<12)
+#define OHCI_RHA_POTPGT_MASK 0xff000000
+
+#define OHCI_RHS_LPS (1<<0)
+#define OHCI_RHS_OCI (1<<1)
+#define OHCI_RHS_DRWE (1<<15)
+#define OHCI_RHS_LPSC (1<<16)
+#define OHCI_RHS_OCIC (1<<17)
+#define OHCI_RHS_CRWE (1<<31)
+
+#define OHCI_PORT_CCS (1<<0)
+#define OHCI_PORT_PES (1<<1)
+#define OHCI_PORT_PSS (1<<2)
+#define OHCI_PORT_POCI (1<<3)
+#define OHCI_PORT_PRS (1<<4)
+#define OHCI_PORT_PPS (1<<8)
+#define OHCI_PORT_LSDA (1<<9)
+#define OHCI_PORT_CSC (1<<16)
+#define OHCI_PORT_PESC (1<<17)
+#define OHCI_PORT_PSSC (1<<18)
+#define OHCI_PORT_OCIC (1<<19)
+#define OHCI_PORT_PRSC (1<<20)
+#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
+ |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
+
+#define OHCI_TD_DIR_SETUP 0x0
+#define OHCI_TD_DIR_OUT 0x1
+#define OHCI_TD_DIR_IN 0x2
+#define OHCI_TD_DIR_RESERVED 0x3
+
+#define OHCI_CC_NOERROR 0x0
+#define OHCI_CC_CRC 0x1
+#define OHCI_CC_BITSTUFFING 0x2
+#define OHCI_CC_DATATOGGLEMISMATCH 0x3
+#define OHCI_CC_STALL 0x4
+#define OHCI_CC_DEVICENOTRESPONDING 0x5
+#define OHCI_CC_PIDCHECKFAILURE 0x6
+#define OHCI_CC_UNDEXPETEDPID 0x7
+#define OHCI_CC_DATAOVERRUN 0x8
+#define OHCI_CC_DATAUNDERRUN 0x9
+#define OHCI_CC_BUFFEROVERRUN 0xc
+#define OHCI_CC_BUFFERUNDERRUN 0xd
+
+/* Update IRQ levels */
+static inline void ohci_intr_update(OHCIState *ohci)
+{
+ int level = 0;
+
+ if ((ohci->intr & OHCI_INTR_MIE) &&
+ (ohci->intr_status & ohci->intr))
+ level = 1;
+
+ pci_set_irq(&ohci->pci_dev, 0, level);
+}
+
+/* Set an interrupt */
+static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
+{
+ ohci->intr_status |= intr;
+ ohci_intr_update(ohci);
+}
+
+/* Attach or detach a device on a root hub port. */
+static void ohci_attach(USBPort *port1, USBDevice *dev)
+{
+ OHCIState *s = port1->opaque;
+ OHCIPort *port = &s->rhport[port1->index];
+ uint32_t old_state = port->ctrl;
+
+ if (dev) {
+ if (port->port.dev) {
+ usb_attach(port1, NULL);
+ }
+ /* set connect status */
+ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
+
+ /* update speed */
+ if (dev->speed == USB_SPEED_LOW)
+ port->ctrl |= OHCI_PORT_LSDA;
+ else
+ port->ctrl &= ~OHCI_PORT_LSDA;
+ port->port.dev = dev;
+ /* send the attach message */
+ dev->handle_packet(dev,
+ USB_MSG_ATTACH, 0, 0, NULL, 0);
+ dprintf("usb-ohci: Attached port %d\n", port1->index);
+ } else {
+ /* set connect status */
+ if (port->ctrl & OHCI_PORT_CCS) {
+ port->ctrl &= ~OHCI_PORT_CCS;
+ port->ctrl |= OHCI_PORT_CSC;
+ }
+ /* disable port */
+ if (port->ctrl & OHCI_PORT_PES) {
+ port->ctrl &= ~OHCI_PORT_PES;
+ port->ctrl |= OHCI_PORT_PESC;
+ }
+ dev = port->port.dev;
+ if (dev) {
+ /* send the detach message */
+ dev->handle_packet(dev,
+ USB_MSG_DETACH, 0, 0, NULL, 0);
+ }
+ port->port.dev = NULL;
+ dprintf("usb-ohci: Detached port %d\n", port1->index);
+ }
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(s, OHCI_INTR_RHSC);
+}
+
+/* Reset the controller */
+static void ohci_reset(OHCIState *ohci)
+{
+ OHCIPort *port;
+ int i;
+
+ ohci->ctl = 0;
+ ohci->status = 0;
+ ohci->intr_status = 0;
+ ohci->intr = OHCI_INTR_MIE;
+
+ ohci->hcca = 0;
+ ohci->ctrl_head = ohci->ctrl_cur = 0;
+ ohci->bulk_head = ohci->bulk_cur = 0;
+ ohci->per_cur = 0;
+ ohci->done = 0;
+ ohci->done_count = 7;
+
+ /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+ * I took the value linux sets ...
+ */
+ ohci->fsmps = 0x2778;
+ ohci->fi = 0x2edf;
+ ohci->fit = 0;
+ ohci->frt = 0;
+ ohci->frame_number = 0;
+ ohci->pstart = 0;
+ ohci->lst = OHCI_LS_THRESH;
+
+ ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+ ohci->rhdesc_b = 0x0; /* Impl. specific */
+ ohci->rhstatus = 0;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ {
+ port = &ohci->rhport[i];
+ port->ctrl = 0;
+ if (port->port.dev)
+ ohci_attach(&port->port, port->port.dev);
+ }
+ dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name);
+}
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+ *buf = le32_to_cpu(*buf);
+ }
+
+ return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint32_t tmp = cpu_to_le32(*buf);
+ cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+ }
+
+ return 1;
+}
+
+static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed)
+{
+ return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_read_td(uint32_t addr, struct ohci_td *td)
+{
+ return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed)
+{
+ return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_put_td(uint32_t addr, struct ohci_td *td)
+{
+ return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+/* Read/Write the contents of a TD from/to main memory. */
+static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
+{
+ uint32_t ptr;
+ uint32_t n;
+
+ ptr = td->cbp;
+ n = 0x1000 - (ptr & 0xfff);
+ if (n > len)
+ n = len;
+ cpu_physical_memory_rw(ptr, buf, n, write);
+ if (n == len)
+ return;
+ ptr = td->be & ~0xfffu;
+ buf += n;
+ cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+/* Service a transport descriptor.
+ Returns nonzero to terminate processing of this endpoint. */
+
+static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
+{
+ int dir;
+ size_t len = 0;
+ uint8_t buf[8192];
+ char *str = NULL;
+ int pid;
+ int ret;
+ int i;
+ USBDevice *dev;
+ struct ohci_td td;
+ uint32_t addr;
+ int flag_r;
+
+ addr = ed->head & OHCI_DPTR_MASK;
+ if (!ohci_read_td(addr, &td)) {
+ fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+ return 0;
+ }
+
+ dir = OHCI_BM(ed->flags, ED_D);
+ switch (dir) {
+ case OHCI_TD_DIR_OUT:
+ case OHCI_TD_DIR_IN:
+ /* Same value. */
+ break;
+ default:
+ dir = OHCI_BM(td.flags, TD_DP);
+ break;
+ }
+
+ switch (dir) {
+ case OHCI_TD_DIR_IN:
+ str = "in";
+ pid = USB_TOKEN_IN;
+ break;
+ case OHCI_TD_DIR_OUT:
+ str = "out";
+ pid = USB_TOKEN_OUT;
+ break;
+ case OHCI_TD_DIR_SETUP:
+ str = "setup";
+ pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad direction\n");
+ return 1;
+ }
+ if (td.cbp && td.be) {
+ if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
+ len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
+ } else {
+ len = (td.be - td.cbp) + 1;
+ }
+
+ if (len && dir != OHCI_TD_DIR_IN) {
+ ohci_copy_td(&td, buf, len, 0);
+ }
+ }
+
+ flag_r = (td.flags & OHCI_TD_R) != 0;
+#ifdef DEBUG_PACKET
+ dprintf(" TD @ 0x%.8x %u bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+ addr, len, str, flag_r, td.cbp, td.be);
+
+ if (len >= 0 && dir != OHCI_TD_DIR_IN) {
+ dprintf(" data:");
+ for (i = 0; i < len; i++)
+ printf(" %.2x", buf[i]);
+ dprintf("\n");
+ }
+#endif
+ ret = USB_RET_NODEV;
+ for (i = 0; i < ohci->num_ports; i++) {
+ dev = ohci->rhport[i].port.dev;
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+ continue;
+
+ ret = dev->handle_packet(dev, pid, OHCI_BM(ed->flags, ED_FA),
+ OHCI_BM(ed->flags, ED_EN), buf, len);
+ if (ret != USB_RET_NODEV)
+ break;
+ }
+#ifdef DEBUG_PACKET
+ dprintf("ret=%d\n", ret);
+#endif
+ if (ret >= 0) {
+ if (dir == OHCI_TD_DIR_IN) {
+ ohci_copy_td(&td, buf, ret, 1);
+#ifdef DEBUG_PACKET
+ dprintf(" data:");
+ for (i = 0; i < ret; i++)
+ printf(" %.2x", buf[i]);
+ dprintf("\n");
+#endif
+ } else {
+ ret = len;
+ }
+ }
+
+ /* Writeback */
+ if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+ /* Transmission succeeded. */
+ if (ret == len) {
+ td.cbp = 0;
+ } else {
+ td.cbp += ret;
+ if ((td.cbp & 0xfff) + ret > 0xfff) {
+ td.cbp &= 0xfff;
+ td.cbp |= td.be & ~0xfff;
+ }
+ }
+ td.flags |= OHCI_TD_T1;
+ td.flags ^= OHCI_TD_T0;
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
+ OHCI_SET_BM(td.flags, TD_EC, 0);
+
+ ed->head &= ~OHCI_ED_C;
+ if (td.flags & OHCI_TD_T0)
+ ed->head |= OHCI_ED_C;
+ } else {
+ if (ret >= 0) {
+ dprintf("usb-ohci: Underrun\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
+ } else {
+ switch (ret) {
+ case USB_RET_NODEV:
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+ case USB_RET_NAK:
+ dprintf("usb-ohci: got NAK\n");
+ return 1;
+ case USB_RET_STALL:
+ dprintf("usb-ohci: got STALL\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
+ break;
+ case USB_RET_BABBLE:
+ dprintf("usb-ohci: got BABBLE\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
+ OHCI_SET_BM(td.flags, TD_EC, 3);
+ break;
+ }
+ }
+ ed->head |= OHCI_ED_H;
+ }
+
+ /* Retire this TD */
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= td.next & OHCI_DPTR_MASK;
+ td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ ohci_put_td(addr, &td);
+ return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
+}
+
+/* Service an endpoint list. Returns nonzero if active TD were found. */
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
+{
+ struct ohci_ed ed;
+ uint32_t next_ed;
+ uint32_t cur;
+ int active;
+
+ active = 0;
+
+ if (head == 0)
+ return 0;
+
+ for (cur = head; cur; cur = next_ed) {
+ if (!ohci_read_ed(cur, &ed)) {
+ fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+ return 0;
+ }
+
+ next_ed = ed.next & OHCI_DPTR_MASK;
+
+ if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K))
+ continue;
+
+ /* Skip isochronous endpoints. */
+ if (ed.flags & OHCI_ED_F)
+ continue;
+
+ while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
+#ifdef DEBUG_PACKET
+ dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
+ "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+ OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
+ OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
+ (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
+ OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
+ (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+ ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+#endif
+ active = 1;
+
+ if (ohci_service_td(ohci, &ed))
+ break;
+ }
+
+ ohci_put_ed(cur, &ed);
+ }
+
+ return active;
+}
+
+/* Generate a SOF event, and set a timer for EOF */
+static void ohci_sof(OHCIState *ohci)
+{
+ ohci->sof_time = qemu_get_clock(vm_clock);
+ qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+ ohci_set_interrupt(ohci, OHCI_INTR_SF);
+}
+
+/* Do frame processing on frame boundary */
+static void ohci_frame_boundary(void *opaque)
+{
+ OHCIState *ohci = opaque;
+ struct ohci_hcca hcca;
+
+ cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0);
+
+ /* Process all the lists at the end of the frame */
+ if (ohci->ctl & OHCI_CTL_PLE) {
+ int n;
+
+ n = ohci->frame_number & 0x1f;
+ ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
+ }
+ if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
+ if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
+ dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur);
+ if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) {
+ ohci->ctrl_cur = 0;
+ ohci->status &= ~OHCI_STATUS_CLF;
+ }
+ }
+
+ if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
+ if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
+ ohci->bulk_cur = 0;
+ ohci->status &= ~OHCI_STATUS_BLF;
+ }
+ }
+
+ /* Frame boundary, so do EOF stuf here */
+ ohci->frt = ohci->fit;
+
+ /* XXX: endianness */
+ ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
+ hcca.frame = cpu_to_le32(ohci->frame_number);
+
+ if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
+ if (!ohci->done)
+ abort();
+ if (ohci->intr & ohci->intr_status)
+ ohci->done |= 1;
+ hcca.done = cpu_to_le32(ohci->done);
+ ohci->done = 0;
+ ohci->done_count = 7;
+ ohci_set_interrupt(ohci, OHCI_INTR_WD);
+ }
+
+ if (ohci->done_count != 7 && ohci->done_count != 0)
+ ohci->done_count--;
+
+ /* Do SOF stuff here */
+ ohci_sof(ohci);
+
+ /* Writeback HCCA */
+ cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1);
+}
+
+/* Start sending SOF tokens across the USB bus, lists are processed in
+ * next frame
+ */
+static int ohci_bus_start(OHCIState *ohci)
+{
+ ohci->eof_timer = qemu_new_timer(vm_clock,
+ ohci_frame_boundary,
+ ohci);
+
+ if (ohci->eof_timer == NULL) {
+ fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n",
+ ohci->pci_dev.name);
+ /* TODO: Signal unrecoverable error */
+ return 0;
+ }
+
+ dprintf("usb-ohci: %s: USB Operational\n", ohci->pci_dev.name);
+
+ ohci_sof(ohci);
+
+ return 1;
+}
+
+/* Stop sending SOF tokens on the bus */
+static void ohci_bus_stop(OHCIState *ohci)
+{
+ if (ohci->eof_timer)
+ qemu_del_timer(ohci->eof_timer);
+}
+
+/* Sets a flag in a port status register but only set it if the port is
+ * connected, if not set ConnectStatusChange flag. If flag is enabled
+ * return 1.
+ */
+static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
+{
+ int ret = 1;
+
+ /* writing a 0 has no effect */
+ if (val == 0)
+ return 0;
+
+ /* If CurrentConnectStatus is cleared we set
+ * ConnectStatusChange
+ */
+ if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
+ if (ohci->rhstatus & OHCI_RHS_DRWE) {
+ /* TODO: CSC is a wakeup event */
+ }
+ return 0;
+ }
+
+ if (ohci->rhport[i].ctrl & val)
+ ret = 0;
+
+ /* set the bit */
+ ohci->rhport[i].ctrl |= val;
+
+ return ret;
+}
+
+/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
+static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
+{
+ val &= OHCI_FMI_FI;
+
+ if (val != ohci->fi) {
+ dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
+ ohci->pci_dev.name, ohci->fi, ohci->fi);
+ }
+
+ ohci->fi = val;
+}
+
+static void ohci_port_power(OHCIState *ohci, int i, int p)
+{
+ if (p) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
+ } else {
+ ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
+ OHCI_PORT_CCS|
+ OHCI_PORT_PSS|
+ OHCI_PORT_PRS);
+ }
+}
+
+/* Set HcControlRegister */
+static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+ uint32_t new_state;
+
+ old_state = ohci->ctl & OHCI_CTL_HCFS;
+ ohci->ctl = val;
+ new_state = ohci->ctl & OHCI_CTL_HCFS;
+
+ /* no state change */
+ if (old_state == new_state)
+ return;
+
+ switch (new_state) {
+ case OHCI_USB_OPERATIONAL:
+ ohci_bus_start(ohci);
+ break;
+ case OHCI_USB_SUSPEND:
+ ohci_bus_stop(ohci);
+ dprintf("usb-ohci: %s: USB Suspended\n", ohci->pci_dev.name);
+ break;
+ case OHCI_USB_RESUME:
+ dprintf("usb-ohci: %s: USB Resume\n", ohci->pci_dev.name);
+ break;
+ case OHCI_USB_RESET:
+ dprintf("usb-ohci: %s: USB Reset\n", ohci->pci_dev.name);
+ break;
+ }
+}
+
+static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
+{
+ uint16_t fr;
+ int64_t tks;
+
+ if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
+ return (ohci->frt << 31);
+
+ /* Being in USB operational state guarnatees sof_time was
+ * set already.
+ */
+ tks = qemu_get_clock(vm_clock) - ohci->sof_time;
+
+ /* avoid muldiv if possible */
+ if (tks >= usb_frame_time)
+ return (ohci->frt << 31);
+
+ tks = muldiv64(1, tks, usb_bit_time);
+ fr = (uint16_t)(ohci->fi - tks);
+
+ return (ohci->frt << 31) | fr;
+}
+
+
+/* Set root hub status */
+static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+
+ old_state = ohci->rhstatus;
+
+ /* write 1 to clear OCIC */
+ if (val & OHCI_RHS_OCIC)
+ ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+ if (val & OHCI_RHS_LPS) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 0);
+ dprintf("usb-ohci: powered down all ports\n");
+ }
+
+ if (val & OHCI_RHS_LPSC) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 1);
+ dprintf("usb-ohci: powered up all ports\n");
+ }
+
+ if (val & OHCI_RHS_DRWE)
+ ohci->rhstatus |= OHCI_RHS_DRWE;
+
+ if (val & OHCI_RHS_CRWE)
+ ohci->rhstatus &= ~OHCI_RHS_DRWE;
+
+ if (old_state != ohci->rhstatus)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+}
+
+/* Set root hub port status */
+static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
+{
+ uint32_t old_state;
+ OHCIPort *port;
+
+ port = &ohci->rhport[portnum];
+ old_state = port->ctrl;
+
+ /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
+ if (val & OHCI_PORT_WTC)
+ port->ctrl &= ~(val & OHCI_PORT_WTC);
+
+ if (val & OHCI_PORT_CCS)
+ port->ctrl &= ~OHCI_PORT_PES;
+
+ ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS))
+ dprintf("usb-ohci: port %d: SUSPEND\n", portnum);
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
+ dprintf("usb-ohci: port %d: RESET\n", portnum);
+ port->port.dev->handle_packet(port->port.dev, USB_MSG_RESET,
+ 0, 0, NULL, 0);
+ port->ctrl &= ~OHCI_PORT_PRS;
+ /* ??? Should this also set OHCI_PORT_PESC. */
+ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
+ }
+
+ /* Invert order here to ensure in ambiguous case, device is
+ * powered up...
+ */
+ if (val & OHCI_PORT_LSDA)
+ ohci_port_power(ohci, portnum, 0);
+ if (val & OHCI_PORT_PPS)
+ ohci_port_power(ohci, portnum, 1);
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+
+ return;
+}
+
+static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
+{
+ OHCIState *ohci = ptr;
+
+ addr -= ohci->mem_base;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+ return 0xffffffff;
+ }
+
+ if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
+ }
+
+ switch (addr >> 2) {
+ case 0: /* HcRevision */
+ return 0x10;
+
+ case 1: /* HcControl */
+ return ohci->ctl;
+
+ case 2: /* HcCommandStatus */
+ return ohci->status;
+
+ case 3: /* HcInterruptStatus */
+ return ohci->intr_status;
+
+ case 4: /* HcInterruptEnable */
+ case 5: /* HcInterruptDisable */
+ return ohci->intr;
+
+ case 6: /* HcHCCA */
+ return ohci->hcca;
+
+ case 7: /* HcPeriodCurrentED */
+ return ohci->per_cur;
+
+ case 8: /* HcControlHeadED */
+ return ohci->ctrl_head;
+
+ case 9: /* HcControlCurrentED */
+ return ohci->ctrl_cur;
+
+ case 10: /* HcBulkHeadED */
+ return ohci->bulk_head;
+
+ case 11: /* HcBulkCurrentED */
+ return ohci->bulk_cur;
+
+ case 12: /* HcDoneHead */
+ return ohci->done;
+
+ case 13: /* HcFmInterval */
+ return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
+
+ case 14: /* HcFmRemaining */
+ return ohci_get_frame_remaining(ohci);
+
+ case 15: /* HcFmNumber */
+ return ohci->frame_number;
+
+ case 16: /* HcPeriodicStart */
+ return ohci->pstart;
+
+ case 17: /* HcLSThreshold */
+ return ohci->lst;
+
+ case 18: /* HcRhDescriptorA */
+ return ohci->rhdesc_a;
+
+ case 19: /* HcRhDescriptorB */
+ return ohci->rhdesc_b;
+
+ case 20: /* HcRhStatus */
+ return ohci->rhstatus;
+
+ default:
+ fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+ return 0xffffffff;
+ }
+}
+
+static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ OHCIState *ohci = ptr;
+
+ addr -= ohci->mem_base;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+ return;
+ }
+
+ if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
+ return;
+ }
+
+ switch (addr >> 2) {
+ case 1: /* HcControl */
+ ohci_set_ctl(ohci, val);
+ break;
+
+ case 2: /* HcCommandStatus */
+ /* SOC is read-only */
+ val = (val & ~OHCI_STATUS_SOC);
+
+ /* Bits written as '0' remain unchanged in the register */
+ ohci->status |= val;
+
+ if (ohci->status & OHCI_STATUS_HCR)
+ ohci_reset(ohci);
+ break;
+
+ case 3: /* HcInterruptStatus */
+ ohci->intr_status &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 4: /* HcInterruptEnable */
+ ohci->intr |= val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 5: /* HcInterruptDisable */
+ ohci->intr &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 6: /* HcHCCA */
+ ohci->hcca = val & OHCI_HCCA_MASK;
+ break;
+
+ case 8: /* HcControlHeadED */
+ ohci->ctrl_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 9: /* HcControlCurrentED */
+ ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 10: /* HcBulkHeadED */
+ ohci->bulk_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 11: /* HcBulkCurrentED */
+ ohci->bulk_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 13: /* HcFmInterval */
+ ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
+ ohci->fit = (val & OHCI_FMI_FIT) >> 31;
+ ohci_set_frame_interval(ohci, val);
+ break;
+
+ case 16: /* HcPeriodicStart */
+ ohci->pstart = val & 0xffff;
+ break;
+
+ case 17: /* HcLSThreshold */
+ ohci->lst = val & 0xffff;
+ break;
+
+ case 18: /* HcRhDescriptorA */
+ ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
+ ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
+ break;
+
+ case 19: /* HcRhDescriptorB */
+ break;
+
+ case 20: /* HcRhStatus */
+ ohci_set_hub_status(ohci, val);
+ break;
+
+ default:
+ fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+ break;
+ }
+}
+
+/* Only dword reads are defined on OHCI register space */
+static CPUReadMemoryFunc *ohci_readfn[3]={
+ ohci_mem_read,
+ ohci_mem_read,
+ ohci_mem_read
+};
+
+/* Only dword writes are defined on OHCI register space */
+static CPUWriteMemoryFunc *ohci_writefn[3]={
+ ohci_mem_write,
+ ohci_mem_write,
+ ohci_mem_write
+};
+
+static void ohci_mapfunc(PCIDevice *pci_dev, int i,
+ uint32_t addr, uint32_t size, int type)
+{
+ OHCIState *ohci = (OHCIState *)pci_dev;
+ ohci->mem_base = addr;
+ cpu_register_physical_memory(addr, size, ohci->mem);
+}
+
+void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn)
+{
+ OHCIState *ohci;
+ int vid = 0x106b;
+ int did = 0x003f;
+ int i;
+
+
+ if (usb_frame_time == 0) {
+#if OHCI_TIME_WARP
+ usb_frame_time = ticks_per_sec;
+ usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000);
+#else
+ usb_frame_time = muldiv64(1, ticks_per_sec, 1000);
+ if (ticks_per_sec >= USB_HZ) {
+ usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ);
+ } else {
+ usb_bit_time = 1;
+ }
+#endif
+ dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n",
+ usb_frame_time, usb_bit_time);
+ }
+
+ ohci = (OHCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci),
+ devfn, NULL, NULL);
+ if (ohci == NULL) {
+ fprintf(stderr, "usb-ohci: Failed to register PCI device\n");
+ return;
+ }
+
+ ohci->pci_dev.config[0x00] = vid & 0xff;
+ ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff;
+ ohci->pci_dev.config[0x02] = did & 0xff;
+ ohci->pci_dev.config[0x03] = (did >> 8) & 0xff;
+ ohci->pci_dev.config[0x09] = 0x10; /* OHCI */
+ ohci->pci_dev.config[0x0a] = 0x3;
+ ohci->pci_dev.config[0x0b] = 0xc;
+ ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
+
+ ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci);
+
+ pci_register_io_region((struct PCIDevice *)ohci, 0, 256,
+ PCI_ADDRESS_SPACE_MEM, ohci_mapfunc);
+
+ ohci->num_ports = num_ports;
+ for (i = 0; i < num_ports; i++) {
+ qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach);
+ }
+
+ ohci_reset(ohci);
+}
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
new file mode 100644
index 0000000..1a6e013
--- /dev/null
+++ b/hw/usb-uhci.c
@@ -0,0 +1,674 @@
+/*
+ * USB UHCI controller emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG
+//#define DEBUG_PACKET
+
+#define UHCI_CMD_GRESET (1 << 2)
+#define UHCI_CMD_HCRESET (1 << 1)
+#define UHCI_CMD_RS (1 << 0)
+
+#define UHCI_STS_HCHALTED (1 << 5)
+#define UHCI_STS_HCPERR (1 << 4)
+#define UHCI_STS_HSERR (1 << 3)
+#define UHCI_STS_RD (1 << 2)
+#define UHCI_STS_USBERR (1 << 1)
+#define UHCI_STS_USBINT (1 << 0)
+
+#define TD_CTRL_SPD (1 << 29)
+#define TD_CTRL_ERROR_SHIFT 27
+#define TD_CTRL_IOS (1 << 25)
+#define TD_CTRL_IOC (1 << 24)
+#define TD_CTRL_ACTIVE (1 << 23)
+#define TD_CTRL_STALL (1 << 22)
+#define TD_CTRL_BABBLE (1 << 20)
+#define TD_CTRL_NAK (1 << 19)
+#define TD_CTRL_TIMEOUT (1 << 18)
+
+#define UHCI_PORT_RESET (1 << 9)
+#define UHCI_PORT_LSDA (1 << 8)
+#define UHCI_PORT_ENC (1 << 3)
+#define UHCI_PORT_EN (1 << 2)
+#define UHCI_PORT_CSC (1 << 1)
+#define UHCI_PORT_CCS (1 << 0)
+
+#define FRAME_TIMER_FREQ 1000
+
+#define FRAME_MAX_LOOPS 100
+
+#define NB_PORTS 2
+
+typedef struct UHCIPort {
+ USBPort port;
+ uint16_t ctrl;
+} UHCIPort;
+
+typedef struct UHCIState {
+ PCIDevice dev;
+ uint16_t cmd; /* cmd register */
+ uint16_t status;
+ uint16_t intr; /* interrupt enable register */
+ uint16_t frnum; /* frame number */
+ uint32_t fl_base_addr; /* frame list base address */
+ uint8_t sof_timing;
+ uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
+ QEMUTimer *frame_timer;
+ UHCIPort ports[NB_PORTS];
+} UHCIState;
+
+typedef struct UHCI_TD {
+ uint32_t link;
+ uint32_t ctrl; /* see TD_CTRL_xxx */
+ uint32_t token;
+ uint32_t buffer;
+} UHCI_TD;
+
+typedef struct UHCI_QH {
+ uint32_t link;
+ uint32_t el_link;
+} UHCI_QH;
+
+static void uhci_attach(USBPort *port1, USBDevice *dev);
+
+static void uhci_update_irq(UHCIState *s)
+{
+ int level;
+ if (((s->status2 & 1) && (s->intr & (1 << 2))) ||
+ ((s->status2 & 2) && (s->intr & (1 << 3))) ||
+ ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) ||
+ ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) ||
+ (s->status & UHCI_STS_HSERR) ||
+ (s->status & UHCI_STS_HCPERR)) {
+ level = 1;
+ } else {
+ level = 0;
+ }
+ pci_set_irq(&s->dev, 3, level);
+}
+
+static void uhci_reset(UHCIState *s)
+{
+ uint8_t *pci_conf;
+ int i;
+ UHCIPort *port;
+
+ pci_conf = s->dev.config;
+
+ pci_conf[0x6a] = 0x01; /* usb clock */
+ pci_conf[0x6b] = 0x00;
+ s->cmd = 0;
+ s->status = 0;
+ s->status2 = 0;
+ s->intr = 0;
+ s->fl_base_addr = 0;
+ s->sof_timing = 64;
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &s->ports[i];
+ port->ctrl = 0x0080;
+ if (port->port.dev)
+ uhci_attach(&port->port, port->port.dev);
+ }
+}
+
+static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x0c:
+ s->sof_timing = val;
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x0c:
+ val = s->sof_timing;
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+ return val;
+}
+
+static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+#ifdef DEBUG
+ printf("uhci writew port=0x%04x val=0x%04x\n", addr, val);
+#endif
+ switch(addr) {
+ case 0x00:
+ if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
+ /* start frame processing */
+ qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock));
+ s->status &= ~UHCI_STS_HCHALTED;
+ } else if (!(val & UHCI_CMD_RS)) {
+ s->status |= UHCI_STS_HCHALTED;
+ }
+ if (val & UHCI_CMD_GRESET) {
+ UHCIPort *port;
+ USBDevice *dev;
+ int i;
+
+ /* send reset on the USB bus */
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev) {
+ dev->handle_packet(dev,
+ USB_MSG_RESET, 0, 0, NULL, 0);
+ }
+ }
+ uhci_reset(s);
+ return;
+ }
+ if (val & UHCI_CMD_HCRESET) {
+ uhci_reset(s);
+ return;
+ }
+ s->cmd = val;
+ break;
+ case 0x02:
+ s->status &= ~val;
+ /* XXX: the chip spec is not coherent, so we add a hidden
+ register to distinguish between IOC and SPD */
+ if (val & UHCI_STS_USBINT)
+ s->status2 = 0;
+ uhci_update_irq(s);
+ break;
+ case 0x04:
+ s->intr = val;
+ uhci_update_irq(s);
+ break;
+ case 0x06:
+ if (s->status & UHCI_STS_HCHALTED)
+ s->frnum = val & 0x7ff;
+ break;
+ case 0x10 ... 0x1f:
+ {
+ UHCIPort *port;
+ USBDevice *dev;
+ int n;
+
+ n = (addr >> 1) & 7;
+ if (n >= NB_PORTS)
+ return;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ if (dev) {
+ /* port reset */
+ if ( (val & UHCI_PORT_RESET) &&
+ !(port->ctrl & UHCI_PORT_RESET) ) {
+ dev->handle_packet(dev,
+ USB_MSG_RESET, 0, 0, NULL, 0);
+ }
+ }
+ port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb);
+ /* some bits are reset when a '1' is written to them */
+ port->ctrl &= ~(val & 0x000a);
+ }
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x00:
+ val = s->cmd;
+ break;
+ case 0x02:
+ val = s->status;
+ break;
+ case 0x04:
+ val = s->intr;
+ break;
+ case 0x06:
+ val = s->frnum;
+ break;
+ case 0x10 ... 0x1f:
+ {
+ UHCIPort *port;
+ int n;
+ n = (addr >> 1) & 7;
+ if (n >= NB_PORTS)
+ goto read_default;
+ port = &s->ports[n];
+ val = port->ctrl;
+ }
+ break;
+ default:
+ read_default:
+ val = 0xff7f; /* disabled port */
+ break;
+ }
+#ifdef DEBUG
+ printf("uhci readw port=0x%04x val=0x%04x\n", addr, val);
+#endif
+ return val;
+}
+
+static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+#ifdef DEBUG
+ printf("uhci writel port=0x%04x val=0x%08x\n", addr, val);
+#endif
+ switch(addr) {
+ case 0x08:
+ s->fl_base_addr = val & ~0xfff;
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x08:
+ val = s->fl_base_addr;
+ break;
+ default:
+ val = 0xffffffff;
+ break;
+ }
+ return val;
+}
+
+static void uhci_attach(USBPort *port1, USBDevice *dev)
+{
+ UHCIState *s = port1->opaque;
+ UHCIPort *port = &s->ports[port1->index];
+
+ if (dev) {
+ if (port->port.dev) {
+ usb_attach(port1, NULL);
+ }
+ /* set connect status */
+ port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
+
+ /* update speed */
+ if (dev->speed == USB_SPEED_LOW)
+ port->ctrl |= UHCI_PORT_LSDA;
+ else
+ port->ctrl &= ~UHCI_PORT_LSDA;
+ port->port.dev = dev;
+ /* send the attach message */
+ dev->handle_packet(dev,
+ USB_MSG_ATTACH, 0, 0, NULL, 0);
+ } else {
+ /* set connect status */
+ if (port->ctrl & UHCI_PORT_CCS) {
+ port->ctrl &= ~UHCI_PORT_CCS;
+ port->ctrl |= UHCI_PORT_CSC;
+ }
+ /* disable port */
+ if (port->ctrl & UHCI_PORT_EN) {
+ port->ctrl &= ~UHCI_PORT_EN;
+ port->ctrl |= UHCI_PORT_ENC;
+ }
+ dev = port->port.dev;
+ if (dev) {
+ /* send the detach message */
+ dev->handle_packet(dev,
+ USB_MSG_DETACH, 0, 0, NULL, 0);
+ }
+ port->port.dev = NULL;
+ }
+}
+
+static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len)
+{
+ UHCIPort *port;
+ USBDevice *dev;
+ int i, ret;
+
+#ifdef DEBUG_PACKET
+ {
+ const char *pidstr;
+ switch(pid) {
+ case USB_TOKEN_SETUP: pidstr = "SETUP"; break;
+ case USB_TOKEN_IN: pidstr = "IN"; break;
+ case USB_TOKEN_OUT: pidstr = "OUT"; break;
+ default: pidstr = "?"; break;
+ }
+ printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n",
+ s->frnum, pidstr, devaddr, devep, len);
+ if (pid != USB_TOKEN_IN) {
+ printf(" data_out=");
+ for(i = 0; i < len; i++) {
+ printf(" %02x", data[i]);
+ }
+ printf("\n");
+ }
+ }
+#endif
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev && (port->ctrl & UHCI_PORT_EN)) {
+ ret = dev->handle_packet(dev, pid,
+ devaddr, devep,
+ data, len);
+ if (ret != USB_RET_NODEV) {
+#ifdef DEBUG_PACKET
+ {
+ printf(" ret=%d ", ret);
+ if (pid == USB_TOKEN_IN && ret > 0) {
+ printf("data_in=");
+ for(i = 0; i < ret; i++) {
+ printf(" %02x", data[i]);
+ }
+ }
+ printf("\n");
+ }
+#endif
+ return ret;
+ }
+ }
+ }
+ return USB_RET_NODEV;
+}
+
+/* return -1 if fatal error (frame must be stopped)
+ 0 if TD successful
+ 1 if TD unsuccessful or inactive
+*/
+static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask)
+{
+ uint8_t pid;
+ uint8_t buf[1280];
+ int len, max_len, err, ret;
+
+ if (td->ctrl & TD_CTRL_IOC) {
+ *int_mask |= 0x01;
+ }
+
+ if (!(td->ctrl & TD_CTRL_ACTIVE))
+ return 1;
+
+ /* TD is active */
+ max_len = ((td->token >> 21) + 1) & 0x7ff;
+ pid = td->token & 0xff;
+ switch(pid) {
+ case USB_TOKEN_OUT:
+ case USB_TOKEN_SETUP:
+ cpu_physical_memory_read(td->buffer, buf, max_len);
+ ret = uhci_broadcast_packet(s, pid,
+ (td->token >> 8) & 0x7f,
+ (td->token >> 15) & 0xf,
+ buf, max_len);
+ len = max_len;
+ break;
+ case USB_TOKEN_IN:
+ ret = uhci_broadcast_packet(s, pid,
+ (td->token >> 8) & 0x7f,
+ (td->token >> 15) & 0xf,
+ buf, max_len);
+ if (ret >= 0) {
+ len = ret;
+ if (len > max_len) {
+ len = max_len;
+ ret = USB_RET_BABBLE;
+ }
+ if (len > 0) {
+ /* write the data back */
+ cpu_physical_memory_write(td->buffer, buf, len);
+ }
+ } else {
+ len = 0;
+ }
+ break;
+ default:
+ /* invalid pid : frame interrupted */
+ s->status |= UHCI_STS_HCPERR;
+ uhci_update_irq(s);
+ return -1;
+ }
+ if (td->ctrl & TD_CTRL_IOS)
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ if (ret >= 0) {
+ td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ if (pid == USB_TOKEN_IN &&
+ (td->ctrl & TD_CTRL_SPD) &&
+ len < max_len) {
+ *int_mask |= 0x02;
+ /* short packet: do not update QH */
+ return 1;
+ } else {
+ /* success */
+ return 0;
+ }
+ } else {
+ switch(ret) {
+ default:
+ case USB_RET_NODEV:
+ do_timeout:
+ td->ctrl |= TD_CTRL_TIMEOUT;
+ err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
+ if (err != 0) {
+ err--;
+ if (err == 0) {
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ s->status |= UHCI_STS_USBERR;
+ uhci_update_irq(s);
+ }
+ }
+ td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
+ (err << TD_CTRL_ERROR_SHIFT);
+ return 1;
+ case USB_RET_NAK:
+ td->ctrl |= TD_CTRL_NAK;
+ if (pid == USB_TOKEN_SETUP)
+ goto do_timeout;
+ return 1;
+ case USB_RET_STALL:
+ td->ctrl |= TD_CTRL_STALL;
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ return 1;
+ case USB_RET_BABBLE:
+ td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ /* frame interrupted */
+ return -1;
+ }
+ }
+}
+
+static void uhci_frame_timer(void *opaque)
+{
+ UHCIState *s = opaque;
+ int64_t expire_time;
+ uint32_t frame_addr, link, old_td_ctrl, val;
+ int int_mask, cnt, ret;
+ UHCI_TD td;
+ UHCI_QH qh;
+
+ if (!(s->cmd & UHCI_CMD_RS)) {
+ qemu_del_timer(s->frame_timer);
+ /* set hchalted bit in status - UHCI11D 2.1.2 */
+ s->status |= UHCI_STS_HCHALTED;
+ return;
+ }
+ frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
+ cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4);
+ le32_to_cpus(&link);
+ int_mask = 0;
+ cnt = FRAME_MAX_LOOPS;
+ while ((link & 1) == 0) {
+ if (--cnt == 0)
+ break;
+ /* valid frame */
+ if (link & 2) {
+ /* QH */
+ cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh));
+ le32_to_cpus(&qh.link);
+ le32_to_cpus(&qh.el_link);
+ depth_first:
+ if (qh.el_link & 1) {
+ /* no element : go to next entry */
+ link = qh.link;
+ } else if (qh.el_link & 2) {
+ /* QH */
+ link = qh.el_link;
+ } else {
+ /* TD */
+ if (--cnt == 0)
+ break;
+ cpu_physical_memory_read(qh.el_link & ~0xf,
+ (uint8_t *)&td, sizeof(td));
+ le32_to_cpus(&td.link);
+ le32_to_cpus(&td.ctrl);
+ le32_to_cpus(&td.token);
+ le32_to_cpus(&td.buffer);
+ old_td_ctrl = td.ctrl;
+ ret = uhci_handle_td(s, &td, &int_mask);
+ /* update the status bits of the TD */
+ if (old_td_ctrl != td.ctrl) {
+ val = cpu_to_le32(td.ctrl);
+ cpu_physical_memory_write((qh.el_link & ~0xf) + 4,
+ (const uint8_t *)&val,
+ sizeof(val));
+ }
+ if (ret < 0)
+ break; /* interrupted frame */
+ if (ret == 0) {
+ /* update qh element link */
+ qh.el_link = td.link;
+ val = cpu_to_le32(qh.el_link);
+ cpu_physical_memory_write((link & ~0xf) + 4,
+ (const uint8_t *)&val,
+ sizeof(val));
+ if (qh.el_link & 4) {
+ /* depth first */
+ goto depth_first;
+ }
+ }
+ /* go to next entry */
+ link = qh.link;
+ }
+ } else {
+ /* TD */
+ cpu_physical_memory_read(link & ~0xf, (uint8_t *)&td, sizeof(td));
+ le32_to_cpus(&td.link);
+ le32_to_cpus(&td.ctrl);
+ le32_to_cpus(&td.token);
+ le32_to_cpus(&td.buffer);
+ old_td_ctrl = td.ctrl;
+ ret = uhci_handle_td(s, &td, &int_mask);
+ /* update the status bits of the TD */
+ if (old_td_ctrl != td.ctrl) {
+ val = cpu_to_le32(td.ctrl);
+ cpu_physical_memory_write((link & ~0xf) + 4,
+ (const uint8_t *)&val,
+ sizeof(val));
+ }
+ if (ret < 0)
+ break; /* interrupted frame */
+ link = td.link;
+ }
+ }
+ s->frnum = (s->frnum + 1) & 0x7ff;
+ if (int_mask) {
+ s->status2 |= int_mask;
+ s->status |= UHCI_STS_USBINT;
+ uhci_update_irq(s);
+ }
+ /* prepare the timer for the next frame */
+ expire_time = qemu_get_clock(vm_clock) +
+ (ticks_per_sec / FRAME_TIMER_FREQ);
+ qemu_mod_timer(s->frame_timer, expire_time);
+}
+
+static void uhci_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ UHCIState *s = (UHCIState *)pci_dev;
+
+ register_ioport_write(addr, 32, 2, uhci_ioport_writew, s);
+ register_ioport_read(addr, 32, 2, uhci_ioport_readw, s);
+ register_ioport_write(addr, 32, 4, uhci_ioport_writel, s);
+ register_ioport_read(addr, 32, 4, uhci_ioport_readl, s);
+ register_ioport_write(addr, 32, 1, uhci_ioport_writeb, s);
+ register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
+}
+
+void usb_uhci_init(PCIBus *bus, int devfn)
+{
+ UHCIState *s;
+ uint8_t *pci_conf;
+ int i;
+
+ s = (UHCIState *)pci_register_device(bus,
+ "USB-UHCI", sizeof(UHCIState),
+ devfn, NULL, NULL);
+ pci_conf = s->dev.config;
+ pci_conf[0x00] = 0x86;
+ pci_conf[0x01] = 0x80;
+ pci_conf[0x02] = 0x20;
+ pci_conf[0x03] = 0x70;
+ pci_conf[0x08] = 0x01; // revision number
+ pci_conf[0x09] = 0x00;
+ pci_conf[0x0a] = 0x03;
+ pci_conf[0x0b] = 0x0c;
+ pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x3d] = 4; // interrupt pin 3
+ pci_conf[0x60] = 0x10; // release number
+
+ for(i = 0; i < NB_PORTS; i++) {
+ qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach);
+ }
+ s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
+
+ uhci_reset(s);
+
+ /* Use region 4 for consistency with real hardware. BSD guests seem
+ to rely on this. */
+ pci_register_io_region(&s->dev, 4, 0x20,
+ PCI_ADDRESS_SPACE_IO, uhci_map);
+}
diff --git a/hw/usb.c b/hw/usb.c
new file mode 100644
index 0000000..34aac5f
--- /dev/null
+++ b/hw/usb.c
@@ -0,0 +1,193 @@
+/*
+ * QEMU USB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+void usb_attach(USBPort *port, USBDevice *dev)
+{
+ port->attach(port, dev);
+}
+
+/**********************/
+/* generic USB device helpers (you are not forced to use them when
+ writing your USB device driver, but they help handling the
+ protocol)
+*/
+
+#define SETUP_STATE_IDLE 0
+#define SETUP_STATE_DATA 1
+#define SETUP_STATE_ACK 2
+
+int usb_generic_handle_packet(USBDevice *s, int pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len)
+{
+ int l, ret = 0;
+
+ switch(pid) {
+ case USB_MSG_ATTACH:
+ s->state = USB_STATE_ATTACHED;
+ break;
+ case USB_MSG_DETACH:
+ s->state = USB_STATE_NOTATTACHED;
+ break;
+ case USB_MSG_RESET:
+ s->remote_wakeup = 0;
+ s->addr = 0;
+ s->state = USB_STATE_DEFAULT;
+ s->handle_reset(s);
+ break;
+ case USB_TOKEN_SETUP:
+ if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
+ return USB_RET_NODEV;
+ if (len != 8)
+ goto fail;
+ memcpy(s->setup_buf, data, 8);
+ s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
+ s->setup_index = 0;
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ ret = s->handle_control(s,
+ (s->setup_buf[0] << 8) | s->setup_buf[1],
+ (s->setup_buf[3] << 8) | s->setup_buf[2],
+ (s->setup_buf[5] << 8) | s->setup_buf[4],
+ s->setup_len,
+ s->data_buf);
+ if (ret < 0)
+ return ret;
+ if (ret < s->setup_len)
+ s->setup_len = ret;
+ s->setup_state = SETUP_STATE_DATA;
+ } else {
+ if (s->setup_len == 0)
+ s->setup_state = SETUP_STATE_ACK;
+ else
+ s->setup_state = SETUP_STATE_DATA;
+ }
+ break;
+ case USB_TOKEN_IN:
+ if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
+ return USB_RET_NODEV;
+ switch(devep) {
+ case 0:
+ switch(s->setup_state) {
+ case SETUP_STATE_ACK:
+ if (!(s->setup_buf[0] & USB_DIR_IN)) {
+ s->setup_state = SETUP_STATE_IDLE;
+ ret = s->handle_control(s,
+ (s->setup_buf[0] << 8) | s->setup_buf[1],
+ (s->setup_buf[3] << 8) | s->setup_buf[2],
+ (s->setup_buf[5] << 8) | s->setup_buf[4],
+ s->setup_len,
+ s->data_buf);
+ if (ret > 0)
+ ret = 0;
+ } else {
+ /* return 0 byte */
+ }
+ break;
+ case SETUP_STATE_DATA:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ l = s->setup_len - s->setup_index;
+ if (l > len)
+ l = len;
+ memcpy(data, s->data_buf + s->setup_index, l);
+ s->setup_index += l;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ ret = l;
+ } else {
+ s->setup_state = SETUP_STATE_IDLE;
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ ret = s->handle_data(s, pid, devep, data, len);
+ break;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
+ return USB_RET_NODEV;
+ switch(devep) {
+ case 0:
+ switch(s->setup_state) {
+ case SETUP_STATE_ACK:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ s->setup_state = SETUP_STATE_IDLE;
+ /* transfer OK */
+ } else {
+ /* ignore additionnal output */
+ }
+ break;
+ case SETUP_STATE_DATA:
+ if (!(s->setup_buf[0] & USB_DIR_IN)) {
+ l = s->setup_len - s->setup_index;
+ if (l > len)
+ l = len;
+ memcpy(s->data_buf + s->setup_index, data, l);
+ s->setup_index += l;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ ret = l;
+ } else {
+ s->setup_state = SETUP_STATE_IDLE;
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ ret = s->handle_data(s, pid, devep, data, len);
+ break;
+ }
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+/* XXX: fix overflow */
+int set_usb_string(uint8_t *buf, const char *str)
+{
+ int len, i;
+ uint8_t *q;
+
+ q = buf;
+ len = strlen(str);
+ *q++ = 2 * len + 2;
+ *q++ = 3;
+ for(i = 0; i < len; i++) {
+ *q++ = str[i];
+ *q++ = 0;
+ }
+ return q - buf;
+}
diff --git a/hw/usb.h b/hw/usb.h
new file mode 100644
index 0000000..98fde06
--- /dev/null
+++ b/hw/usb.h
@@ -0,0 +1,178 @@
+/*
+ * QEMU USB API
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define USB_TOKEN_SETUP 0x2d
+#define USB_TOKEN_IN 0x69 /* device -> host */
+#define USB_TOKEN_OUT 0xe1 /* host -> device */
+
+/* specific usb messages, also sent in the 'pid' parameter */
+#define USB_MSG_ATTACH 0x100
+#define USB_MSG_DETACH 0x101
+#define USB_MSG_RESET 0x102
+
+#define USB_RET_NODEV (-1)
+#define USB_RET_NAK (-2)
+#define USB_RET_STALL (-3)
+#define USB_RET_BABBLE (-4)
+
+#define USB_SPEED_LOW 0
+#define USB_SPEED_FULL 1
+#define USB_SPEED_HIGH 2
+
+#define USB_STATE_NOTATTACHED 0
+#define USB_STATE_ATTACHED 1
+//#define USB_STATE_POWERED 2
+#define USB_STATE_DEFAULT 3
+//#define USB_STATE_ADDRESS 4
+//#define USB_STATE_CONFIGURED 5
+#define USB_STATE_SUSPENDED 6
+
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_STILL_IMAGE 6
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_CDC_DATA 0x0a
+#define USB_CLASS_CSCID 0x0b
+#define USB_CLASS_CONTENT_SEC 0x0d
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+#define USB_DIR_OUT 0
+#define USB_DIR_IN 0x80
+
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define InterfaceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+#define USB_DEVICE_SELF_POWERED 0
+#define USB_DEVICE_REMOTE_WAKEUP 1
+
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+
+typedef struct USBPort USBPort;
+typedef struct USBDevice USBDevice;
+
+/* definition of a USB device */
+struct USBDevice {
+ void *opaque;
+ int (*handle_packet)(USBDevice *dev, int pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len);
+ void (*handle_destroy)(USBDevice *dev);
+
+ int speed;
+
+ /* The following fields are used by the generic USB device
+ layer. They are here just to avoid creating a new structure for
+ them. */
+ void (*handle_reset)(USBDevice *dev);
+ int (*handle_control)(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data);
+ int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
+ uint8_t *data, int len);
+ uint8_t addr;
+ char devname[32];
+
+ int state;
+ uint8_t setup_buf[8];
+ uint8_t data_buf[1024];
+ int remote_wakeup;
+ int setup_state;
+ int setup_len;
+ int setup_index;
+};
+
+typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev);
+
+/* USB port on which a device can be connected */
+struct USBPort {
+ USBDevice *dev;
+ usb_attachfn attach;
+ void *opaque;
+ int index; /* internal port index, may be used with the opaque */
+ struct USBPort *next; /* Used internally by qemu. */
+};
+
+void usb_attach(USBPort *port, USBDevice *dev);
+int usb_generic_handle_packet(USBDevice *s, int pid,
+ uint8_t devaddr, uint8_t devep,
+ uint8_t *data, int len);
+int set_usb_string(uint8_t *buf, const char *str);
+
+/* usb hub */
+USBDevice *usb_hub_init(int nb_ports);
+
+/* usb-uhci.c */
+void usb_uhci_init(PCIBus *bus, int devfn);
+
+/* usb-ohci.c */
+void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn);
+
+/* usb-linux.c */
+USBDevice *usb_host_device_open(const char *devname);
+void usb_host_info(void);
+
+/* usb-hid.c */
+USBDevice *usb_mouse_init(void);
+USBDevice *usb_tablet_init(void);
+
+/* usb-msd.c */
+USBDevice *usb_msd_init(const char *filename);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
new file mode 100644
index 0000000..34a4bc6
--- /dev/null
+++ b/hw/versatile_pci.c
@@ -0,0 +1,119 @@
+/*
+ * ARM Versatile/PB PCI host controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "vl.h"
+
+static inline uint32_t vpb_pci_config_addr(target_phys_addr_t addr)
+{
+ return addr & 0xf8ff;
+}
+
+static void pci_vpb_config_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 1);
+}
+
+static void pci_vpb_config_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 2);
+}
+
+static void pci_vpb_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 4);
+}
+
+static uint32_t pci_vpb_config_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 1);
+ return val;
+}
+
+static uint32_t pci_vpb_config_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 2);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t pci_vpb_config_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 4);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+static CPUWriteMemoryFunc *pci_vpb_config_write[] = {
+ &pci_vpb_config_writeb,
+ &pci_vpb_config_writew,
+ &pci_vpb_config_writel,
+};
+
+static CPUReadMemoryFunc *pci_vpb_config_read[] = {
+ &pci_vpb_config_readb,
+ &pci_vpb_config_readw,
+ &pci_vpb_config_readl,
+};
+
+static void pci_vpb_set_irq(PCIDevice *d, void *pic, int irq_num, int level)
+{
+ pic_set_irq_new(pic, 27 + irq_num, level);
+}
+
+PCIBus *pci_vpb_init(void *pic)
+{
+ PCIBus *s;
+ PCIDevice *d;
+ int mem_config;
+
+ s = pci_register_bus(pci_vpb_set_irq, pic, 11 << 3);
+ /* ??? Register memory space. */
+
+ mem_config = cpu_register_io_memory(0, pci_vpb_config_read,
+ pci_vpb_config_write, s);
+ /* Selfconfig area. */
+ cpu_register_physical_memory(0x41000000, 0x10000, mem_config);
+ /* Normal config area. */
+ cpu_register_physical_memory(0x42000000, 0x10000, mem_config);
+
+ d = pci_register_device(s, "Versatile/PB PCI Controller",
+ sizeof(PCIDevice), -1, NULL, NULL);
+ d->config[0x00] = 0xee; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x00; // device_id
+ d->config[0x03] = 0x03;
+ d->config[0x04] = 0x00;
+ d->config[0x05] = 0x00;
+ d->config[0x06] = 0x20;
+ d->config[0x07] = 0x02;
+ d->config[0x08] = 0x00; // revision
+ d->config[0x09] = 0x00; // programming i/f
+ d->config[0x0A] = 0x40; // class_sub = pci host
+ d->config[0x0B] = 0x0b; // class_base = PCI_bridge
+ d->config[0x0D] = 0x10; // latency_timer
+
+ return s;
+}
+
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
new file mode 100644
index 0000000..28ec137
--- /dev/null
+++ b/hw/versatilepb.c
@@ -0,0 +1,473 @@
+/*
+ * ARM Versatile Platform/Application Baseboard System emulation.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define LOCK_VALUE 0xa05f
+
+/* Primary interrupt controller. */
+
+typedef struct vpb_sic_state
+{
+ arm_pic_handler handler;
+ uint32_t base;
+ uint32_t level;
+ uint32_t mask;
+ uint32_t pic_enable;
+ void *parent;
+ int irq;
+} vpb_sic_state;
+
+static void vpb_sic_update(vpb_sic_state *s)
+{
+ uint32_t flags;
+
+ flags = s->level & s->mask;
+ pic_set_irq_new(s->parent, s->irq, flags != 0);
+}
+
+static void vpb_sic_update_pic(vpb_sic_state *s)
+{
+ int i;
+ uint32_t mask;
+
+ for (i = 21; i <= 30; i++) {
+ mask = 1u << i;
+ if (!(s->pic_enable & mask))
+ continue;
+ pic_set_irq_new(s->parent, i, (s->level & mask) != 0);
+ }
+}
+
+static void vpb_sic_set_irq(void *opaque, int irq, int level)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ if (s->pic_enable & (1u << irq))
+ pic_set_irq_new(s->parent, irq, level);
+ vpb_sic_update(s);
+}
+
+static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case 0: /* STATUS */
+ return s->level & s->mask;
+ case 1: /* RAWSTAT */
+ return s->level;
+ case 2: /* ENABLE */
+ return s->mask;
+ case 4: /* SOFTINT */
+ return s->level & 1;
+ case 8: /* PICENABLE */
+ return s->pic_enable;
+ default:
+ printf ("vpb_sic_read: Bad register offset 0x%x\n", offset);
+ return 0;
+ }
+}
+
+static void vpb_sic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ offset -= s->base;
+
+ switch (offset >> 2) {
+ case 2: /* ENSET */
+ s->mask |= value;
+ break;
+ case 3: /* ENCLR */
+ s->mask &= ~value;
+ break;
+ case 4: /* SOFTINTSET */
+ if (value)
+ s->mask |= 1;
+ break;
+ case 5: /* SOFTINTCLR */
+ if (value)
+ s->mask &= ~1u;
+ break;
+ case 8: /* PICENSET */
+ s->pic_enable |= (value & 0x7fe00000);
+ vpb_sic_update_pic(s);
+ break;
+ case 9: /* PICENCLR */
+ s->pic_enable &= ~value;
+ vpb_sic_update_pic(s);
+ break;
+ default:
+ printf ("vpb_sic_write: Bad register offset 0x%x\n", offset);
+ return;
+ }
+ vpb_sic_update(s);
+}
+
+static CPUReadMemoryFunc *vpb_sic_readfn[] = {
+ vpb_sic_read,
+ vpb_sic_read,
+ vpb_sic_read
+};
+
+static CPUWriteMemoryFunc *vpb_sic_writefn[] = {
+ vpb_sic_write,
+ vpb_sic_write,
+ vpb_sic_write
+};
+
+static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq)
+{
+ vpb_sic_state *s;
+ int iomemtype;
+
+ s = (vpb_sic_state *)qemu_mallocz(sizeof(vpb_sic_state));
+ if (!s)
+ return NULL;
+ s->handler = vpb_sic_set_irq;
+ s->base = base;
+ s->parent = parent;
+ s->irq = irq;
+ iomemtype = cpu_register_io_memory(0, vpb_sic_readfn,
+ vpb_sic_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* System controller. */
+
+typedef struct {
+ uint32_t base;
+ uint32_t leds;
+ uint16_t lockval;
+ uint32_t cfgdata1;
+ uint32_t cfgdata2;
+ uint32_t flags;
+ uint32_t nvflags;
+ uint32_t resetlevel;
+} vpb_sys_state;
+
+static uint32_t vpb_sys_read(void *opaque, target_phys_addr_t offset)
+{
+ vpb_sys_state *s = (vpb_sys_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case 0x00: /* ID */
+ return 0x41007004;
+ case 0x04: /* SW */
+ /* General purpose hardware switches.
+ We don't have a useful way of exposing these to the user. */
+ return 0;
+ case 0x08: /* LED */
+ return s->leds;
+ case 0x20: /* LOCK */
+ return s->lockval;
+ case 0x0c: /* OSC0 */
+ case 0x10: /* OSC1 */
+ case 0x14: /* OSC2 */
+ case 0x18: /* OSC3 */
+ case 0x1c: /* OSC4 */
+ case 0x24: /* 100HZ */
+ /* ??? Implement these. */
+ return 0;
+ case 0x28: /* CFGDATA1 */
+ return s->cfgdata1;
+ case 0x2c: /* CFGDATA2 */
+ return s->cfgdata2;
+ case 0x30: /* FLAGS */
+ return s->flags;
+ case 0x38: /* NVFLAGS */
+ return s->nvflags;
+ case 0x40: /* RESETCTL */
+ return s->resetlevel;
+ case 0x44: /* PCICTL */
+ return 1;
+ case 0x48: /* MCI */
+ return 0;
+ case 0x4c: /* FLASH */
+ return 0;
+ case 0x50: /* CLCD */
+ return 0x1000;
+ case 0x54: /* CLCDSER */
+ return 0;
+ case 0x58: /* BOOTCS */
+ return 0;
+ case 0x5c: /* 24MHz */
+ /* ??? not implemented. */
+ return 0;
+ case 0x60: /* MISC */
+ return 0;
+ case 0x64: /* DMAPSR0 */
+ case 0x68: /* DMAPSR1 */
+ case 0x6c: /* DMAPSR2 */
+ case 0x8c: /* OSCRESET0 */
+ case 0x90: /* OSCRESET1 */
+ case 0x94: /* OSCRESET2 */
+ case 0x98: /* OSCRESET3 */
+ case 0x9c: /* OSCRESET4 */
+ case 0xc0: /* SYS_TEST_OSC0 */
+ case 0xc4: /* SYS_TEST_OSC1 */
+ case 0xc8: /* SYS_TEST_OSC2 */
+ case 0xcc: /* SYS_TEST_OSC3 */
+ case 0xd0: /* SYS_TEST_OSC4 */
+ return 0;
+ default:
+ printf ("vpb_sys_read: Bad register offset 0x%x\n", offset);
+ return 0;
+ }
+}
+
+static void vpb_sys_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ vpb_sys_state *s = (vpb_sys_state *)opaque;
+ offset -= s->base;
+
+ switch (offset) {
+ case 0x08: /* LED */
+ s->leds = val;
+ case 0x0c: /* OSC0 */
+ case 0x10: /* OSC1 */
+ case 0x14: /* OSC2 */
+ case 0x18: /* OSC3 */
+ case 0x1c: /* OSC4 */
+ /* ??? */
+ break;
+ case 0x20: /* LOCK */
+ if (val == LOCK_VALUE)
+ s->lockval = val;
+ else
+ s->lockval = val & 0x7fff;
+ break;
+ case 0x28: /* CFGDATA1 */
+ /* ??? Need to implement this. */
+ s->cfgdata1 = val;
+ break;
+ case 0x2c: /* CFGDATA2 */
+ /* ??? Need to implement this. */
+ s->cfgdata2 = val;
+ break;
+ case 0x30: /* FLAGSSET */
+ s->flags |= val;
+ break;
+ case 0x34: /* FLAGSCLR */
+ s->flags &= ~val;
+ break;
+ case 0x38: /* NVFLAGSSET */
+ s->nvflags |= val;
+ break;
+ case 0x3c: /* NVFLAGSCLR */
+ s->nvflags &= ~val;
+ break;
+ case 0x40: /* RESETCTL */
+ if (s->lockval == LOCK_VALUE) {
+ s->resetlevel = val;
+ if (val & 0x100)
+ cpu_abort(cpu_single_env, "Board reset\n");
+ }
+ break;
+ case 0x44: /* PCICTL */
+ /* nothing to do. */
+ break;
+ case 0x4c: /* FLASH */
+ case 0x50: /* CLCD */
+ case 0x54: /* CLCDSER */
+ case 0x64: /* DMAPSR0 */
+ case 0x68: /* DMAPSR1 */
+ case 0x6c: /* DMAPSR2 */
+ case 0x8c: /* OSCRESET0 */
+ case 0x90: /* OSCRESET1 */
+ case 0x94: /* OSCRESET2 */
+ case 0x98: /* OSCRESET3 */
+ case 0x9c: /* OSCRESET4 */
+ break;
+ default:
+ printf ("vpb_sys_write: Bad register offset 0x%x\n", offset);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc *vpb_sys_readfn[] = {
+ vpb_sys_read,
+ vpb_sys_read,
+ vpb_sys_read
+};
+
+static CPUWriteMemoryFunc *vpb_sys_writefn[] = {
+ vpb_sys_write,
+ vpb_sys_write,
+ vpb_sys_write
+};
+
+static vpb_sys_state *vpb_sys_init(uint32_t base)
+{
+ vpb_sys_state *s;
+ int iomemtype;
+
+ s = (vpb_sys_state *)qemu_mallocz(sizeof(vpb_sys_state));
+ if (!s)
+ return NULL;
+ s->base = base;
+ iomemtype = cpu_register_io_memory(0, vpb_sys_readfn,
+ vpb_sys_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ /* ??? Save/restore. */
+ return s;
+}
+
+/* Board init. */
+
+/* The AB and PB boards both use the same core, just with different
+ peripherans and expansion busses. For now we emulate a subset of the
+ PB peripherals and just change the board ID. */
+
+static void versatile_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, int board_id)
+{
+ CPUState *env;
+ void *pic;
+ void *sic;
+ void *scsi_hba;
+ PCIBus *pci_bus;
+ NICInfo *nd;
+ int n;
+ int done_smc = 0;
+
+ env = cpu_init();
+ cpu_arm_set_model(env, ARM_CPUID_ARM926);
+ /* ??? RAM shoud repeat to fill physical memory space. */
+ /* SDRAM at address zero. */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ vpb_sys_init(0x10000000);
+ pic = arm_pic_init_cpu(env);
+ pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ);
+ sic = vpb_sic_init(0x10003000, pic, 31);
+ pl050_init(0x10006000, sic, 3, 0);
+ pl050_init(0x10007000, sic, 4, 1);
+
+ pci_bus = pci_vpb_init(sic);
+ /* The Versatile PCI bridge does not provide access to PCI IO space,
+ so many of the qemu PCI devices are not useable. */
+ for(n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+ if (!nd->model)
+ nd->model = done_smc ? "rtl8139" : "smc91c111";
+ if (strcmp(nd->model, "smc91c111") == 0) {
+ smc91c111_init(nd, 0x10010000, sic, 25);
+ } else {
+ pci_nic_init(pci_bus, nd);
+ }
+ }
+ if (usb_enabled) {
+ usb_ohci_init(pci_bus, 3, -1);
+ }
+ scsi_hba = lsi_scsi_init(pci_bus, -1);
+ for (n = 0; n < MAX_DISKS; n++) {
+ if (bs_table[n]) {
+ lsi_scsi_attach(scsi_hba, bs_table[n], n);
+ }
+ }
+
+ pl011_init(0x101f1000, pic, 12, serial_hds[0]);
+ pl011_init(0x101f2000, pic, 13, serial_hds[1]);
+ pl011_init(0x101f3000, pic, 14, serial_hds[2]);
+ pl011_init(0x10009000, sic, 6, serial_hds[3]);
+
+ pl080_init(0x10130000, pic, 17);
+ sp804_init(0x101e2000, pic, 4);
+ sp804_init(0x101e3000, pic, 5);
+
+ /* The versatile/PB actually has a modified Color LCD controller
+ that includes hardware cursor support from the PL111. */
+ pl110_init(ds, 0x10120000, pic, 16, 1);
+
+ /* Memory map for Versatile/PB: */
+ /* 0x10000000 System registers. */
+ /* 0x10001000 PCI controller config registers. */
+ /* 0x10002000 Serial bus interface. */
+ /* 0x10003000 Secondary interrupt controller. */
+ /* 0x10004000 AACI (audio). */
+ /* 0x10005000 MMCI0. */
+ /* 0x10006000 KMI0 (keyboard). */
+ /* 0x10007000 KMI1 (mouse). */
+ /* 0x10008000 Character LCD Interface. */
+ /* 0x10009000 UART3. */
+ /* 0x1000a000 Smart card 1. */
+ /* 0x1000b000 MMCI1. */
+ /* 0x10010000 Ethernet. */
+ /* 0x10020000 USB. */
+ /* 0x10100000 SSMC. */
+ /* 0x10110000 MPMC. */
+ /* 0x10120000 CLCD Controller. */
+ /* 0x10130000 DMA Controller. */
+ /* 0x10140000 Vectored interrupt controller. */
+ /* 0x101d0000 AHB Monitor Interface. */
+ /* 0x101e0000 System Controller. */
+ /* 0x101e1000 Watchdog Interface. */
+ /* 0x101e2000 Timer 0/1. */
+ /* 0x101e3000 Timer 2/3. */
+ /* 0x101e4000 GPIO port 0. */
+ /* 0x101e5000 GPIO port 1. */
+ /* 0x101e6000 GPIO port 2. */
+ /* 0x101e7000 GPIO port 3. */
+ /* 0x101e8000 RTC. */
+ /* 0x101f0000 Smart card 0. */
+ /* 0x101f1000 UART0. */
+ /* 0x101f2000 UART1. */
+ /* 0x101f3000 UART2. */
+ /* 0x101f4000 SSPI. */
+
+ arm_load_kernel(ram_size, kernel_filename, kernel_cmdline,
+ initrd_filename, board_id);
+}
+
+static void vpb_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ versatile_init(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 0x183);
+}
+
+static void vab_init(int ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ versatile_init(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 0x25e);
+}
+
+QEMUMachine versatilepb_machine = {
+ "versatilepb",
+ "ARM Versatile/PB (ARM926EJ-S)",
+ vpb_init,
+};
+
+QEMUMachine versatileab_machine = {
+ "versatileab",
+ "ARM Versatile/AB (ARM926EJ-S)",
+ vab_init,
+};
diff --git a/hw/vga.c b/hw/vga.c
new file mode 100644
index 0000000..8f748b3
--- /dev/null
+++ b/hw/vga.c
@@ -0,0 +1,1945 @@
+/*
+ * QEMU VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "vga_int.h"
+
+//#define DEBUG_VGA
+//#define DEBUG_VGA_MEM
+//#define DEBUG_VGA_REG
+
+//#define DEBUG_BOCHS_VBE
+
+/* force some bits to zero */
+const uint8_t sr_mask[8] = {
+ (uint8_t)~0xfc,
+ (uint8_t)~0xc2,
+ (uint8_t)~0xf0,
+ (uint8_t)~0xc0,
+ (uint8_t)~0xf1,
+ (uint8_t)~0xff,
+ (uint8_t)~0xff,
+ (uint8_t)~0x00,
+};
+
+const uint8_t gr_mask[16] = {
+ (uint8_t)~0xf0, /* 0x00 */
+ (uint8_t)~0xf0, /* 0x01 */
+ (uint8_t)~0xf0, /* 0x02 */
+ (uint8_t)~0xe0, /* 0x03 */
+ (uint8_t)~0xfc, /* 0x04 */
+ (uint8_t)~0x84, /* 0x05 */
+ (uint8_t)~0xf0, /* 0x06 */
+ (uint8_t)~0xf0, /* 0x07 */
+ (uint8_t)~0x00, /* 0x08 */
+ (uint8_t)~0xff, /* 0x09 */
+ (uint8_t)~0xff, /* 0x0a */
+ (uint8_t)~0xff, /* 0x0b */
+ (uint8_t)~0xff, /* 0x0c */
+ (uint8_t)~0xff, /* 0x0d */
+ (uint8_t)~0xff, /* 0x0e */
+ (uint8_t)~0xff, /* 0x0f */
+};
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef WORDS_BIGENDIAN
+#define PAT(x) cbswap_32(x)
+#else
+#define PAT(x) (x)
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define BIG 1
+#else
+#define BIG 0
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
+#else
+#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
+#endif
+
+static const uint32_t mask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+#undef PAT
+
+#ifdef WORDS_BIGENDIAN
+#define PAT(x) (x)
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+ PAT(0x00000000),
+ PAT(0x0000ffff),
+ PAT(0xffff0000),
+ PAT(0xffffffff),
+};
+
+static uint32_t expand4[256];
+static uint16_t expand2[256];
+static uint8_t expand4to8[16];
+
+VGAState *vga_state;
+int vga_io_memory;
+
+static void vga_screen_dump(void *opaque, const char *filename);
+
+static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
+{
+ VGAState *s = opaque;
+ int val, index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
+ (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
+ val = 0xff;
+ } else {
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ val = s->sr[s->sr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ break;
+ case 0x3c9:
+ val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
+ if (++s->dac_sub_index == 3) {
+ s->dac_sub_index = 0;
+ s->dac_read_index++;
+ }
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ val = s->gr[s->gr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ val = s->cr[s->cr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* just toggle to fool polling */
+ s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
+ val = s->st01;
+ s->ar_flip_flop = 0;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ }
+#if defined(DEBUG_VGA)
+ printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGAState *s = opaque;
+ int index;
+
+ /* check port range access depending on color/monochrome mode */
+ if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
+ (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION)))
+ return;
+
+#ifdef DEBUG_VGA
+ printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch(index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ break;
+ case 0x3c4:
+ s->sr_index = val & 7;
+ break;
+ case 0x3c5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ s->dac_cache[s->dac_sub_index] = val;
+ if (++s->dac_sub_index == 3) {
+ memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
+ s->dac_sub_index = 0;
+ s->dac_write_index++;
+ }
+ break;
+ case 0x3ce:
+ s->gr_index = val & 0x0f;
+ break;
+ case 0x3cf:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ /* handle CR0-7 protection */
+ if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->cr_index == 7)
+ s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
+ return;
+ }
+ switch(s->cr_index) {
+ case 0x01: /* horizontal display end */
+ case 0x07:
+ case 0x09:
+ case 0x0c:
+ case 0x0d:
+ case 0x12: /* veritcal display end */
+ s->cr[s->cr_index] = val;
+ break;
+ default:
+ s->cr[s->cr_index] = val;
+ break;
+ }
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+#ifdef CONFIG_BOCHS_VBE
+static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
+{
+ VGAState *s = opaque;
+ uint32_t val;
+ val = s->vbe_index;
+ return val;
+}
+
+static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
+{
+ VGAState *s = opaque;
+ uint32_t val;
+
+ if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
+ switch(s->vbe_index) {
+ /* XXX: do not hardcode ? */
+ case VBE_DISPI_INDEX_XRES:
+ val = VBE_DISPI_MAX_XRES;
+ break;
+ case VBE_DISPI_INDEX_YRES:
+ val = VBE_DISPI_MAX_YRES;
+ break;
+ case VBE_DISPI_INDEX_BPP:
+ val = VBE_DISPI_MAX_BPP;
+ break;
+ default:
+ val = s->vbe_regs[s->vbe_index];
+ break;
+ }
+ } else {
+ val = s->vbe_regs[s->vbe_index];
+ }
+ } else {
+ val = 0;
+ }
+#ifdef DEBUG_BOCHS_VBE
+ printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+ return val;
+}
+
+static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGAState *s = opaque;
+ s->vbe_index = val;
+}
+
+static void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGAState *s = opaque;
+
+ if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
+#ifdef DEBUG_BOCHS_VBE
+ printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+ switch(s->vbe_index) {
+ case VBE_DISPI_INDEX_ID:
+ if (val == VBE_DISPI_ID0 ||
+ val == VBE_DISPI_ID1 ||
+ val == VBE_DISPI_ID2) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_XRES:
+ if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_YRES:
+ if (val <= VBE_DISPI_MAX_YRES) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_BPP:
+ if (val == 0)
+ val = 8;
+ if (val == 4 || val == 8 || val == 15 ||
+ val == 16 || val == 24 || val == 32) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_BANK:
+ val &= s->vbe_bank_mask;
+ s->vbe_regs[s->vbe_index] = val;
+ s->bank_offset = (val << 16);
+ break;
+ case VBE_DISPI_INDEX_ENABLE:
+ if ((val & VBE_DISPI_ENABLED) &&
+ !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
+ int h, shift_control;
+
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
+ s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
+ s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
+ s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
+ else
+ s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
+ ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ s->vbe_start_addr = 0;
+
+ /* clear the screen (should be done in BIOS) */
+ if (!(val & VBE_DISPI_NOCLEARMEM)) {
+ memset(s->vram_ptr, 0,
+ s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
+ }
+
+ /* we initialize the VGA graphic mode (should be done
+ in BIOS) */
+ s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
+ s->cr[0x17] |= 3; /* no CGA modes */
+ s->cr[0x13] = s->vbe_line_offset >> 3;
+ /* width */
+ s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
+ /* height (only meaningful if < 1024) */
+ h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
+ s->cr[0x12] = h;
+ s->cr[0x07] = (s->cr[0x07] & ~0x42) |
+ ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
+ /* line compare to 1023 */
+ s->cr[0x18] = 0xff;
+ s->cr[0x07] |= 0x10;
+ s->cr[0x09] |= 0x40;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+ shift_control = 0;
+ s->sr[0x01] &= ~8; /* no double line */
+ } else {
+ shift_control = 2;
+ s->sr[4] |= 0x08; /* set chain 4 mode */
+ s->sr[2] |= 0x0f; /* activate all planes */
+ }
+ s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
+ s->cr[0x09] &= ~0x9f; /* no double scan */
+ } else {
+ /* XXX: the bios should do that */
+ s->bank_offset = 0;
+ }
+ s->vbe_regs[s->vbe_index] = val;
+ break;
+ case VBE_DISPI_INDEX_VIRT_WIDTH:
+ {
+ int w, h, line_offset;
+
+ if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
+ return;
+ w = val;
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ line_offset = w >> 1;
+ else
+ line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ h = s->vram_size / line_offset;
+ /* XXX: support weird bochs semantics ? */
+ if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
+ return;
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
+ s->vbe_line_offset = line_offset;
+ }
+ break;
+ case VBE_DISPI_INDEX_X_OFFSET:
+ case VBE_DISPI_INDEX_Y_OFFSET:
+ {
+ int x;
+ s->vbe_regs[s->vbe_index] = val;
+ s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
+ x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ s->vbe_start_addr += x >> 1;
+ else
+ s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ s->vbe_start_addr >>= 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+#endif
+
+/* called for accesses between 0xa0000 and 0xc0000 */
+uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ VGAState *s = opaque;
+ int memory_map_mode, plane;
+ uint32_t ret;
+
+ /* convert to VGA memory offset */
+ memory_map_mode = (s->gr[6] >> 2) & 3;
+ addr &= 0x1ffff;
+ switch(memory_map_mode) {
+ case 0:
+ break;
+ case 1:
+ if (addr >= 0x10000)
+ return 0xff;
+ addr += s->bank_offset;
+ break;
+ case 2:
+ addr -= 0x10000;
+ if (addr >= 0x8000)
+ return 0xff;
+ break;
+ default:
+ case 3:
+ addr -= 0x18000;
+ if (addr >= 0x8000)
+ return 0xff;
+ break;
+ }
+
+ if (s->sr[4] & 0x08) {
+ /* chain 4 mode : simplest access */
+ ret = s->vram_ptr[addr];
+ } else if (s->gr[5] & 0x10) {
+ /* odd/even mode (aka text mode mapping) */
+ plane = (s->gr[4] & 2) | (addr & 1);
+ ret = s->vram_ptr[((addr & ~1) << 1) | plane];
+ } else {
+ /* standard VGA latched access */
+ s->latch = ((uint32_t *)s->vram_ptr)[addr];
+
+ if (!(s->gr[5] & 0x08)) {
+ /* read mode 0 */
+ plane = s->gr[4];
+ ret = GET_PLANE(s->latch, plane);
+ } else {
+ /* read mode 1 */
+ ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
+ ret |= ret >> 16;
+ ret |= ret >> 8;
+ ret = (~ret) & 0xff;
+ }
+ }
+ return ret;
+}
+
+static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = vga_mem_readb(opaque, addr) << 8;
+ v |= vga_mem_readb(opaque, addr + 1);
+#else
+ v = vga_mem_readb(opaque, addr);
+ v |= vga_mem_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = vga_mem_readb(opaque, addr) << 24;
+ v |= vga_mem_readb(opaque, addr + 1) << 16;
+ v |= vga_mem_readb(opaque, addr + 2) << 8;
+ v |= vga_mem_readb(opaque, addr + 3);
+#else
+ v = vga_mem_readb(opaque, addr);
+ v |= vga_mem_readb(opaque, addr + 1) << 8;
+ v |= vga_mem_readb(opaque, addr + 2) << 16;
+ v |= vga_mem_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+/* called for accesses between 0xa0000 and 0xc0000 */
+void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ VGAState *s = opaque;
+ int memory_map_mode, plane, write_mode, b, func_select, mask;
+ uint32_t write_mask, bit_mask, set_mask;
+
+#ifdef DEBUG_VGA_MEM
+ printf("vga: [0x%x] = 0x%02x\n", addr, val);
+#endif
+ /* convert to VGA memory offset */
+ memory_map_mode = (s->gr[6] >> 2) & 3;
+ addr &= 0x1ffff;
+ switch(memory_map_mode) {
+ case 0:
+ break;
+ case 1:
+ if (addr >= 0x10000)
+ return;
+ addr += s->bank_offset;
+ break;
+ case 2:
+ addr -= 0x10000;
+ if (addr >= 0x8000)
+ return;
+ break;
+ default:
+ case 3:
+ addr -= 0x18000;
+ if (addr >= 0x8000)
+ return;
+ break;
+ }
+
+ if (s->sr[4] & 0x08) {
+ /* chain 4 mode : simplest access */
+ plane = addr & 3;
+ mask = (1 << plane);
+ if (s->sr[2] & mask) {
+ s->vram_ptr[addr] = val;
+#ifdef DEBUG_VGA_MEM
+ printf("vga: chain4: [0x%x]\n", addr);
+#endif
+ s->plane_updated |= mask; /* only used to detect font change */
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+ }
+ } else if (s->gr[5] & 0x10) {
+ /* odd/even mode (aka text mode mapping) */
+ plane = (s->gr[4] & 2) | (addr & 1);
+ mask = (1 << plane);
+ if (s->sr[2] & mask) {
+ addr = ((addr & ~1) << 1) | plane;
+ s->vram_ptr[addr] = val;
+#ifdef DEBUG_VGA_MEM
+ printf("vga: odd/even: [0x%x]\n", addr);
+#endif
+ s->plane_updated |= mask; /* only used to detect font change */
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+ }
+ } else {
+ /* standard VGA latched access */
+ write_mode = s->gr[5] & 3;
+ switch(write_mode) {
+ default:
+ case 0:
+ /* rotate */
+ b = s->gr[3] & 7;
+ val = ((val >> b) | (val << (8 - b))) & 0xff;
+ val |= val << 8;
+ val |= val << 16;
+
+ /* apply set/reset mask */
+ set_mask = mask16[s->gr[1]];
+ val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
+ bit_mask = s->gr[8];
+ break;
+ case 1:
+ val = s->latch;
+ goto do_write;
+ case 2:
+ val = mask16[val & 0x0f];
+ bit_mask = s->gr[8];
+ break;
+ case 3:
+ /* rotate */
+ b = s->gr[3] & 7;
+ val = (val >> b) | (val << (8 - b));
+
+ bit_mask = s->gr[8] & val;
+ val = mask16[s->gr[0]];
+ break;
+ }
+
+ /* apply logical operation */
+ func_select = s->gr[3] >> 3;
+ switch(func_select) {
+ case 0:
+ default:
+ /* nothing to do */
+ break;
+ case 1:
+ /* and */
+ val &= s->latch;
+ break;
+ case 2:
+ /* or */
+ val |= s->latch;
+ break;
+ case 3:
+ /* xor */
+ val ^= s->latch;
+ break;
+ }
+
+ /* apply bit mask */
+ bit_mask |= bit_mask << 8;
+ bit_mask |= bit_mask << 16;
+ val = (val & bit_mask) | (s->latch & ~bit_mask);
+
+ do_write:
+ /* mask data according to sr[2] */
+ mask = s->sr[2];
+ s->plane_updated |= mask; /* only used to detect font change */
+ write_mask = mask16[mask];
+ ((uint32_t *)s->vram_ptr)[addr] =
+ (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
+ (val & write_mask);
+#ifdef DEBUG_VGA_MEM
+ printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
+ addr * 4, write_mask, val);
+#endif
+ cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
+ }
+}
+
+static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
+ vga_mem_writeb(opaque, addr + 1, val & 0xff);
+#else
+ vga_mem_writeb(opaque, addr, val & 0xff);
+ vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
+ vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ vga_mem_writeb(opaque, addr + 3, val & 0xff);
+#else
+ vga_mem_writeb(opaque, addr, val & 0xff);
+ vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol);
+typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol, int dup9);
+typedef void vga_draw_line_func(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width);
+
+static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
+}
+
+static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+}
+
+static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
+{
+ return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+}
+
+static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
+{
+ return (r << 16) | (g << 8) | b;
+}
+
+static inline unsigned int rgb_to_pixel32bgr(unsigned int r, unsigned int g, unsigned b)
+{
+ return (b << 16) | (g << 8) | r;
+}
+
+#define DEPTH 8
+#include "vga_template.h"
+
+#define DEPTH 15
+#include "vga_template.h"
+
+#define DEPTH 16
+#include "vga_template.h"
+
+#define DEPTH 32
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 32
+#include "vga_template.h"
+
+static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel8(r, g, b);
+ col |= col << 8;
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel15(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel16(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel32(r, g, b);
+ return col;
+}
+
+static unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel32bgr(r, g, b);
+ return col;
+}
+
+/* return true if the palette was modified */
+static int update_palette16(VGAState *s)
+{
+ int full_update, i;
+ uint32_t v, col, *palette;
+
+ full_update = 0;
+ palette = s->last_palette;
+ for(i = 0; i < 16; i++) {
+ v = s->ar[i];
+ if (s->ar[0x10] & 0x80)
+ v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
+ else
+ v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
+ v = v * 3;
+ col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
+ c6_to_8(s->palette[v + 1]),
+ c6_to_8(s->palette[v + 2]));
+ if (col != palette[i]) {
+ full_update = 1;
+ palette[i] = col;
+ }
+ }
+ return full_update;
+}
+
+/* return true if the palette was modified */
+static int update_palette256(VGAState *s)
+{
+ int full_update, i;
+ uint32_t v, col, *palette;
+
+ full_update = 0;
+ palette = s->last_palette;
+ v = 0;
+ for(i = 0; i < 256; i++) {
+ col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
+ c6_to_8(s->palette[v + 1]),
+ c6_to_8(s->palette[v + 2]));
+ if (col != palette[i]) {
+ full_update = 1;
+ palette[i] = col;
+ }
+ v += 3;
+ }
+ return full_update;
+}
+
+static void vga_get_offsets(VGAState *s,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr)
+{
+ uint32_t start_addr, line_offset;
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ line_offset = s->vbe_line_offset;
+ start_addr = s->vbe_start_addr;
+ } else
+#endif
+ {
+ /* compute line_offset in bytes */
+ line_offset = s->cr[0x13];
+ line_offset <<= 3;
+
+ /* starting address */
+ start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
+ }
+ *pline_offset = line_offset;
+ *pstart_addr = start_addr;
+}
+
+/* update start_addr and line_offset. Return TRUE if modified */
+static int update_basic_params(VGAState *s)
+{
+ int full_update;
+ uint32_t start_addr, line_offset, line_compare;
+
+ full_update = 0;
+
+ s->get_offsets(s, &line_offset, &start_addr);
+ /* line compare */
+ line_compare = s->cr[0x18] |
+ ((s->cr[0x07] & 0x10) << 4) |
+ ((s->cr[0x09] & 0x40) << 3);
+
+ if (line_offset != s->line_offset ||
+ start_addr != s->start_addr ||
+ line_compare != s->line_compare) {
+ s->line_offset = line_offset;
+ s->start_addr = start_addr;
+ s->line_compare = line_compare;
+ full_update = 1;
+ }
+ return full_update;
+}
+
+#define NB_DEPTHS 5
+
+static inline int get_depth_index(DisplayState *s)
+{
+ switch(s->depth) {
+ default:
+ case 8:
+ return 0;
+ case 15:
+ return 1;
+ case 16:
+ return 2;
+ case 32:
+ if (s->bgr)
+ return 4;
+ else
+ return 3;
+ }
+}
+
+static vga_draw_glyph8_func *vga_draw_glyph8_table[NB_DEPTHS] = {
+ vga_draw_glyph8_8,
+ vga_draw_glyph8_16,
+ vga_draw_glyph8_16,
+ vga_draw_glyph8_32,
+ vga_draw_glyph8_32,
+};
+
+static vga_draw_glyph8_func *vga_draw_glyph16_table[NB_DEPTHS] = {
+ vga_draw_glyph16_8,
+ vga_draw_glyph16_16,
+ vga_draw_glyph16_16,
+ vga_draw_glyph16_32,
+ vga_draw_glyph16_32,
+};
+
+static vga_draw_glyph9_func *vga_draw_glyph9_table[NB_DEPTHS] = {
+ vga_draw_glyph9_8,
+ vga_draw_glyph9_16,
+ vga_draw_glyph9_16,
+ vga_draw_glyph9_32,
+ vga_draw_glyph9_32,
+};
+
+static const uint8_t cursor_glyph[32 * 4] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+/*
+ * Text mode update
+ * Missing:
+ * - double scan
+ * - double width
+ * - underline
+ * - flashing
+ */
+static void vga_draw_text(VGAState *s, int full_update)
+{
+ int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
+ int cx_min, cx_max, linesize, x_incr;
+ uint32_t offset, fgcol, bgcol, v, cursor_offset;
+ uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
+ const uint8_t *font_ptr, *font_base[2];
+ int dup9, line_offset, depth_index;
+ uint32_t *palette;
+ uint32_t *ch_attr_ptr;
+ vga_draw_glyph8_func *vga_draw_glyph8;
+ vga_draw_glyph9_func *vga_draw_glyph9;
+
+ full_update |= update_palette16(s);
+ palette = s->last_palette;
+
+ /* compute font data address (in plane 2) */
+ v = s->sr[3];
+ offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
+ if (offset != s->font_offsets[0]) {
+ s->font_offsets[0] = offset;
+ full_update = 1;
+ }
+ font_base[0] = s->vram_ptr + offset;
+
+ offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
+ font_base[1] = s->vram_ptr + offset;
+ if (offset != s->font_offsets[1]) {
+ s->font_offsets[1] = offset;
+ full_update = 1;
+ }
+ if (s->plane_updated & (1 << 2)) {
+ /* if the plane 2 was modified since the last display, it
+ indicates the font may have been modified */
+ s->plane_updated = 0;
+ full_update = 1;
+ }
+ full_update |= update_basic_params(s);
+
+ line_offset = s->line_offset;
+ s1 = s->vram_ptr + (s->start_addr * 4);
+
+ /* total width & height */
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cw = 8;
+ if (!(s->sr[1] & 0x01))
+ cw = 9;
+ if (s->sr[1] & 0x08)
+ cw = 16; /* NOTE: no 18 pixel wide */
+ x_incr = cw * ((s->ds->depth + 7) >> 3);
+ width = (s->cr[0x01] + 1);
+ if (s->cr[0x06] == 100) {
+ /* ugly hack for CGA 160x100x16 - explain me the logic */
+ height = 100;
+ } else {
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+ }
+ if ((height * width) > CH_ATTR_SIZE) {
+ /* better than nothing: exit if transient size is too big */
+ return;
+ }
+
+ if (width != s->last_width || height != s->last_height ||
+ cw != s->last_cw || cheight != s->last_ch) {
+ s->last_scr_width = width * cw;
+ s->last_scr_height = height * cheight;
+ dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
+ s->last_width = width;
+ s->last_height = height;
+ s->last_ch = cheight;
+ s->last_cw = cw;
+ full_update = 1;
+ }
+ cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
+ if (cursor_offset != s->cursor_offset ||
+ s->cr[0xa] != s->cursor_start ||
+ s->cr[0xb] != s->cursor_end) {
+ /* if the cursor position changed, we update the old and new
+ chars */
+ if (s->cursor_offset < CH_ATTR_SIZE)
+ s->last_ch_attr[s->cursor_offset] = -1;
+ if (cursor_offset < CH_ATTR_SIZE)
+ s->last_ch_attr[cursor_offset] = -1;
+ s->cursor_offset = cursor_offset;
+ s->cursor_start = s->cr[0xa];
+ s->cursor_end = s->cr[0xb];
+ }
+ cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
+
+ depth_index = get_depth_index(s->ds);
+ if (cw == 16)
+ vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
+ else
+ vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
+ vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
+
+ dest = s->ds->data;
+ linesize = s->ds->linesize;
+ ch_attr_ptr = s->last_ch_attr;
+ for(cy = 0; cy < height; cy++) {
+ d1 = dest;
+ src = s1;
+ cx_min = width;
+ cx_max = -1;
+ for(cx = 0; cx < width; cx++) {
+ ch_attr = *(uint16_t *)src;
+ if (full_update || ch_attr != *ch_attr_ptr) {
+ if (cx < cx_min)
+ cx_min = cx;
+ if (cx > cx_max)
+ cx_max = cx;
+ *ch_attr_ptr = ch_attr;
+#ifdef WORDS_BIGENDIAN
+ ch = ch_attr >> 8;
+ cattr = ch_attr & 0xff;
+#else
+ ch = ch_attr & 0xff;
+ cattr = ch_attr >> 8;
+#endif
+ font_ptr = font_base[(cattr >> 3) & 1];
+ font_ptr += 32 * 4 * ch;
+ bgcol = palette[cattr >> 4];
+ fgcol = palette[cattr & 0x0f];
+ if (cw != 9) {
+ vga_draw_glyph8(d1, linesize,
+ font_ptr, cheight, fgcol, bgcol);
+ } else {
+ dup9 = 0;
+ if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
+ dup9 = 1;
+ vga_draw_glyph9(d1, linesize,
+ font_ptr, cheight, fgcol, bgcol, dup9);
+ }
+ if (src == cursor_ptr &&
+ !(s->cr[0x0a] & 0x20)) {
+ int line_start, line_last, h;
+ /* draw the cursor */
+ line_start = s->cr[0x0a] & 0x1f;
+ line_last = s->cr[0x0b] & 0x1f;
+ /* XXX: check that */
+ if (line_last > cheight - 1)
+ line_last = cheight - 1;
+ if (line_last >= line_start && line_start < cheight) {
+ h = line_last - line_start + 1;
+ d = d1 + linesize * line_start;
+ if (cw != 9) {
+ vga_draw_glyph8(d, linesize,
+ cursor_glyph, h, fgcol, bgcol);
+ } else {
+ vga_draw_glyph9(d, linesize,
+ cursor_glyph, h, fgcol, bgcol, 1);
+ }
+ }
+ }
+ }
+ d1 += x_incr;
+ src += 4;
+ ch_attr_ptr++;
+ }
+ if (cx_max != -1) {
+ dpy_update(s->ds, cx_min * cw, cy * cheight,
+ (cx_max - cx_min + 1) * cw, cheight);
+ }
+ dest += linesize * cheight;
+ s1 += line_offset;
+ }
+}
+
+enum {
+ VGA_DRAW_LINE2,
+ VGA_DRAW_LINE2D2,
+ VGA_DRAW_LINE4,
+ VGA_DRAW_LINE4D2,
+ VGA_DRAW_LINE8D2,
+ VGA_DRAW_LINE8,
+ VGA_DRAW_LINE15,
+ VGA_DRAW_LINE16,
+ VGA_DRAW_LINE24,
+ VGA_DRAW_LINE32,
+ VGA_DRAW_LINE_NB,
+};
+
+static vga_draw_line_func *vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = {
+ vga_draw_line2_8,
+ vga_draw_line2_16,
+ vga_draw_line2_16,
+ vga_draw_line2_32,
+ vga_draw_line2_32,
+
+ vga_draw_line2d2_8,
+ vga_draw_line2d2_16,
+ vga_draw_line2d2_16,
+ vga_draw_line2d2_32,
+ vga_draw_line2d2_32,
+
+ vga_draw_line4_8,
+ vga_draw_line4_16,
+ vga_draw_line4_16,
+ vga_draw_line4_32,
+ vga_draw_line4_32,
+
+ vga_draw_line4d2_8,
+ vga_draw_line4d2_16,
+ vga_draw_line4d2_16,
+ vga_draw_line4d2_32,
+ vga_draw_line4d2_32,
+
+ vga_draw_line8d2_8,
+ vga_draw_line8d2_16,
+ vga_draw_line8d2_16,
+ vga_draw_line8d2_32,
+ vga_draw_line8d2_32,
+
+ vga_draw_line8_8,
+ vga_draw_line8_16,
+ vga_draw_line8_16,
+ vga_draw_line8_32,
+ vga_draw_line8_32,
+
+ vga_draw_line15_8,
+ vga_draw_line15_15,
+ vga_draw_line15_16,
+ vga_draw_line15_32,
+ vga_draw_line15_32bgr,
+
+ vga_draw_line16_8,
+ vga_draw_line16_15,
+ vga_draw_line16_16,
+ vga_draw_line16_32,
+ vga_draw_line16_32bgr,
+
+ vga_draw_line24_8,
+ vga_draw_line24_15,
+ vga_draw_line24_16,
+ vga_draw_line24_32,
+ vga_draw_line24_32bgr,
+
+ vga_draw_line32_8,
+ vga_draw_line32_15,
+ vga_draw_line32_16,
+ vga_draw_line32_32,
+ vga_draw_line32_32bgr,
+};
+
+typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b);
+
+static rgb_to_pixel_dup_func *rgb_to_pixel_dup_table[NB_DEPTHS] = {
+ rgb_to_pixel8_dup,
+ rgb_to_pixel15_dup,
+ rgb_to_pixel16_dup,
+ rgb_to_pixel32_dup,
+ rgb_to_pixel32bgr_dup,
+};
+
+static int vga_get_bpp(VGAState *s)
+{
+ int ret;
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
+ } else
+#endif
+ {
+ ret = 0;
+ }
+ return ret;
+}
+
+static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
+{
+ int width, height;
+
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ } else
+#endif
+ {
+ width = (s->cr[0x01] + 1) * 8;
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1);
+ }
+ *pwidth = width;
+ *pheight = height;
+}
+
+void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
+{
+ int y;
+ if (y1 >= VGA_MAX_HEIGHT)
+ return;
+ if (y2 >= VGA_MAX_HEIGHT)
+ y2 = VGA_MAX_HEIGHT;
+ for(y = y1; y < y2; y++) {
+ s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
+ }
+}
+
+/*
+ * graphic modes
+ */
+static void vga_draw_graphic(VGAState *s, int full_update)
+{
+ int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
+ int width, height, shift_control, line_offset, page0, page1, bwidth;
+ int disp_width, multi_scan, multi_run;
+ uint8_t *d;
+ uint32_t v, addr1, addr;
+ vga_draw_line_func *vga_draw_line;
+
+ full_update |= update_basic_params(s);
+
+ s->get_resolution(s, &width, &height);
+ disp_width = width;
+
+ shift_control = (s->gr[0x05] >> 5) & 3;
+ double_scan = (s->cr[0x09] >> 7);
+ if (shift_control != 1) {
+ multi_scan = (((s->cr[0x09] & 0x1f) + 1) << double_scan) - 1;
+ } else {
+ /* in CGA modes, multi_scan is ignored */
+ /* XXX: is it correct ? */
+ multi_scan = double_scan;
+ }
+ multi_run = multi_scan;
+ if (shift_control != s->shift_control ||
+ double_scan != s->double_scan) {
+ full_update = 1;
+ s->shift_control = shift_control;
+ s->double_scan = double_scan;
+ }
+
+ if (shift_control == 0) {
+ full_update |= update_palette16(s);
+ if (s->sr[0x01] & 8) {
+ v = VGA_DRAW_LINE4D2;
+ disp_width <<= 1;
+ } else {
+ v = VGA_DRAW_LINE4;
+ }
+ } else if (shift_control == 1) {
+ full_update |= update_palette16(s);
+ if (s->sr[0x01] & 8) {
+ v = VGA_DRAW_LINE2D2;
+ disp_width <<= 1;
+ } else {
+ v = VGA_DRAW_LINE2;
+ }
+ } else {
+ switch(s->get_bpp(s)) {
+ default:
+ case 0:
+ full_update |= update_palette256(s);
+ v = VGA_DRAW_LINE8D2;
+ break;
+ case 8:
+ full_update |= update_palette256(s);
+ v = VGA_DRAW_LINE8;
+ break;
+ case 15:
+ v = VGA_DRAW_LINE15;
+ break;
+ case 16:
+ v = VGA_DRAW_LINE16;
+ break;
+ case 24:
+ v = VGA_DRAW_LINE24;
+ break;
+ case 32:
+ v = VGA_DRAW_LINE32;
+ break;
+ }
+ }
+ vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + get_depth_index(s->ds)];
+
+ if (disp_width != s->last_width ||
+ height != s->last_height) {
+ dpy_resize(s->ds, disp_width, height);
+ s->last_scr_width = disp_width;
+ s->last_scr_height = height;
+ s->last_width = disp_width;
+ s->last_height = height;
+ full_update = 1;
+ }
+ if (s->cursor_invalidate)
+ s->cursor_invalidate(s);
+
+ line_offset = s->line_offset;
+#if 0
+ printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
+ width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]);
+#endif
+ addr1 = (s->start_addr * 4);
+ bwidth = width * 4;
+ y_start = -1;
+ page_min = 0x7fffffff;
+ page_max = -1;
+ d = s->ds->data;
+ linesize = s->ds->linesize;
+ y1 = 0;
+ for(y = 0; y < height; y++) {
+ addr = addr1;
+ if (!(s->cr[0x17] & 1)) {
+ int shift;
+ /* CGA compatibility handling */
+ shift = 14 + ((s->cr[0x17] >> 6) & 1);
+ addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
+ }
+ if (!(s->cr[0x17] & 2)) {
+ addr = (addr & ~0x8000) | ((y1 & 2) << 14);
+ }
+ page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
+ page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
+ update = full_update |
+ cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
+ cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
+ if ((page1 - page0) > TARGET_PAGE_SIZE) {
+ /* if wide line, can use another page */
+ update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+ /* explicit invalidation for the hardware cursor */
+ update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
+ if (update) {
+ if (y_start < 0)
+ y_start = y;
+ if (page0 < page_min)
+ page_min = page0;
+ if (page1 > page_max)
+ page_max = page1;
+ vga_draw_line(s, d, s->vram_ptr + addr, width);
+ if (s->cursor_draw_line)
+ s->cursor_draw_line(s, d, y);
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(s->ds, 0, y_start,
+ disp_width, y - y_start);
+ y_start = -1;
+ }
+ }
+ if (!multi_run) {
+ mask = (s->cr[0x17] & 3) ^ 3;
+ if ((y1 & mask) == mask)
+ addr1 += line_offset;
+ y1++;
+ multi_run = multi_scan;
+ } else {
+ multi_run--;
+ }
+ /* line compare acts on the displayed lines */
+ if (y == s->line_compare)
+ addr1 = 0;
+ d += linesize;
+ }
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(s->ds, 0, y_start,
+ disp_width, y - y_start);
+ }
+ /* reset modified pages */
+ if (page_max != -1) {
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+ memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
+}
+
+static void vga_draw_blank(VGAState *s, int full_update)
+{
+ int i, w, val;
+ uint8_t *d;
+
+ if (!full_update)
+ return;
+ if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
+ return;
+ if (s->ds->depth == 8)
+ val = s->rgb_to_pixel(0, 0, 0);
+ else
+ val = 0;
+ w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
+ d = s->ds->data;
+ for(i = 0; i < s->last_scr_height; i++) {
+ memset(d, val, w);
+ d += s->ds->linesize;
+ }
+ dpy_update(s->ds, 0, 0,
+ s->last_scr_width, s->last_scr_height);
+}
+
+#define GMODE_TEXT 0
+#define GMODE_GRAPH 1
+#define GMODE_BLANK 2
+
+static void vga_update_display(void *opaque)
+{
+ VGAState *s = (VGAState *)opaque;
+ int full_update, graphic_mode;
+
+ if (s->ds->depth == 0) {
+ /* nothing to do */
+ } else {
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
+
+ full_update = 0;
+ if (!(s->ar_index & 0x20)) {
+ graphic_mode = GMODE_BLANK;
+ } else {
+ graphic_mode = s->gr[6] & 1;
+ }
+ if (graphic_mode != s->graphic_mode) {
+ s->graphic_mode = graphic_mode;
+ full_update = 1;
+ }
+ switch(graphic_mode) {
+ case GMODE_TEXT:
+ vga_draw_text(s, full_update);
+ break;
+ case GMODE_GRAPH:
+ vga_draw_graphic(s, full_update);
+ break;
+ case GMODE_BLANK:
+ default:
+ vga_draw_blank(s, full_update);
+ break;
+ }
+ }
+}
+
+/* force a full display refresh */
+static void vga_invalidate_display(void *opaque)
+{
+ VGAState *s = (VGAState *)opaque;
+
+ s->last_width = -1;
+ s->last_height = -1;
+}
+
+static void vga_reset(VGAState *s)
+{
+ memset(s, 0, sizeof(VGAState));
+ s->graphic_mode = -1; /* force full update */
+}
+
+static CPUReadMemoryFunc *vga_mem_read[3] = {
+ vga_mem_readb,
+ vga_mem_readw,
+ vga_mem_readl,
+};
+
+static CPUWriteMemoryFunc *vga_mem_write[3] = {
+ vga_mem_writeb,
+ vga_mem_writew,
+ vga_mem_writel,
+};
+
+static void vga_save(QEMUFile *f, void *opaque)
+{
+ VGAState *s = opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->latch);
+ qemu_put_8s(f, &s->sr_index);
+ qemu_put_buffer(f, s->sr, 8);
+ qemu_put_8s(f, &s->gr_index);
+ qemu_put_buffer(f, s->gr, 16);
+ qemu_put_8s(f, &s->ar_index);
+ qemu_put_buffer(f, s->ar, 21);
+ qemu_put_be32s(f, &s->ar_flip_flop);
+ qemu_put_8s(f, &s->cr_index);
+ qemu_put_buffer(f, s->cr, 256);
+ qemu_put_8s(f, &s->msr);
+ qemu_put_8s(f, &s->fcr);
+ qemu_put_8s(f, &s->st00);
+ qemu_put_8s(f, &s->st01);
+
+ qemu_put_8s(f, &s->dac_state);
+ qemu_put_8s(f, &s->dac_sub_index);
+ qemu_put_8s(f, &s->dac_read_index);
+ qemu_put_8s(f, &s->dac_write_index);
+ qemu_put_buffer(f, s->dac_cache, 3);
+ qemu_put_buffer(f, s->palette, 768);
+
+ qemu_put_be32s(f, &s->bank_offset);
+#ifdef CONFIG_BOCHS_VBE
+ qemu_put_byte(f, 1);
+ qemu_put_be16s(f, &s->vbe_index);
+ for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
+ qemu_put_be16s(f, &s->vbe_regs[i]);
+ qemu_put_be32s(f, &s->vbe_start_addr);
+ qemu_put_be32s(f, &s->vbe_line_offset);
+ qemu_put_be32s(f, &s->vbe_bank_mask);
+#else
+ qemu_put_byte(f, 0);
+#endif
+}
+
+static int vga_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VGAState *s = opaque;
+ int is_vbe, i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->latch);
+ qemu_get_8s(f, &s->sr_index);
+ qemu_get_buffer(f, s->sr, 8);
+ qemu_get_8s(f, &s->gr_index);
+ qemu_get_buffer(f, s->gr, 16);
+ qemu_get_8s(f, &s->ar_index);
+ qemu_get_buffer(f, s->ar, 21);
+ qemu_get_be32s(f, &s->ar_flip_flop);
+ qemu_get_8s(f, &s->cr_index);
+ qemu_get_buffer(f, s->cr, 256);
+ qemu_get_8s(f, &s->msr);
+ qemu_get_8s(f, &s->fcr);
+ qemu_get_8s(f, &s->st00);
+ qemu_get_8s(f, &s->st01);
+
+ qemu_get_8s(f, &s->dac_state);
+ qemu_get_8s(f, &s->dac_sub_index);
+ qemu_get_8s(f, &s->dac_read_index);
+ qemu_get_8s(f, &s->dac_write_index);
+ qemu_get_buffer(f, s->dac_cache, 3);
+ qemu_get_buffer(f, s->palette, 768);
+
+ qemu_get_be32s(f, &s->bank_offset);
+ is_vbe = qemu_get_byte(f);
+#ifdef CONFIG_BOCHS_VBE
+ if (!is_vbe)
+ return -EINVAL;
+ qemu_get_be16s(f, &s->vbe_index);
+ for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
+ qemu_get_be16s(f, &s->vbe_regs[i]);
+ qemu_get_be32s(f, &s->vbe_start_addr);
+ qemu_get_be32s(f, &s->vbe_line_offset);
+ qemu_get_be32s(f, &s->vbe_bank_mask);
+#else
+ if (is_vbe)
+ return -EINVAL;
+#endif
+
+ /* force refresh */
+ s->graphic_mode = -1;
+ return 0;
+}
+
+static void vga_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ VGAState *s = vga_state;
+ if (region_num == PCI_ROM_SLOT) {
+ cpu_register_physical_memory(addr, s->bios_size, s->bios_offset);
+ } else {
+ cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
+ }
+}
+
+void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size)
+{
+ int i, j, v, b;
+
+ for(i = 0;i < 256; i++) {
+ v = 0;
+ for(j = 0; j < 8; j++) {
+ v |= ((i >> j) & 1) << (j * 4);
+ }
+ expand4[i] = v;
+
+ v = 0;
+ for(j = 0; j < 4; j++) {
+ v |= ((i >> (2 * j)) & 3) << (j * 4);
+ }
+ expand2[i] = v;
+ }
+ for(i = 0; i < 16; i++) {
+ v = 0;
+ for(j = 0; j < 4; j++) {
+ b = ((i >> j) & 1);
+ v |= b << (2 * j);
+ v |= b << (2 * j + 1);
+ }
+ expand4to8[i] = v;
+ }
+
+ vga_reset(s);
+
+ s->vram_ptr = vga_ram_base;
+ s->vram_offset = vga_ram_offset;
+ s->vram_size = vga_ram_size;
+ s->ds = ds;
+ s->get_bpp = vga_get_bpp;
+ s->get_offsets = vga_get_offsets;
+ s->get_resolution = vga_get_resolution;
+ graphic_console_init(s->ds, vga_update_display, vga_invalidate_display,
+ vga_screen_dump, s);
+ /* XXX: currently needed for display */
+ vga_state = s;
+}
+
+
+int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size,
+ unsigned long vga_bios_offset, int vga_bios_size)
+{
+ VGAState *s;
+
+ s = qemu_mallocz(sizeof(VGAState));
+ if (!s)
+ return -1;
+
+ vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
+
+ register_savevm("vga", 0, 1, vga_save, vga_load, s);
+
+ register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
+
+ register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
+ register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
+
+ register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
+
+ register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
+ register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
+ s->bank_offset = 0;
+
+#ifdef CONFIG_BOCHS_VBE
+ s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
+ s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
+#if defined (TARGET_I386)
+ register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+ register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
+
+ register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+ register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
+
+ /* old Bochs IO ports */
+ register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
+ register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
+
+ register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
+ register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
+#else
+ register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+ register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
+
+ register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+ register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
+#endif
+#endif /* CONFIG_BOCHS_VBE */
+
+ vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
+ cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+ vga_io_memory);
+
+ if (bus) {
+ PCIDevice *d;
+ uint8_t *pci_conf;
+
+ d = pci_register_device(bus, "VGA",
+ sizeof(PCIDevice),
+ -1, NULL, NULL);
+ pci_conf = d->config;
+ pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
+ pci_conf[0x01] = 0x12;
+ pci_conf[0x02] = 0x11;
+ pci_conf[0x03] = 0x11;
+ pci_conf[0x0a] = 0x00; // VGA controller
+ pci_conf[0x0b] = 0x03;
+ pci_conf[0x0e] = 0x00; // header_type
+
+ /* XXX: vga_ram_size must be a power of two */
+ pci_register_io_region(d, 0, vga_ram_size,
+ PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
+ if (vga_bios_size != 0) {
+ unsigned int bios_total_size;
+ s->bios_offset = vga_bios_offset;
+ s->bios_size = vga_bios_size;
+ /* must be a power of two */
+ bios_total_size = 1;
+ while (bios_total_size < vga_bios_size)
+ bios_total_size <<= 1;
+ pci_register_io_region(d, PCI_ROM_SLOT, bios_total_size,
+ PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
+ }
+ } else {
+#ifdef CONFIG_BOCHS_VBE
+ /* XXX: use optimized standard vga accesses */
+ cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
+ vga_ram_size, vga_ram_offset);
+#endif
+ }
+ return 0;
+}
+
+/********************************************************/
+/* vga screen dump */
+
+static int vga_save_w, vga_save_h;
+
+static void vga_save_dpy_update(DisplayState *s,
+ int x, int y, int w, int h)
+{
+}
+
+static void vga_save_dpy_resize(DisplayState *s, int w, int h)
+{
+ s->linesize = w * 4;
+ s->data = qemu_malloc(h * s->linesize);
+ vga_save_w = w;
+ vga_save_h = h;
+}
+
+static void vga_save_dpy_refresh(DisplayState *s)
+{
+}
+
+static int ppm_save(const char *filename, uint8_t *data,
+ int w, int h, int linesize)
+{
+ FILE *f;
+ uint8_t *d, *d1;
+ unsigned int v;
+ int y, x;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return -1;
+ fprintf(f, "P6\n%d %d\n%d\n",
+ w, h, 255);
+ d1 = data;
+ for(y = 0; y < h; y++) {
+ d = d1;
+ for(x = 0; x < w; x++) {
+ v = *(uint32_t *)d;
+ fputc((v >> 16) & 0xff, f);
+ fputc((v >> 8) & 0xff, f);
+ fputc((v) & 0xff, f);
+ d += 4;
+ }
+ d1 += linesize;
+ }
+ fclose(f);
+ return 0;
+}
+
+/* save the vga display in a PPM image even if no display is
+ available */
+static void vga_screen_dump(void *opaque, const char *filename)
+{
+ VGAState *s = (VGAState *)opaque;
+ DisplayState *saved_ds, ds1, *ds = &ds1;
+
+ /* XXX: this is a little hackish */
+ vga_invalidate_display(s);
+ saved_ds = s->ds;
+
+ memset(ds, 0, sizeof(DisplayState));
+ ds->dpy_update = vga_save_dpy_update;
+ ds->dpy_resize = vga_save_dpy_resize;
+ ds->dpy_refresh = vga_save_dpy_refresh;
+ ds->depth = 32;
+
+ s->ds = ds;
+ s->graphic_mode = -1;
+ vga_update_display(s);
+
+ if (ds->data) {
+ ppm_save(filename, ds->data, vga_save_w, vga_save_h,
+ s->ds->linesize);
+ qemu_free(ds->data);
+ }
+ s->ds = saved_ds;
+}
diff --git a/hw/vga_int.h b/hw/vga_int.h
new file mode 100644
index 0000000..b33ab57
--- /dev/null
+++ b/hw/vga_int.h
@@ -0,0 +1,173 @@
+/*
+ * QEMU internal VGA defines.
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define MSR_COLOR_EMULATION 0x01
+#define MSR_PAGE_SELECT 0x20
+
+#define ST01_V_RETRACE 0x08
+#define ST01_DISP_ENABLE 0x01
+
+/* bochs VBE support */
+#define CONFIG_BOCHS_VBE
+
+#define VBE_DISPI_MAX_XRES 1600
+#define VBE_DISPI_MAX_YRES 1200
+#define VBE_DISPI_MAX_BPP 32
+
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_NB 0xa
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_GETCAPS 0x02
+#define VBE_DISPI_8BIT_DAC 0x20
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+#ifdef CONFIG_BOCHS_VBE
+
+#define VGA_STATE_COMMON_BOCHS_VBE \
+ uint16_t vbe_index; \
+ uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; \
+ uint32_t vbe_start_addr; \
+ uint32_t vbe_line_offset; \
+ uint32_t vbe_bank_mask;
+
+#else
+
+#define VGA_STATE_COMMON_BOCHS_VBE
+
+#endif /* !CONFIG_BOCHS_VBE */
+
+#define CH_ATTR_SIZE (160 * 100)
+#define VGA_MAX_HEIGHT 2048
+
+#define VGA_STATE_COMMON \
+ uint8_t *vram_ptr; \
+ unsigned long vram_offset; \
+ unsigned int vram_size; \
+ unsigned long bios_offset; \
+ unsigned int bios_size; \
+ uint32_t latch; \
+ uint8_t sr_index; \
+ uint8_t sr[256]; \
+ uint8_t gr_index; \
+ uint8_t gr[256]; \
+ uint8_t ar_index; \
+ uint8_t ar[21]; \
+ int ar_flip_flop; \
+ uint8_t cr_index; \
+ uint8_t cr[256]; /* CRT registers */ \
+ uint8_t msr; /* Misc Output Register */ \
+ uint8_t fcr; /* Feature Control Register */ \
+ uint8_t st00; /* status 0 */ \
+ uint8_t st01; /* status 1 */ \
+ uint8_t dac_state; \
+ uint8_t dac_sub_index; \
+ uint8_t dac_read_index; \
+ uint8_t dac_write_index; \
+ uint8_t dac_cache[3]; /* used when writing */ \
+ uint8_t palette[768]; \
+ int32_t bank_offset; \
+ int (*get_bpp)(struct VGAState *s); \
+ void (*get_offsets)(struct VGAState *s, \
+ uint32_t *pline_offset, \
+ uint32_t *pstart_addr); \
+ void (*get_resolution)(struct VGAState *s, \
+ int *pwidth, \
+ int *pheight); \
+ VGA_STATE_COMMON_BOCHS_VBE \
+ /* display refresh support */ \
+ DisplayState *ds; \
+ uint32_t font_offsets[2]; \
+ int graphic_mode; \
+ uint8_t shift_control; \
+ uint8_t double_scan; \
+ uint32_t line_offset; \
+ uint32_t line_compare; \
+ uint32_t start_addr; \
+ uint32_t plane_updated; \
+ uint8_t last_cw, last_ch; \
+ uint32_t last_width, last_height; /* in chars or pixels */ \
+ uint32_t last_scr_width, last_scr_height; /* in pixels */ \
+ uint8_t cursor_start, cursor_end; \
+ uint32_t cursor_offset; \
+ unsigned int (*rgb_to_pixel)(unsigned int r, \
+ unsigned int g, unsigned b); \
+ /* hardware mouse cursor support */ \
+ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; \
+ void (*cursor_invalidate)(struct VGAState *s); \
+ void (*cursor_draw_line)(struct VGAState *s, uint8_t *d, int y); \
+ /* tell for each page if it has been updated since the last time */ \
+ uint32_t last_palette[256]; \
+ uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
+
+
+typedef struct VGAState {
+ VGA_STATE_COMMON
+} VGAState;
+
+static inline int c6_to_8(int v)
+{
+ int b;
+ v &= 0x3f;
+ b = v & 1;
+ return (v << 2) | (b << 1) | b;
+}
+
+void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr);
+void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val);
+void vga_invalidate_scanlines(VGAState *s, int y1, int y2);
+
+void vga_draw_cursor_line_8(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+void vga_draw_cursor_line_16(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+void vga_draw_cursor_line_32(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+
+extern const uint8_t sr_mask[8];
+extern const uint8_t gr_mask[16];
diff --git a/hw/vga_template.h b/hw/vga_template.h
new file mode 100644
index 0000000..e7e8cb8
--- /dev/null
+++ b/hw/vga_template.h
@@ -0,0 +1,525 @@
+/*
+ * QEMU VGA Emulator templates
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+#define BPP 4
+#define PIXEL_TYPE uint32_t
+#else
+#error unsupport depth
+#endif
+
+#ifdef BGR_FORMAT
+#define PIXEL_NAME glue(DEPTH, bgr)
+#else
+#define PIXEL_NAME DEPTH
+#endif /* BGR_FORMAT */
+
+#if DEPTH != 15 && !defined(BGR_FORMAT)
+
+static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d,
+ uint32_t font_data,
+ uint32_t xorcol,
+ uint32_t bgcol)
+{
+#if BPP == 1
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+#elif BPP == 2
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+#endif
+}
+
+static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d,
+ expand4to8[font_data >> 4],
+ xorcol, bgcol);
+ glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP,
+ expand4to8[font_data & 0x0f],
+ xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol, int dup9)
+{
+ uint32_t font_data, xorcol, v;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+#if BPP == 1
+ cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol);
+ v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+1, v);
+ if (dup9)
+ ((uint8_t *)d)[8] = v >> (24 * (1 - BIG));
+ else
+ ((uint8_t *)d)[8] = bgcol;
+
+#elif BPP == 2
+ cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol);
+ v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+3, v);
+ if (dup9)
+ ((uint16_t *)d)[8] = v >> (16 * (1 - BIG));
+ else
+ ((uint16_t *)d)[8] = bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = v;
+ if (dup9)
+ ((uint32_t *)d)[8] = v;
+ else
+ ((uint32_t *)d)[8] = bgcol;
+#endif
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+/*
+ * 4 color mode
+ */
+static void glue(vga_draw_line2_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf];
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ ((PIXEL_TYPE *)d)[4] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+#if BPP == 1
+#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
+#elif BPP == 2
+#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
+#else
+#define PUT_PIXEL2(d, n, v) \
+((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
+#endif
+
+/*
+ * 4 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line2d2_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ PUT_PIXEL2(d, 0, palette[v >> 12]);
+ PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ PUT_PIXEL2(d, 4, palette[v >> 12]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode
+ */
+static void glue(vga_draw_line4_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 28];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf];
+ ((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line4d2_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ PUT_PIXEL2(d, 0, palette[v >> 28]);
+ PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
+ PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 256 color mode, double pixels
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8d2_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ PUT_PIXEL2(d, 0, palette[s[0]]);
+ PUT_PIXEL2(d, 1, palette[s[1]]);
+ PUT_PIXEL2(d, 2, palette[s[2]]);
+ PUT_PIXEL2(d, 3, palette[s[3]]);
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * standard 256 color mode
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8_, DEPTH)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ ((PIXEL_TYPE *)d)[0] = palette[s[0]];
+ ((PIXEL_TYPE *)d)[1] = palette[s[1]];
+ ((PIXEL_TYPE *)d)[2] = palette[s[2]];
+ ((PIXEL_TYPE *)d)[3] = palette[s[3]];
+ ((PIXEL_TYPE *)d)[4] = palette[s[4]];
+ ((PIXEL_TYPE *)d)[5] = palette[s[5]];
+ ((PIXEL_TYPE *)d)[6] = palette[s[6]];
+ ((PIXEL_TYPE *)d)[7] = palette[s[7]];
+ d += BPP * 8;
+ s += 8;
+ }
+}
+
+void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1,
+ const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0,
+ unsigned int color1,
+ unsigned int color_xor)
+{
+ const uint8_t *plane0, *plane1;
+ int x, b0, b1;
+ uint8_t *d;
+
+ d = d1;
+ plane0 = src1;
+ plane1 = src1 + poffset;
+ for(x = 0; x < w; x++) {
+ b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1;
+ b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1;
+#if DEPTH == 8
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ d[0] ^= color_xor;
+ break;
+ case 2:
+ d[0] = color0;
+ break;
+ case 3:
+ d[0] = color1;
+ break;
+ }
+#elif DEPTH == 16
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint16_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint16_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint16_t *)d)[0] = color1;
+ break;
+ }
+#elif DEPTH == 32
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint32_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint32_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint32_t *)d)[0] = color1;
+ break;
+ }
+#else
+#error unsupported depth
+#endif
+ d += BPP;
+ }
+}
+
+#endif /* DEPTH != 15 */
+
+
+/* XXX: optimize */
+
+/*
+ * 15 bit color
+ */
+static void glue(vga_draw_line15_, PIXEL_NAME)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 15 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 7) & 0xf8;
+ g = (v >> 2) & 0xf8;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 16 bit color
+ */
+static void glue(vga_draw_line16_, PIXEL_NAME)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 16 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 8) & 0xf8;
+ g = (v >> 3) & 0xfc;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 24 bit color
+ */
+static void glue(vga_draw_line24_, PIXEL_NAME)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[0];
+ g = s[1];
+ b = s[2];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 3;
+ d += BPP;
+ } while (--w != 0);
+}
+
+/*
+ * 32 bit color
+ */
+static void glue(vga_draw_line32_, PIXEL_NAME)(VGAState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 32 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) && !defined(BGR_FORMAT)
+ memcpy(d, s, width * 4);
+#else
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[1];
+ g = s[2];
+ b = s[3];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 4;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+#undef PUT_PIXEL2
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
+#undef PIXEL_NAME
+#undef BGR_FORMAT
diff --git a/i386-dis.c b/i386-dis.c
new file mode 100644
index 0000000..0496e14
--- /dev/null
+++ b/i386-dis.c
@@ -0,0 +1,4143 @@
+/* Print i386 instructions for GDB, the GNU debugger.
+ Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2001
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+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. */
+
+/*
+ * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ * modified by John Hassey (hassey@dg-rtp.dg.com)
+ * x86-64 support added by Jan Hubicka (jh@suse.cz)
+ */
+
+/*
+ * The main tables describing the instructions is essentially a copy
+ * of the "Opcode Map" chapter (Appendix A) of the Intel 80386
+ * Programmers Manual. Usually, there is a capital letter, followed
+ * by a small letter. The capital letter tell the addressing mode,
+ * and the small letter tells about the operand size. Refer to
+ * the Intel manual for details.
+ */
+
+#include <stdlib.h>
+#include "dis-asm.h"
+
+#define MAXLEN 20
+
+#include <setjmp.h>
+
+#ifndef UNIXWARE_COMPAT
+/* Set non-zero for broken, compatible instructions. Set to zero for
+ non-broken opcodes. */
+#define UNIXWARE_COMPAT 1
+#endif
+
+static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *));
+static void ckprefix PARAMS ((void));
+static const char *prefix_name PARAMS ((int, int));
+static int print_insn PARAMS ((bfd_vma, disassemble_info *));
+static void dofloat PARAMS ((int));
+static void OP_ST PARAMS ((int, int));
+static void OP_STi PARAMS ((int, int));
+static int putop PARAMS ((const char *, int));
+static void oappend PARAMS ((const char *));
+static void append_seg PARAMS ((void));
+static void OP_indirE PARAMS ((int, int));
+static void print_operand_value PARAMS ((char *, int, bfd_vma));
+static void OP_E PARAMS ((int, int));
+static void OP_G PARAMS ((int, int));
+static bfd_vma get64 PARAMS ((void));
+static bfd_signed_vma get32 PARAMS ((void));
+static bfd_signed_vma get32s PARAMS ((void));
+static int get16 PARAMS ((void));
+static void set_op PARAMS ((bfd_vma, int));
+static void OP_REG PARAMS ((int, int));
+static void OP_IMREG PARAMS ((int, int));
+static void OP_I PARAMS ((int, int));
+static void OP_I64 PARAMS ((int, int));
+static void OP_sI PARAMS ((int, int));
+static void OP_J PARAMS ((int, int));
+static void OP_SEG PARAMS ((int, int));
+static void OP_DIR PARAMS ((int, int));
+static void OP_OFF PARAMS ((int, int));
+static void OP_OFF64 PARAMS ((int, int));
+static void ptr_reg PARAMS ((int, int));
+static void OP_ESreg PARAMS ((int, int));
+static void OP_DSreg PARAMS ((int, int));
+static void OP_C PARAMS ((int, int));
+static void OP_D PARAMS ((int, int));
+static void OP_T PARAMS ((int, int));
+static void OP_Rd PARAMS ((int, int));
+static void OP_MMX PARAMS ((int, int));
+static void OP_XMM PARAMS ((int, int));
+static void OP_EM PARAMS ((int, int));
+static void OP_EX PARAMS ((int, int));
+static void OP_MS PARAMS ((int, int));
+static void OP_XS PARAMS ((int, int));
+static void OP_3DNowSuffix PARAMS ((int, int));
+static void OP_SIMD_Suffix PARAMS ((int, int));
+static void SIMD_Fixup PARAMS ((int, int));
+static void BadOp PARAMS ((void));
+
+struct dis_private {
+ /* Points to first byte not fetched. */
+ bfd_byte *max_fetched;
+ bfd_byte the_buffer[MAXLEN];
+ bfd_vma insn_start;
+ int orig_sizeflag;
+ jmp_buf bailout;
+};
+
+/* The opcode for the fwait instruction, which we treat as a prefix
+ when we can. */
+#define FWAIT_OPCODE (0x9b)
+
+/* Set to 1 for 64bit mode disassembly. */
+static int mode_64bit;
+
+/* Flags for the prefixes for the current instruction. See below. */
+static int prefixes;
+
+/* REX prefix the current instruction. See below. */
+static int rex;
+/* Bits of REX we've already used. */
+static int rex_used;
+#define REX_MODE64 8
+#define REX_EXTX 4
+#define REX_EXTY 2
+#define REX_EXTZ 1
+/* Mark parts used in the REX prefix. When we are testing for
+ empty prefix (for 8bit register REX extension), just mask it
+ out. Otherwise test for REX bit is excuse for existence of REX
+ only in case value is nonzero. */
+#define USED_REX(value) \
+ { \
+ if (value) \
+ rex_used |= (rex & value) ? (value) | 0x40 : 0; \
+ else \
+ rex_used |= 0x40; \
+ }
+
+/* Flags for prefixes which we somehow handled when printing the
+ current instruction. */
+static int used_prefixes;
+
+/* Flags stored in PREFIXES. */
+#define PREFIX_REPZ 1
+#define PREFIX_REPNZ 2
+#define PREFIX_LOCK 4
+#define PREFIX_CS 8
+#define PREFIX_SS 0x10
+#define PREFIX_DS 0x20
+#define PREFIX_ES 0x40
+#define PREFIX_FS 0x80
+#define PREFIX_GS 0x100
+#define PREFIX_DATA 0x200
+#define PREFIX_ADDR 0x400
+#define PREFIX_FWAIT 0x800
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+ to ADDR (exclusive) are valid. Returns 1 for success, longjmps
+ on error. */
+#define FETCH_DATA(info, addr) \
+ ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \
+ ? 1 : fetch_data ((info), (addr)))
+
+static int
+fetch_data (info, addr)
+ struct disassemble_info *info;
+ bfd_byte *addr;
+{
+ int status;
+ struct dis_private *priv = (struct dis_private *) info->private_data;
+ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+ status = (*info->read_memory_func) (start,
+ priv->max_fetched,
+ addr - priv->max_fetched,
+ info);
+ if (status != 0)
+ {
+ /* If we did manage to read at least one byte, then
+ print_insn_i386 will do something sensible. Otherwise, print
+ an error. We do that here because this is where we know
+ STATUS. */
+ if (priv->max_fetched == priv->the_buffer)
+ (*info->memory_error_func) (status, start, info);
+ longjmp (priv->bailout, 1);
+ }
+ else
+ priv->max_fetched = addr;
+ return 1;
+}
+
+#define XX NULL, 0
+
+#define Eb OP_E, b_mode
+#define Ev OP_E, v_mode
+#define Ed OP_E, d_mode
+#define indirEb OP_indirE, b_mode
+#define indirEv OP_indirE, v_mode
+#define Ew OP_E, w_mode
+#define Ma OP_E, v_mode
+#define M OP_E, 0 /* lea, lgdt, etc. */
+#define Mp OP_E, 0 /* 32 or 48 bit memory operand for LDS, LES etc */
+#define Gb OP_G, b_mode
+#define Gv OP_G, v_mode
+#define Gd OP_G, d_mode
+#define Gw OP_G, w_mode
+#define Rd OP_Rd, d_mode
+#define Rm OP_Rd, m_mode
+#define Ib OP_I, b_mode
+#define sIb OP_sI, b_mode /* sign extened byte */
+#define Iv OP_I, v_mode
+#define Iq OP_I, q_mode
+#define Iv64 OP_I64, v_mode
+#define Iw OP_I, w_mode
+#define Jb OP_J, b_mode
+#define Jv OP_J, v_mode
+#define Cm OP_C, m_mode
+#define Dm OP_D, m_mode
+#define Td OP_T, d_mode
+
+#define RMeAX OP_REG, eAX_reg
+#define RMeBX OP_REG, eBX_reg
+#define RMeCX OP_REG, eCX_reg
+#define RMeDX OP_REG, eDX_reg
+#define RMeSP OP_REG, eSP_reg
+#define RMeBP OP_REG, eBP_reg
+#define RMeSI OP_REG, eSI_reg
+#define RMeDI OP_REG, eDI_reg
+#define RMrAX OP_REG, rAX_reg
+#define RMrBX OP_REG, rBX_reg
+#define RMrCX OP_REG, rCX_reg
+#define RMrDX OP_REG, rDX_reg
+#define RMrSP OP_REG, rSP_reg
+#define RMrBP OP_REG, rBP_reg
+#define RMrSI OP_REG, rSI_reg
+#define RMrDI OP_REG, rDI_reg
+#define RMAL OP_REG, al_reg
+#define RMAL OP_REG, al_reg
+#define RMCL OP_REG, cl_reg
+#define RMDL OP_REG, dl_reg
+#define RMBL OP_REG, bl_reg
+#define RMAH OP_REG, ah_reg
+#define RMCH OP_REG, ch_reg
+#define RMDH OP_REG, dh_reg
+#define RMBH OP_REG, bh_reg
+#define RMAX OP_REG, ax_reg
+#define RMDX OP_REG, dx_reg
+
+#define eAX OP_IMREG, eAX_reg
+#define eBX OP_IMREG, eBX_reg
+#define eCX OP_IMREG, eCX_reg
+#define eDX OP_IMREG, eDX_reg
+#define eSP OP_IMREG, eSP_reg
+#define eBP OP_IMREG, eBP_reg
+#define eSI OP_IMREG, eSI_reg
+#define eDI OP_IMREG, eDI_reg
+#define AL OP_IMREG, al_reg
+#define AL OP_IMREG, al_reg
+#define CL OP_IMREG, cl_reg
+#define DL OP_IMREG, dl_reg
+#define BL OP_IMREG, bl_reg
+#define AH OP_IMREG, ah_reg
+#define CH OP_IMREG, ch_reg
+#define DH OP_IMREG, dh_reg
+#define BH OP_IMREG, bh_reg
+#define AX OP_IMREG, ax_reg
+#define DX OP_IMREG, dx_reg
+#define indirDX OP_IMREG, indir_dx_reg
+
+#define Sw OP_SEG, w_mode
+#define Ap OP_DIR, 0
+#define Ob OP_OFF, b_mode
+#define Ob64 OP_OFF64, b_mode
+#define Ov OP_OFF, v_mode
+#define Ov64 OP_OFF64, v_mode
+#define Xb OP_DSreg, eSI_reg
+#define Xv OP_DSreg, eSI_reg
+#define Yb OP_ESreg, eDI_reg
+#define Yv OP_ESreg, eDI_reg
+#define DSBX OP_DSreg, eBX_reg
+
+#define es OP_REG, es_reg
+#define ss OP_REG, ss_reg
+#define cs OP_REG, cs_reg
+#define ds OP_REG, ds_reg
+#define fs OP_REG, fs_reg
+#define gs OP_REG, gs_reg
+
+#define MX OP_MMX, 0
+#define XM OP_XMM, 0
+#define EM OP_EM, v_mode
+#define EX OP_EX, v_mode
+#define MS OP_MS, v_mode
+#define XS OP_XS, v_mode
+#define None OP_E, 0
+#define OPSUF OP_3DNowSuffix, 0
+#define OPSIMD OP_SIMD_Suffix, 0
+
+#define cond_jump_flag NULL, cond_jump_mode
+#define loop_jcxz_flag NULL, loop_jcxz_mode
+
+/* bits in sizeflag */
+#define SUFFIX_ALWAYS 4
+#define AFLAG 2
+#define DFLAG 1
+
+#define b_mode 1 /* byte operand */
+#define v_mode 2 /* operand size depends on prefixes */
+#define w_mode 3 /* word operand */
+#define d_mode 4 /* double word operand */
+#define q_mode 5 /* quad word operand */
+#define x_mode 6
+#define m_mode 7 /* d_mode in 32bit, q_mode in 64bit mode. */
+#define cond_jump_mode 8
+#define loop_jcxz_mode 9
+
+#define es_reg 100
+#define cs_reg 101
+#define ss_reg 102
+#define ds_reg 103
+#define fs_reg 104
+#define gs_reg 105
+
+#define eAX_reg 108
+#define eCX_reg 109
+#define eDX_reg 110
+#define eBX_reg 111
+#define eSP_reg 112
+#define eBP_reg 113
+#define eSI_reg 114
+#define eDI_reg 115
+
+#define al_reg 116
+#define cl_reg 117
+#define dl_reg 118
+#define bl_reg 119
+#define ah_reg 120
+#define ch_reg 121
+#define dh_reg 122
+#define bh_reg 123
+
+#define ax_reg 124
+#define cx_reg 125
+#define dx_reg 126
+#define bx_reg 127
+#define sp_reg 128
+#define bp_reg 129
+#define si_reg 130
+#define di_reg 131
+
+#define rAX_reg 132
+#define rCX_reg 133
+#define rDX_reg 134
+#define rBX_reg 135
+#define rSP_reg 136
+#define rBP_reg 137
+#define rSI_reg 138
+#define rDI_reg 139
+
+#define indir_dx_reg 150
+
+#define FLOATCODE 1
+#define USE_GROUPS 2
+#define USE_PREFIX_USER_TABLE 3
+#define X86_64_SPECIAL 4
+
+#define FLOAT NULL, NULL, FLOATCODE, NULL, 0, NULL, 0
+
+#define GRP1b NULL, NULL, USE_GROUPS, NULL, 0, NULL, 0
+#define GRP1S NULL, NULL, USE_GROUPS, NULL, 1, NULL, 0
+#define GRP1Ss NULL, NULL, USE_GROUPS, NULL, 2, NULL, 0
+#define GRP2b NULL, NULL, USE_GROUPS, NULL, 3, NULL, 0
+#define GRP2S NULL, NULL, USE_GROUPS, NULL, 4, NULL, 0
+#define GRP2b_one NULL, NULL, USE_GROUPS, NULL, 5, NULL, 0
+#define GRP2S_one NULL, NULL, USE_GROUPS, NULL, 6, NULL, 0
+#define GRP2b_cl NULL, NULL, USE_GROUPS, NULL, 7, NULL, 0
+#define GRP2S_cl NULL, NULL, USE_GROUPS, NULL, 8, NULL, 0
+#define GRP3b NULL, NULL, USE_GROUPS, NULL, 9, NULL, 0
+#define GRP3S NULL, NULL, USE_GROUPS, NULL, 10, NULL, 0
+#define GRP4 NULL, NULL, USE_GROUPS, NULL, 11, NULL, 0
+#define GRP5 NULL, NULL, USE_GROUPS, NULL, 12, NULL, 0
+#define GRP6 NULL, NULL, USE_GROUPS, NULL, 13, NULL, 0
+#define GRP7 NULL, NULL, USE_GROUPS, NULL, 14, NULL, 0
+#define GRP8 NULL, NULL, USE_GROUPS, NULL, 15, NULL, 0
+#define GRP9 NULL, NULL, USE_GROUPS, NULL, 16, NULL, 0
+#define GRP10 NULL, NULL, USE_GROUPS, NULL, 17, NULL, 0
+#define GRP11 NULL, NULL, USE_GROUPS, NULL, 18, NULL, 0
+#define GRP12 NULL, NULL, USE_GROUPS, NULL, 19, NULL, 0
+#define GRP13 NULL, NULL, USE_GROUPS, NULL, 20, NULL, 0
+#define GRP14 NULL, NULL, USE_GROUPS, NULL, 21, NULL, 0
+#define GRPAMD NULL, NULL, USE_GROUPS, NULL, 22, NULL, 0
+
+#define PREGRP0 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 0, NULL, 0
+#define PREGRP1 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 1, NULL, 0
+#define PREGRP2 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 2, NULL, 0
+#define PREGRP3 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 3, NULL, 0
+#define PREGRP4 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 4, NULL, 0
+#define PREGRP5 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 5, NULL, 0
+#define PREGRP6 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 6, NULL, 0
+#define PREGRP7 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 7, NULL, 0
+#define PREGRP8 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 8, NULL, 0
+#define PREGRP9 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 9, NULL, 0
+#define PREGRP10 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 10, NULL, 0
+#define PREGRP11 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 11, NULL, 0
+#define PREGRP12 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 12, NULL, 0
+#define PREGRP13 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 13, NULL, 0
+#define PREGRP14 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 14, NULL, 0
+#define PREGRP15 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 15, NULL, 0
+#define PREGRP16 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 16, NULL, 0
+#define PREGRP17 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 17, NULL, 0
+#define PREGRP18 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 18, NULL, 0
+#define PREGRP19 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 19, NULL, 0
+#define PREGRP20 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 20, NULL, 0
+#define PREGRP21 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 21, NULL, 0
+#define PREGRP22 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 22, NULL, 0
+#define PREGRP23 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 23, NULL, 0
+#define PREGRP24 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 24, NULL, 0
+#define PREGRP25 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 25, NULL, 0
+#define PREGRP26 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 26, NULL, 0
+
+#define X86_64_0 NULL, NULL, X86_64_SPECIAL, NULL, 0, NULL, 0
+
+typedef void (*op_rtn) PARAMS ((int bytemode, int sizeflag));
+
+struct dis386 {
+ const char *name;
+ op_rtn op1;
+ int bytemode1;
+ op_rtn op2;
+ int bytemode2;
+ op_rtn op3;
+ int bytemode3;
+};
+
+/* Upper case letters in the instruction names here are macros.
+ 'A' => print 'b' if no register operands or suffix_always is true
+ 'B' => print 'b' if suffix_always is true
+ 'E' => print 'e' if 32-bit form of jcxz
+ 'F' => print 'w' or 'l' depending on address size prefix (loop insns)
+ 'H' => print ",pt" or ",pn" branch hint
+ 'L' => print 'l' if suffix_always is true
+ 'N' => print 'n' if instruction has no wait "prefix"
+ 'O' => print 'd', or 'o'
+ 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix,
+ . or suffix_always is true. print 'q' if rex prefix is present.
+ 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always
+ . is true
+ 'R' => print 'w', 'l' or 'q' ("wd" or "dq" in intel mode)
+ 'S' => print 'w', 'l' or 'q' if suffix_always is true
+ 'T' => print 'q' in 64bit mode and behave as 'P' otherwise
+ 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise
+ 'X' => print 's', 'd' depending on data16 prefix (for XMM)
+ 'W' => print 'b' or 'w' ("w" or "de" in intel mode)
+ 'Y' => 'q' if instruction has an REX 64bit overwrite prefix
+
+ Many of the above letters print nothing in Intel mode. See "putop"
+ for the details.
+
+ Braces '{' and '}', and vertical bars '|', indicate alternative
+ mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel
+ modes. In cases where there are only two alternatives, the X86_64
+ instruction is reserved, and "(bad)" is printed.
+*/
+
+static const struct dis386 dis386[] = {
+ /* 00 */
+ { "addB", Eb, Gb, XX },
+ { "addS", Ev, Gv, XX },
+ { "addB", Gb, Eb, XX },
+ { "addS", Gv, Ev, XX },
+ { "addB", AL, Ib, XX },
+ { "addS", eAX, Iv, XX },
+ { "push{T|}", es, XX, XX },
+ { "pop{T|}", es, XX, XX },
+ /* 08 */
+ { "orB", Eb, Gb, XX },
+ { "orS", Ev, Gv, XX },
+ { "orB", Gb, Eb, XX },
+ { "orS", Gv, Ev, XX },
+ { "orB", AL, Ib, XX },
+ { "orS", eAX, Iv, XX },
+ { "push{T|}", cs, XX, XX },
+ { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */
+ /* 10 */
+ { "adcB", Eb, Gb, XX },
+ { "adcS", Ev, Gv, XX },
+ { "adcB", Gb, Eb, XX },
+ { "adcS", Gv, Ev, XX },
+ { "adcB", AL, Ib, XX },
+ { "adcS", eAX, Iv, XX },
+ { "push{T|}", ss, XX, XX },
+ { "popT|}", ss, XX, XX },
+ /* 18 */
+ { "sbbB", Eb, Gb, XX },
+ { "sbbS", Ev, Gv, XX },
+ { "sbbB", Gb, Eb, XX },
+ { "sbbS", Gv, Ev, XX },
+ { "sbbB", AL, Ib, XX },
+ { "sbbS", eAX, Iv, XX },
+ { "push{T|}", ds, XX, XX },
+ { "pop{T|}", ds, XX, XX },
+ /* 20 */
+ { "andB", Eb, Gb, XX },
+ { "andS", Ev, Gv, XX },
+ { "andB", Gb, Eb, XX },
+ { "andS", Gv, Ev, XX },
+ { "andB", AL, Ib, XX },
+ { "andS", eAX, Iv, XX },
+ { "(bad)", XX, XX, XX }, /* SEG ES prefix */
+ { "daa{|}", XX, XX, XX },
+ /* 28 */
+ { "subB", Eb, Gb, XX },
+ { "subS", Ev, Gv, XX },
+ { "subB", Gb, Eb, XX },
+ { "subS", Gv, Ev, XX },
+ { "subB", AL, Ib, XX },
+ { "subS", eAX, Iv, XX },
+ { "(bad)", XX, XX, XX }, /* SEG CS prefix */
+ { "das{|}", XX, XX, XX },
+ /* 30 */
+ { "xorB", Eb, Gb, XX },
+ { "xorS", Ev, Gv, XX },
+ { "xorB", Gb, Eb, XX },
+ { "xorS", Gv, Ev, XX },
+ { "xorB", AL, Ib, XX },
+ { "xorS", eAX, Iv, XX },
+ { "(bad)", XX, XX, XX }, /* SEG SS prefix */
+ { "aaa{|}", XX, XX, XX },
+ /* 38 */
+ { "cmpB", Eb, Gb, XX },
+ { "cmpS", Ev, Gv, XX },
+ { "cmpB", Gb, Eb, XX },
+ { "cmpS", Gv, Ev, XX },
+ { "cmpB", AL, Ib, XX },
+ { "cmpS", eAX, Iv, XX },
+ { "(bad)", XX, XX, XX }, /* SEG DS prefix */
+ { "aas{|}", XX, XX, XX },
+ /* 40 */
+ { "inc{S|}", RMeAX, XX, XX },
+ { "inc{S|}", RMeCX, XX, XX },
+ { "inc{S|}", RMeDX, XX, XX },
+ { "inc{S|}", RMeBX, XX, XX },
+ { "inc{S|}", RMeSP, XX, XX },
+ { "inc{S|}", RMeBP, XX, XX },
+ { "inc{S|}", RMeSI, XX, XX },
+ { "inc{S|}", RMeDI, XX, XX },
+ /* 48 */
+ { "dec{S|}", RMeAX, XX, XX },
+ { "dec{S|}", RMeCX, XX, XX },
+ { "dec{S|}", RMeDX, XX, XX },
+ { "dec{S|}", RMeBX, XX, XX },
+ { "dec{S|}", RMeSP, XX, XX },
+ { "dec{S|}", RMeBP, XX, XX },
+ { "dec{S|}", RMeSI, XX, XX },
+ { "dec{S|}", RMeDI, XX, XX },
+ /* 50 */
+ { "pushS", RMrAX, XX, XX },
+ { "pushS", RMrCX, XX, XX },
+ { "pushS", RMrDX, XX, XX },
+ { "pushS", RMrBX, XX, XX },
+ { "pushS", RMrSP, XX, XX },
+ { "pushS", RMrBP, XX, XX },
+ { "pushS", RMrSI, XX, XX },
+ { "pushS", RMrDI, XX, XX },
+ /* 58 */
+ { "popS", RMrAX, XX, XX },
+ { "popS", RMrCX, XX, XX },
+ { "popS", RMrDX, XX, XX },
+ { "popS", RMrBX, XX, XX },
+ { "popS", RMrSP, XX, XX },
+ { "popS", RMrBP, XX, XX },
+ { "popS", RMrSI, XX, XX },
+ { "popS", RMrDI, XX, XX },
+ /* 60 */
+ { "pusha{P|}", XX, XX, XX },
+ { "popa{P|}", XX, XX, XX },
+ { "bound{S|}", Gv, Ma, XX },
+ { X86_64_0 },
+ { "(bad)", XX, XX, XX }, /* seg fs */
+ { "(bad)", XX, XX, XX }, /* seg gs */
+ { "(bad)", XX, XX, XX }, /* op size prefix */
+ { "(bad)", XX, XX, XX }, /* adr size prefix */
+ /* 68 */
+ { "pushT", Iq, XX, XX },
+ { "imulS", Gv, Ev, Iv },
+ { "pushT", sIb, XX, XX },
+ { "imulS", Gv, Ev, sIb },
+ { "ins{b||b|}", Yb, indirDX, XX },
+ { "ins{R||R|}", Yv, indirDX, XX },
+ { "outs{b||b|}", indirDX, Xb, XX },
+ { "outs{R||R|}", indirDX, Xv, XX },
+ /* 70 */
+ { "joH", Jb, XX, cond_jump_flag },
+ { "jnoH", Jb, XX, cond_jump_flag },
+ { "jbH", Jb, XX, cond_jump_flag },
+ { "jaeH", Jb, XX, cond_jump_flag },
+ { "jeH", Jb, XX, cond_jump_flag },
+ { "jneH", Jb, XX, cond_jump_flag },
+ { "jbeH", Jb, XX, cond_jump_flag },
+ { "jaH", Jb, XX, cond_jump_flag },
+ /* 78 */
+ { "jsH", Jb, XX, cond_jump_flag },
+ { "jnsH", Jb, XX, cond_jump_flag },
+ { "jpH", Jb, XX, cond_jump_flag },
+ { "jnpH", Jb, XX, cond_jump_flag },
+ { "jlH", Jb, XX, cond_jump_flag },
+ { "jgeH", Jb, XX, cond_jump_flag },
+ { "jleH", Jb, XX, cond_jump_flag },
+ { "jgH", Jb, XX, cond_jump_flag },
+ /* 80 */
+ { GRP1b },
+ { GRP1S },
+ { "(bad)", XX, XX, XX },
+ { GRP1Ss },
+ { "testB", Eb, Gb, XX },
+ { "testS", Ev, Gv, XX },
+ { "xchgB", Eb, Gb, XX },
+ { "xchgS", Ev, Gv, XX },
+ /* 88 */
+ { "movB", Eb, Gb, XX },
+ { "movS", Ev, Gv, XX },
+ { "movB", Gb, Eb, XX },
+ { "movS", Gv, Ev, XX },
+ { "movQ", Ev, Sw, XX },
+ { "leaS", Gv, M, XX },
+ { "movQ", Sw, Ev, XX },
+ { "popU", Ev, XX, XX },
+ /* 90 */
+ { "nop", XX, XX, XX },
+ /* FIXME: NOP with REPz prefix is called PAUSE. */
+ { "xchgS", RMeCX, eAX, XX },
+ { "xchgS", RMeDX, eAX, XX },
+ { "xchgS", RMeBX, eAX, XX },
+ { "xchgS", RMeSP, eAX, XX },
+ { "xchgS", RMeBP, eAX, XX },
+ { "xchgS", RMeSI, eAX, XX },
+ { "xchgS", RMeDI, eAX, XX },
+ /* 98 */
+ { "cW{tR||tR|}", XX, XX, XX },
+ { "cR{tO||tO|}", XX, XX, XX },
+ { "lcall{T|}", Ap, XX, XX },
+ { "(bad)", XX, XX, XX }, /* fwait */
+ { "pushfT", XX, XX, XX },
+ { "popfT", XX, XX, XX },
+ { "sahf{|}", XX, XX, XX },
+ { "lahf{|}", XX, XX, XX },
+ /* a0 */
+ { "movB", AL, Ob64, XX },
+ { "movS", eAX, Ov64, XX },
+ { "movB", Ob64, AL, XX },
+ { "movS", Ov64, eAX, XX },
+ { "movs{b||b|}", Yb, Xb, XX },
+ { "movs{R||R|}", Yv, Xv, XX },
+ { "cmps{b||b|}", Xb, Yb, XX },
+ { "cmps{R||R|}", Xv, Yv, XX },
+ /* a8 */
+ { "testB", AL, Ib, XX },
+ { "testS", eAX, Iv, XX },
+ { "stosB", Yb, AL, XX },
+ { "stosS", Yv, eAX, XX },
+ { "lodsB", AL, Xb, XX },
+ { "lodsS", eAX, Xv, XX },
+ { "scasB", AL, Yb, XX },
+ { "scasS", eAX, Yv, XX },
+ /* b0 */
+ { "movB", RMAL, Ib, XX },
+ { "movB", RMCL, Ib, XX },
+ { "movB", RMDL, Ib, XX },
+ { "movB", RMBL, Ib, XX },
+ { "movB", RMAH, Ib, XX },
+ { "movB", RMCH, Ib, XX },
+ { "movB", RMDH, Ib, XX },
+ { "movB", RMBH, Ib, XX },
+ /* b8 */
+ { "movS", RMeAX, Iv64, XX },
+ { "movS", RMeCX, Iv64, XX },
+ { "movS", RMeDX, Iv64, XX },
+ { "movS", RMeBX, Iv64, XX },
+ { "movS", RMeSP, Iv64, XX },
+ { "movS", RMeBP, Iv64, XX },
+ { "movS", RMeSI, Iv64, XX },
+ { "movS", RMeDI, Iv64, XX },
+ /* c0 */
+ { GRP2b },
+ { GRP2S },
+ { "retT", Iw, XX, XX },
+ { "retT", XX, XX, XX },
+ { "les{S|}", Gv, Mp, XX },
+ { "ldsS", Gv, Mp, XX },
+ { "movA", Eb, Ib, XX },
+ { "movQ", Ev, Iv, XX },
+ /* c8 */
+ { "enterT", Iw, Ib, XX },
+ { "leaveT", XX, XX, XX },
+ { "lretP", Iw, XX, XX },
+ { "lretP", XX, XX, XX },
+ { "int3", XX, XX, XX },
+ { "int", Ib, XX, XX },
+ { "into{|}", XX, XX, XX },
+ { "iretP", XX, XX, XX },
+ /* d0 */
+ { GRP2b_one },
+ { GRP2S_one },
+ { GRP2b_cl },
+ { GRP2S_cl },
+ { "aam{|}", sIb, XX, XX },
+ { "aad{|}", sIb, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "xlat", DSBX, XX, XX },
+ /* d8 */
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ /* e0 */
+ { "loopneFH", Jb, XX, loop_jcxz_flag },
+ { "loopeFH", Jb, XX, loop_jcxz_flag },
+ { "loopFH", Jb, XX, loop_jcxz_flag },
+ { "jEcxzH", Jb, XX, loop_jcxz_flag },
+ { "inB", AL, Ib, XX },
+ { "inS", eAX, Ib, XX },
+ { "outB", Ib, AL, XX },
+ { "outS", Ib, eAX, XX },
+ /* e8 */
+ { "callT", Jv, XX, XX },
+ { "jmpT", Jv, XX, XX },
+ { "ljmp{T|}", Ap, XX, XX },
+ { "jmp", Jb, XX, XX },
+ { "inB", AL, indirDX, XX },
+ { "inS", eAX, indirDX, XX },
+ { "outB", indirDX, AL, XX },
+ { "outS", indirDX, eAX, XX },
+ /* f0 */
+ { "(bad)", XX, XX, XX }, /* lock prefix */
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX }, /* repne */
+ { "(bad)", XX, XX, XX }, /* repz */
+ { "hlt", XX, XX, XX },
+ { "cmc", XX, XX, XX },
+ { GRP3b },
+ { GRP3S },
+ /* f8 */
+ { "clc", XX, XX, XX },
+ { "stc", XX, XX, XX },
+ { "cli", XX, XX, XX },
+ { "sti", XX, XX, XX },
+ { "cld", XX, XX, XX },
+ { "std", XX, XX, XX },
+ { GRP4 },
+ { GRP5 },
+};
+
+static const struct dis386 dis386_twobyte[] = {
+ /* 00 */
+ { GRP6 },
+ { GRP7 },
+ { "larS", Gv, Ew, XX },
+ { "lslS", Gv, Ew, XX },
+ { "(bad)", XX, XX, XX },
+ { "syscall", XX, XX, XX },
+ { "clts", XX, XX, XX },
+ { "sysretP", XX, XX, XX },
+ /* 08 */
+ { "invd", XX, XX, XX },
+ { "wbinvd", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "ud2a", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { GRPAMD },
+ { "femms", XX, XX, XX },
+ { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix. */
+ /* 10 */
+ { PREGRP8 },
+ { PREGRP9 },
+ { "movlpX", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */
+ { "movlpX", EX, XM, SIMD_Fixup, 'h' },
+ { "unpcklpX", XM, EX, XX },
+ { "unpckhpX", XM, EX, XX },
+ { "movhpX", XM, EX, SIMD_Fixup, 'l' },
+ { "movhpX", EX, XM, SIMD_Fixup, 'l' },
+ /* 18 */
+ { GRP14 },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ /* 20 */
+ { "movL", Rm, Cm, XX },
+ { "movL", Rm, Dm, XX },
+ { "movL", Cm, Rm, XX },
+ { "movL", Dm, Rm, XX },
+ { "movL", Rd, Td, XX },
+ { "(bad)", XX, XX, XX },
+ { "movL", Td, Rd, XX },
+ { "(bad)", XX, XX, XX },
+ /* 28 */
+ { "movapX", XM, EX, XX },
+ { "movapX", EX, XM, XX },
+ { PREGRP2 },
+ { "movntpX", Ev, XM, XX },
+ { PREGRP4 },
+ { PREGRP3 },
+ { "ucomisX", XM,EX, XX },
+ { "comisX", XM,EX, XX },
+ /* 30 */
+ { "wrmsr", XX, XX, XX },
+ { "rdtsc", XX, XX, XX },
+ { "rdmsr", XX, XX, XX },
+ { "rdpmc", XX, XX, XX },
+ { "sysenter", XX, XX, XX },
+ { "sysexit", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ /* 38 */
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ /* 40 */
+ { "cmovo", Gv, Ev, XX },
+ { "cmovno", Gv, Ev, XX },
+ { "cmovb", Gv, Ev, XX },
+ { "cmovae", Gv, Ev, XX },
+ { "cmove", Gv, Ev, XX },
+ { "cmovne", Gv, Ev, XX },
+ { "cmovbe", Gv, Ev, XX },
+ { "cmova", Gv, Ev, XX },
+ /* 48 */
+ { "cmovs", Gv, Ev, XX },
+ { "cmovns", Gv, Ev, XX },
+ { "cmovp", Gv, Ev, XX },
+ { "cmovnp", Gv, Ev, XX },
+ { "cmovl", Gv, Ev, XX },
+ { "cmovge", Gv, Ev, XX },
+ { "cmovle", Gv, Ev, XX },
+ { "cmovg", Gv, Ev, XX },
+ /* 50 */
+ { "movmskpX", Gd, XS, XX },
+ { PREGRP13 },
+ { PREGRP12 },
+ { PREGRP11 },
+ { "andpX", XM, EX, XX },
+ { "andnpX", XM, EX, XX },
+ { "orpX", XM, EX, XX },
+ { "xorpX", XM, EX, XX },
+ /* 58 */
+ { PREGRP0 },
+ { PREGRP10 },
+ { PREGRP17 },
+ { PREGRP16 },
+ { PREGRP14 },
+ { PREGRP7 },
+ { PREGRP5 },
+ { PREGRP6 },
+ /* 60 */
+ { "punpcklbw", MX, EM, XX },
+ { "punpcklwd", MX, EM, XX },
+ { "punpckldq", MX, EM, XX },
+ { "packsswb", MX, EM, XX },
+ { "pcmpgtb", MX, EM, XX },
+ { "pcmpgtw", MX, EM, XX },
+ { "pcmpgtd", MX, EM, XX },
+ { "packuswb", MX, EM, XX },
+ /* 68 */
+ { "punpckhbw", MX, EM, XX },
+ { "punpckhwd", MX, EM, XX },
+ { "punpckhdq", MX, EM, XX },
+ { "packssdw", MX, EM, XX },
+ { PREGRP26 },
+ { PREGRP24 },
+ { "movd", MX, Ed, XX },
+ { PREGRP19 },
+ /* 70 */
+ { PREGRP22 },
+ { GRP10 },
+ { GRP11 },
+ { GRP12 },
+ { "pcmpeqb", MX, EM, XX },
+ { "pcmpeqw", MX, EM, XX },
+ { "pcmpeqd", MX, EM, XX },
+ { "emms", XX, XX, XX },
+ /* 78 */
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { PREGRP23 },
+ { PREGRP20 },
+ /* 80 */
+ { "joH", Jv, XX, cond_jump_flag },
+ { "jnoH", Jv, XX, cond_jump_flag },
+ { "jbH", Jv, XX, cond_jump_flag },
+ { "jaeH", Jv, XX, cond_jump_flag },
+ { "jeH", Jv, XX, cond_jump_flag },
+ { "jneH", Jv, XX, cond_jump_flag },
+ { "jbeH", Jv, XX, cond_jump_flag },
+ { "jaH", Jv, XX, cond_jump_flag },
+ /* 88 */
+ { "jsH", Jv, XX, cond_jump_flag },
+ { "jnsH", Jv, XX, cond_jump_flag },
+ { "jpH", Jv, XX, cond_jump_flag },
+ { "jnpH", Jv, XX, cond_jump_flag },
+ { "jlH", Jv, XX, cond_jump_flag },
+ { "jgeH", Jv, XX, cond_jump_flag },
+ { "jleH", Jv, XX, cond_jump_flag },
+ { "jgH", Jv, XX, cond_jump_flag },
+ /* 90 */
+ { "seto", Eb, XX, XX },
+ { "setno", Eb, XX, XX },
+ { "setb", Eb, XX, XX },
+ { "setae", Eb, XX, XX },
+ { "sete", Eb, XX, XX },
+ { "setne", Eb, XX, XX },
+ { "setbe", Eb, XX, XX },
+ { "seta", Eb, XX, XX },
+ /* 98 */
+ { "sets", Eb, XX, XX },
+ { "setns", Eb, XX, XX },
+ { "setp", Eb, XX, XX },
+ { "setnp", Eb, XX, XX },
+ { "setl", Eb, XX, XX },
+ { "setge", Eb, XX, XX },
+ { "setle", Eb, XX, XX },
+ { "setg", Eb, XX, XX },
+ /* a0 */
+ { "pushT", fs, XX, XX },
+ { "popT", fs, XX, XX },
+ { "cpuid", XX, XX, XX },
+ { "btS", Ev, Gv, XX },
+ { "shldS", Ev, Gv, Ib },
+ { "shldS", Ev, Gv, CL },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ /* a8 */
+ { "pushT", gs, XX, XX },
+ { "popT", gs, XX, XX },
+ { "rsm", XX, XX, XX },
+ { "btsS", Ev, Gv, XX },
+ { "shrdS", Ev, Gv, Ib },
+ { "shrdS", Ev, Gv, CL },
+ { GRP13 },
+ { "imulS", Gv, Ev, XX },
+ /* b0 */
+ { "cmpxchgB", Eb, Gb, XX },
+ { "cmpxchgS", Ev, Gv, XX },
+ { "lssS", Gv, Mp, XX },
+ { "btrS", Ev, Gv, XX },
+ { "lfsS", Gv, Mp, XX },
+ { "lgsS", Gv, Mp, XX },
+ { "movz{bR|x|bR|x}", Gv, Eb, XX },
+ { "movz{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movzww ! */
+ /* b8 */
+ { "(bad)", XX, XX, XX },
+ { "ud2b", XX, XX, XX },
+ { GRP8 },
+ { "btcS", Ev, Gv, XX },
+ { "bsfS", Gv, Ev, XX },
+ { "bsrS", Gv, Ev, XX },
+ { "movs{bR|x|bR|x}", Gv, Eb, XX },
+ { "movs{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movsww ! */
+ /* c0 */
+ { "xaddB", Eb, Gb, XX },
+ { "xaddS", Ev, Gv, XX },
+ { PREGRP1 },
+ { "movntiS", Ev, Gv, XX },
+ { "pinsrw", MX, Ed, Ib },
+ { "pextrw", Gd, MS, Ib },
+ { "shufpX", XM, EX, Ib },
+ { GRP9 },
+ /* c8 */
+ { "bswap", RMeAX, XX, XX },
+ { "bswap", RMeCX, XX, XX },
+ { "bswap", RMeDX, XX, XX },
+ { "bswap", RMeBX, XX, XX },
+ { "bswap", RMeSP, XX, XX },
+ { "bswap", RMeBP, XX, XX },
+ { "bswap", RMeSI, XX, XX },
+ { "bswap", RMeDI, XX, XX },
+ /* d0 */
+ { "(bad)", XX, XX, XX },
+ { "psrlw", MX, EM, XX },
+ { "psrld", MX, EM, XX },
+ { "psrlq", MX, EM, XX },
+ { "paddq", MX, EM, XX },
+ { "pmullw", MX, EM, XX },
+ { PREGRP21 },
+ { "pmovmskb", Gd, MS, XX },
+ /* d8 */
+ { "psubusb", MX, EM, XX },
+ { "psubusw", MX, EM, XX },
+ { "pminub", MX, EM, XX },
+ { "pand", MX, EM, XX },
+ { "paddusb", MX, EM, XX },
+ { "paddusw", MX, EM, XX },
+ { "pmaxub", MX, EM, XX },
+ { "pandn", MX, EM, XX },
+ /* e0 */
+ { "pavgb", MX, EM, XX },
+ { "psraw", MX, EM, XX },
+ { "psrad", MX, EM, XX },
+ { "pavgw", MX, EM, XX },
+ { "pmulhuw", MX, EM, XX },
+ { "pmulhw", MX, EM, XX },
+ { PREGRP15 },
+ { PREGRP25 },
+ /* e8 */
+ { "psubsb", MX, EM, XX },
+ { "psubsw", MX, EM, XX },
+ { "pminsw", MX, EM, XX },
+ { "por", MX, EM, XX },
+ { "paddsb", MX, EM, XX },
+ { "paddsw", MX, EM, XX },
+ { "pmaxsw", MX, EM, XX },
+ { "pxor", MX, EM, XX },
+ /* f0 */
+ { "(bad)", XX, XX, XX },
+ { "psllw", MX, EM, XX },
+ { "pslld", MX, EM, XX },
+ { "psllq", MX, EM, XX },
+ { "pmuludq", MX, EM, XX },
+ { "pmaddwd", MX, EM, XX },
+ { "psadbw", MX, EM, XX },
+ { PREGRP18 },
+ /* f8 */
+ { "psubb", MX, EM, XX },
+ { "psubw", MX, EM, XX },
+ { "psubd", MX, EM, XX },
+ { "psubq", MX, EM, XX },
+ { "paddb", MX, EM, XX },
+ { "paddw", MX, EM, XX },
+ { "paddd", MX, EM, XX },
+ { "(bad)", XX, XX, XX }
+};
+
+static const unsigned char onebyte_has_modrm[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */
+ /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */
+ /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */
+ /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
+ /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
+ /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */
+ /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */
+ /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */
+ /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_has_modrm[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */
+ /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */
+ /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */
+ /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */
+ /* 70 */ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */
+ /* a0 */ 0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1, /* af */
+ /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */
+ /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */
+ /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */
+ /* f0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_uses_SSE_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */
+ /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static char obuf[100];
+static char *obufp;
+static char scratchbuf[100];
+static unsigned char *start_codep;
+static unsigned char *insn_codep;
+static unsigned char *codep;
+static disassemble_info *the_info;
+static int mod;
+static int rm;
+static int reg;
+static unsigned char need_modrm;
+
+/* If we are accessing mod/rm/reg without need_modrm set, then the
+ values are stale. Hitting this abort likely indicates that you
+ need to update onebyte_has_modrm or twobyte_has_modrm. */
+#define MODRM_CHECK if (!need_modrm) abort ()
+
+static const char **names64;
+static const char **names32;
+static const char **names16;
+static const char **names8;
+static const char **names8rex;
+static const char **names_seg;
+static const char **index16;
+
+static const char *intel_names64[] = {
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+static const char *intel_names32[] = {
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
+};
+static const char *intel_names16[] = {
+ "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"
+};
+static const char *intel_names8[] = {
+ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+};
+static const char *intel_names8rex[] = {
+ "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"
+};
+static const char *intel_names_seg[] = {
+ "es", "cs", "ss", "ds", "fs", "gs", "?", "?",
+};
+static const char *intel_index16[] = {
+ "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"
+};
+
+static const char *att_names64[] = {
+ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
+};
+static const char *att_names32[] = {
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+ "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d"
+};
+static const char *att_names16[] = {
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w"
+};
+static const char *att_names8[] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+};
+static const char *att_names8rex[] = {
+ "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+static const char *att_names_seg[] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?",
+};
+static const char *att_index16[] = {
+ "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx"
+};
+
+static const struct dis386 grps[][8] = {
+ /* GRP1b */
+ {
+ { "addA", Eb, Ib, XX },
+ { "orA", Eb, Ib, XX },
+ { "adcA", Eb, Ib, XX },
+ { "sbbA", Eb, Ib, XX },
+ { "andA", Eb, Ib, XX },
+ { "subA", Eb, Ib, XX },
+ { "xorA", Eb, Ib, XX },
+ { "cmpA", Eb, Ib, XX }
+ },
+ /* GRP1S */
+ {
+ { "addQ", Ev, Iv, XX },
+ { "orQ", Ev, Iv, XX },
+ { "adcQ", Ev, Iv, XX },
+ { "sbbQ", Ev, Iv, XX },
+ { "andQ", Ev, Iv, XX },
+ { "subQ", Ev, Iv, XX },
+ { "xorQ", Ev, Iv, XX },
+ { "cmpQ", Ev, Iv, XX }
+ },
+ /* GRP1Ss */
+ {
+ { "addQ", Ev, sIb, XX },
+ { "orQ", Ev, sIb, XX },
+ { "adcQ", Ev, sIb, XX },
+ { "sbbQ", Ev, sIb, XX },
+ { "andQ", Ev, sIb, XX },
+ { "subQ", Ev, sIb, XX },
+ { "xorQ", Ev, sIb, XX },
+ { "cmpQ", Ev, sIb, XX }
+ },
+ /* GRP2b */
+ {
+ { "rolA", Eb, Ib, XX },
+ { "rorA", Eb, Ib, XX },
+ { "rclA", Eb, Ib, XX },
+ { "rcrA", Eb, Ib, XX },
+ { "shlA", Eb, Ib, XX },
+ { "shrA", Eb, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "sarA", Eb, Ib, XX },
+ },
+ /* GRP2S */
+ {
+ { "rolQ", Ev, Ib, XX },
+ { "rorQ", Ev, Ib, XX },
+ { "rclQ", Ev, Ib, XX },
+ { "rcrQ", Ev, Ib, XX },
+ { "shlQ", Ev, Ib, XX },
+ { "shrQ", Ev, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "sarQ", Ev, Ib, XX },
+ },
+ /* GRP2b_one */
+ {
+ { "rolA", Eb, XX, XX },
+ { "rorA", Eb, XX, XX },
+ { "rclA", Eb, XX, XX },
+ { "rcrA", Eb, XX, XX },
+ { "shlA", Eb, XX, XX },
+ { "shrA", Eb, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "sarA", Eb, XX, XX },
+ },
+ /* GRP2S_one */
+ {
+ { "rolQ", Ev, XX, XX },
+ { "rorQ", Ev, XX, XX },
+ { "rclQ", Ev, XX, XX },
+ { "rcrQ", Ev, XX, XX },
+ { "shlQ", Ev, XX, XX },
+ { "shrQ", Ev, XX, XX },
+ { "(bad)", XX, XX, XX},
+ { "sarQ", Ev, XX, XX },
+ },
+ /* GRP2b_cl */
+ {
+ { "rolA", Eb, CL, XX },
+ { "rorA", Eb, CL, XX },
+ { "rclA", Eb, CL, XX },
+ { "rcrA", Eb, CL, XX },
+ { "shlA", Eb, CL, XX },
+ { "shrA", Eb, CL, XX },
+ { "(bad)", XX, XX, XX },
+ { "sarA", Eb, CL, XX },
+ },
+ /* GRP2S_cl */
+ {
+ { "rolQ", Ev, CL, XX },
+ { "rorQ", Ev, CL, XX },
+ { "rclQ", Ev, CL, XX },
+ { "rcrQ", Ev, CL, XX },
+ { "shlQ", Ev, CL, XX },
+ { "shrQ", Ev, CL, XX },
+ { "(bad)", XX, XX, XX },
+ { "sarQ", Ev, CL, XX }
+ },
+ /* GRP3b */
+ {
+ { "testA", Eb, Ib, XX },
+ { "(bad)", Eb, XX, XX },
+ { "notA", Eb, XX, XX },
+ { "negA", Eb, XX, XX },
+ { "mulA", Eb, XX, XX }, /* Don't print the implicit %al register, */
+ { "imulA", Eb, XX, XX }, /* to distinguish these opcodes from other */
+ { "divA", Eb, XX, XX }, /* mul/imul opcodes. Do the same for div */
+ { "idivA", Eb, XX, XX } /* and idiv for consistency. */
+ },
+ /* GRP3S */
+ {
+ { "testQ", Ev, Iv, XX },
+ { "(bad)", XX, XX, XX },
+ { "notQ", Ev, XX, XX },
+ { "negQ", Ev, XX, XX },
+ { "mulQ", Ev, XX, XX }, /* Don't print the implicit register. */
+ { "imulQ", Ev, XX, XX },
+ { "divQ", Ev, XX, XX },
+ { "idivQ", Ev, XX, XX },
+ },
+ /* GRP4 */
+ {
+ { "incA", Eb, XX, XX },
+ { "decA", Eb, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRP5 */
+ {
+ { "incQ", Ev, XX, XX },
+ { "decQ", Ev, XX, XX },
+ { "callT", indirEv, XX, XX },
+ { "lcallT", indirEv, XX, XX },
+ { "jmpT", indirEv, XX, XX },
+ { "ljmpT", indirEv, XX, XX },
+ { "pushU", Ev, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRP6 */
+ {
+ { "sldtQ", Ev, XX, XX },
+ { "strQ", Ev, XX, XX },
+ { "lldt", Ew, XX, XX },
+ { "ltr", Ew, XX, XX },
+ { "verr", Ew, XX, XX },
+ { "verw", Ew, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX }
+ },
+ /* GRP7 */
+ {
+ { "sgdtQ", M, XX, XX },
+ { "sidtQ", M, XX, XX },
+ { "lgdtQ", M, XX, XX },
+ { "lidtQ", M, XX, XX },
+ { "smswQ", Ev, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "lmsw", Ew, XX, XX },
+ { "invlpg", Ew, XX, XX },
+ },
+ /* GRP8 */
+ {
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "btQ", Ev, Ib, XX },
+ { "btsQ", Ev, Ib, XX },
+ { "btrQ", Ev, Ib, XX },
+ { "btcQ", Ev, Ib, XX },
+ },
+ /* GRP9 */
+ {
+ { "(bad)", XX, XX, XX },
+ { "cmpxchg8b", Ev, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRP10 */
+ {
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "psrlw", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "psraw", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "psllw", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRP11 */
+ {
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "psrld", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "psrad", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "pslld", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRP12 */
+ {
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "psrlq", MS, Ib, XX },
+ { "psrldq", MS, Ib, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "psllq", MS, Ib, XX },
+ { "pslldq", MS, Ib, XX },
+ },
+ /* GRP13 */
+ {
+ { "fxsave", Ev, XX, XX },
+ { "fxrstor", Ev, XX, XX },
+ { "ldmxcsr", Ev, XX, XX },
+ { "stmxcsr", Ev, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "lfence", None, XX, XX },
+ { "mfence", None, XX, XX },
+ { "sfence", None, XX, XX },
+ /* FIXME: the sfence with memory operand is clflush! */
+ },
+ /* GRP14 */
+ {
+ { "prefetchnta", Ev, XX, XX },
+ { "prefetcht0", Ev, XX, XX },
+ { "prefetcht1", Ev, XX, XX },
+ { "prefetcht2", Ev, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* GRPAMD */
+ {
+ { "prefetch", Eb, XX, XX },
+ { "prefetchw", Eb, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ }
+};
+
+static const struct dis386 prefix_user_table[][4] = {
+ /* PREGRP0 */
+ {
+ { "addps", XM, EX, XX },
+ { "addss", XM, EX, XX },
+ { "addpd", XM, EX, XX },
+ { "addsd", XM, EX, XX },
+ },
+ /* PREGRP1 */
+ {
+ { "", XM, EX, OPSIMD }, /* See OP_SIMD_SUFFIX. */
+ { "", XM, EX, OPSIMD },
+ { "", XM, EX, OPSIMD },
+ { "", XM, EX, OPSIMD },
+ },
+ /* PREGRP2 */
+ {
+ { "cvtpi2ps", XM, EM, XX },
+ { "cvtsi2ssY", XM, Ev, XX },
+ { "cvtpi2pd", XM, EM, XX },
+ { "cvtsi2sdY", XM, Ev, XX },
+ },
+ /* PREGRP3 */
+ {
+ { "cvtps2pi", MX, EX, XX },
+ { "cvtss2siY", Gv, EX, XX },
+ { "cvtpd2pi", MX, EX, XX },
+ { "cvtsd2siY", Gv, EX, XX },
+ },
+ /* PREGRP4 */
+ {
+ { "cvttps2pi", MX, EX, XX },
+ { "cvttss2siY", Gv, EX, XX },
+ { "cvttpd2pi", MX, EX, XX },
+ { "cvttsd2siY", Gv, EX, XX },
+ },
+ /* PREGRP5 */
+ {
+ { "divps", XM, EX, XX },
+ { "divss", XM, EX, XX },
+ { "divpd", XM, EX, XX },
+ { "divsd", XM, EX, XX },
+ },
+ /* PREGRP6 */
+ {
+ { "maxps", XM, EX, XX },
+ { "maxss", XM, EX, XX },
+ { "maxpd", XM, EX, XX },
+ { "maxsd", XM, EX, XX },
+ },
+ /* PREGRP7 */
+ {
+ { "minps", XM, EX, XX },
+ { "minss", XM, EX, XX },
+ { "minpd", XM, EX, XX },
+ { "minsd", XM, EX, XX },
+ },
+ /* PREGRP8 */
+ {
+ { "movups", XM, EX, XX },
+ { "movss", XM, EX, XX },
+ { "movupd", XM, EX, XX },
+ { "movsd", XM, EX, XX },
+ },
+ /* PREGRP9 */
+ {
+ { "movups", EX, XM, XX },
+ { "movss", EX, XM, XX },
+ { "movupd", EX, XM, XX },
+ { "movsd", EX, XM, XX },
+ },
+ /* PREGRP10 */
+ {
+ { "mulps", XM, EX, XX },
+ { "mulss", XM, EX, XX },
+ { "mulpd", XM, EX, XX },
+ { "mulsd", XM, EX, XX },
+ },
+ /* PREGRP11 */
+ {
+ { "rcpps", XM, EX, XX },
+ { "rcpss", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP12 */
+ {
+ { "rsqrtps", XM, EX, XX },
+ { "rsqrtss", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP13 */
+ {
+ { "sqrtps", XM, EX, XX },
+ { "sqrtss", XM, EX, XX },
+ { "sqrtpd", XM, EX, XX },
+ { "sqrtsd", XM, EX, XX },
+ },
+ /* PREGRP14 */
+ {
+ { "subps", XM, EX, XX },
+ { "subss", XM, EX, XX },
+ { "subpd", XM, EX, XX },
+ { "subsd", XM, EX, XX },
+ },
+ /* PREGRP15 */
+ {
+ { "(bad)", XM, EX, XX },
+ { "cvtdq2pd", XM, EX, XX },
+ { "cvttpd2dq", XM, EX, XX },
+ { "cvtpd2dq", XM, EX, XX },
+ },
+ /* PREGRP16 */
+ {
+ { "cvtdq2ps", XM, EX, XX },
+ { "cvttps2dq",XM, EX, XX },
+ { "cvtps2dq",XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP17 */
+ {
+ { "cvtps2pd", XM, EX, XX },
+ { "cvtss2sd", XM, EX, XX },
+ { "cvtpd2ps", XM, EX, XX },
+ { "cvtsd2ss", XM, EX, XX },
+ },
+ /* PREGRP18 */
+ {
+ { "maskmovq", MX, MS, XX },
+ { "(bad)", XM, EX, XX },
+ { "maskmovdqu", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP19 */
+ {
+ { "movq", MX, EM, XX },
+ { "movdqu", XM, EX, XX },
+ { "movdqa", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP20 */
+ {
+ { "movq", EM, MX, XX },
+ { "movdqu", EX, XM, XX },
+ { "movdqa", EX, XM, XX },
+ { "(bad)", EX, XM, XX },
+ },
+ /* PREGRP21 */
+ {
+ { "(bad)", EX, XM, XX },
+ { "movq2dq", XM, MS, XX },
+ { "movq", EX, XM, XX },
+ { "movdq2q", MX, XS, XX },
+ },
+ /* PREGRP22 */
+ {
+ { "pshufw", MX, EM, Ib },
+ { "pshufhw", XM, EX, Ib },
+ { "pshufd", XM, EX, Ib },
+ { "pshuflw", XM, EX, Ib },
+ },
+ /* PREGRP23 */
+ {
+ { "movd", Ed, MX, XX },
+ { "movq", XM, EX, XX },
+ { "movd", Ed, XM, XX },
+ { "(bad)", Ed, XM, XX },
+ },
+ /* PREGRP24 */
+ {
+ { "(bad)", MX, EX, XX },
+ { "(bad)", XM, EX, XX },
+ { "punpckhqdq", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+ /* PREGRP25 */
+ {
+ { "movntq", Ev, MX, XX },
+ { "(bad)", Ev, XM, XX },
+ { "movntdq", Ev, XM, XX },
+ { "(bad)", Ev, XM, XX },
+ },
+ /* PREGRP26 */
+ {
+ { "(bad)", MX, EX, XX },
+ { "(bad)", XM, EX, XX },
+ { "punpcklqdq", XM, EX, XX },
+ { "(bad)", XM, EX, XX },
+ },
+};
+
+static const struct dis386 x86_64_table[][2] = {
+ {
+ { "arpl", Ew, Gw, XX },
+ { "movs{||lq|xd}", Gv, Ed, XX },
+ },
+};
+
+#define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>")
+
+static void
+ckprefix ()
+{
+ int newrex;
+ rex = 0;
+ prefixes = 0;
+ used_prefixes = 0;
+ rex_used = 0;
+ while (1)
+ {
+ FETCH_DATA (the_info, codep + 1);
+ newrex = 0;
+ switch (*codep)
+ {
+ /* REX prefixes family. */
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ if (mode_64bit)
+ newrex = *codep;
+ else
+ return;
+ break;
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ break;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ break;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ break;
+ case 0x2e:
+ prefixes |= PREFIX_CS;
+ break;
+ case 0x36:
+ prefixes |= PREFIX_SS;
+ break;
+ case 0x3e:
+ prefixes |= PREFIX_DS;
+ break;
+ case 0x26:
+ prefixes |= PREFIX_ES;
+ break;
+ case 0x64:
+ prefixes |= PREFIX_FS;
+ break;
+ case 0x65:
+ prefixes |= PREFIX_GS;
+ break;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ break;
+ case 0x67:
+ prefixes |= PREFIX_ADDR;
+ break;
+ case FWAIT_OPCODE:
+ /* fwait is really an instruction. If there are prefixes
+ before the fwait, they belong to the fwait, *not* to the
+ following instruction. */
+ if (prefixes)
+ {
+ prefixes |= PREFIX_FWAIT;
+ codep++;
+ return;
+ }
+ prefixes = PREFIX_FWAIT;
+ break;
+ default:
+ return;
+ }
+ /* Rex is ignored when followed by another prefix. */
+ if (rex)
+ {
+ oappend (prefix_name (rex, 0));
+ oappend (" ");
+ }
+ rex = newrex;
+ codep++;
+ }
+}
+
+/* Return the name of the prefix byte PREF, or NULL if PREF is not a
+ prefix byte. */
+
+static const char *
+prefix_name (pref, sizeflag)
+ int pref;
+ int sizeflag;
+{
+ switch (pref)
+ {
+ /* REX prefixes family. */
+ case 0x40:
+ return "rex";
+ case 0x41:
+ return "rexZ";
+ case 0x42:
+ return "rexY";
+ case 0x43:
+ return "rexYZ";
+ case 0x44:
+ return "rexX";
+ case 0x45:
+ return "rexXZ";
+ case 0x46:
+ return "rexXY";
+ case 0x47:
+ return "rexXYZ";
+ case 0x48:
+ return "rex64";
+ case 0x49:
+ return "rex64Z";
+ case 0x4a:
+ return "rex64Y";
+ case 0x4b:
+ return "rex64YZ";
+ case 0x4c:
+ return "rex64X";
+ case 0x4d:
+ return "rex64XZ";
+ case 0x4e:
+ return "rex64XY";
+ case 0x4f:
+ return "rex64XYZ";
+ case 0xf3:
+ return "repz";
+ case 0xf2:
+ return "repnz";
+ case 0xf0:
+ return "lock";
+ case 0x2e:
+ return "cs";
+ case 0x36:
+ return "ss";
+ case 0x3e:
+ return "ds";
+ case 0x26:
+ return "es";
+ case 0x64:
+ return "fs";
+ case 0x65:
+ return "gs";
+ case 0x66:
+ return (sizeflag & DFLAG) ? "data16" : "data32";
+ case 0x67:
+ if (mode_64bit)
+ return (sizeflag & AFLAG) ? "addr32" : "addr64";
+ else
+ return ((sizeflag & AFLAG) && !mode_64bit) ? "addr16" : "addr32";
+ case FWAIT_OPCODE:
+ return "fwait";
+ default:
+ return NULL;
+ }
+}
+
+static char op1out[100], op2out[100], op3out[100];
+static int op_ad, op_index[3];
+static bfd_vma op_address[3];
+static bfd_vma op_riprel[3];
+static bfd_vma start_pc;
+
+/*
+ * On the 386's of 1988, the maximum length of an instruction is 15 bytes.
+ * (see topic "Redundant prefixes" in the "Differences from 8086"
+ * section of the "Virtual 8086 Mode" chapter.)
+ * 'pc' should be the address of this instruction, it will
+ * be used to print the target address if this is a relative jump or call
+ * The function returns the length of this instruction in bytes.
+ */
+
+static int8_t intel_syntax;
+static char open_char;
+static char close_char;
+static char separator_char;
+static char scale_char;
+
+/* Here for backwards compatibility. When gdb stops using
+ print_insn_i386_att and print_insn_i386_intel these functions can
+ disappear, and print_insn_i386 be merged into print_insn. */
+int
+print_insn_i386_att (pc, info)
+ bfd_vma pc;
+ disassemble_info *info;
+{
+ intel_syntax = 0;
+
+ return print_insn (pc, info);
+}
+
+int
+print_insn_i386_intel (pc, info)
+ bfd_vma pc;
+ disassemble_info *info;
+{
+ intel_syntax = 1;
+
+ return print_insn (pc, info);
+}
+
+int
+print_insn_i386 (pc, info)
+ bfd_vma pc;
+ disassemble_info *info;
+{
+ intel_syntax = -1;
+
+ return print_insn (pc, info);
+}
+
+static int
+print_insn (pc, info)
+ bfd_vma pc;
+ disassemble_info *info;
+{
+ const struct dis386 *dp;
+ int i;
+ int two_source_ops;
+ char *first, *second, *third;
+ int needcomma;
+ unsigned char uses_SSE_prefix;
+ int sizeflag;
+ const char *p;
+ struct dis_private priv;
+
+ mode_64bit = (info->mach == bfd_mach_x86_64_intel_syntax
+ || info->mach == bfd_mach_x86_64);
+
+ if (intel_syntax == -1)
+ intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax
+ || info->mach == bfd_mach_x86_64_intel_syntax);
+
+ if (info->mach == bfd_mach_i386_i386
+ || info->mach == bfd_mach_x86_64
+ || info->mach == bfd_mach_i386_i386_intel_syntax
+ || info->mach == bfd_mach_x86_64_intel_syntax)
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ else if (info->mach == bfd_mach_i386_i8086)
+ priv.orig_sizeflag = 0;
+ else
+ abort ();
+
+ for (p = info->disassembler_options; p != NULL; )
+ {
+ if (strncmp (p, "x86-64", 6) == 0)
+ {
+ mode_64bit = 1;
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ }
+ else if (strncmp (p, "i386", 4) == 0)
+ {
+ mode_64bit = 0;
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ }
+ else if (strncmp (p, "i8086", 5) == 0)
+ {
+ mode_64bit = 0;
+ priv.orig_sizeflag = 0;
+ }
+ else if (strncmp (p, "intel", 5) == 0)
+ {
+ intel_syntax = 1;
+ }
+ else if (strncmp (p, "att", 3) == 0)
+ {
+ intel_syntax = 0;
+ }
+ else if (strncmp (p, "addr", 4) == 0)
+ {
+ if (p[4] == '1' && p[5] == '6')
+ priv.orig_sizeflag &= ~AFLAG;
+ else if (p[4] == '3' && p[5] == '2')
+ priv.orig_sizeflag |= AFLAG;
+ }
+ else if (strncmp (p, "data", 4) == 0)
+ {
+ if (p[4] == '1' && p[5] == '6')
+ priv.orig_sizeflag &= ~DFLAG;
+ else if (p[4] == '3' && p[5] == '2')
+ priv.orig_sizeflag |= DFLAG;
+ }
+ else if (strncmp (p, "suffix", 6) == 0)
+ priv.orig_sizeflag |= SUFFIX_ALWAYS;
+
+ p = strchr (p, ',');
+ if (p != NULL)
+ p++;
+ }
+
+ if (intel_syntax)
+ {
+ names64 = intel_names64;
+ names32 = intel_names32;
+ names16 = intel_names16;
+ names8 = intel_names8;
+ names8rex = intel_names8rex;
+ names_seg = intel_names_seg;
+ index16 = intel_index16;
+ open_char = '[';
+ close_char = ']';
+ separator_char = '+';
+ scale_char = '*';
+ }
+ else
+ {
+ names64 = att_names64;
+ names32 = att_names32;
+ names16 = att_names16;
+ names8 = att_names8;
+ names8rex = att_names8rex;
+ names_seg = att_names_seg;
+ index16 = att_index16;
+ open_char = '(';
+ close_char = ')';
+ separator_char = ',';
+ scale_char = ',';
+ }
+
+ /* The output looks better if we put 7 bytes on a line, since that
+ puts most long word instructions on a single line. */
+ info->bytes_per_line = 7;
+
+ info->private_data = (PTR) &priv;
+ priv.max_fetched = priv.the_buffer;
+ priv.insn_start = pc;
+
+ obuf[0] = 0;
+ op1out[0] = 0;
+ op2out[0] = 0;
+ op3out[0] = 0;
+
+ op_index[0] = op_index[1] = op_index[2] = -1;
+
+ the_info = info;
+ start_pc = pc;
+ start_codep = priv.the_buffer;
+ codep = priv.the_buffer;
+
+ if (setjmp (priv.bailout) != 0)
+ {
+ const char *name;
+
+ /* Getting here means we tried for data but didn't get it. That
+ means we have an incomplete instruction of some sort. Just
+ print the first byte as a prefix or a .byte pseudo-op. */
+ if (codep > priv.the_buffer)
+ {
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name != NULL)
+ (*info->fprintf_func) (info->stream, "%s", name);
+ else
+ {
+ /* Just print the first byte as a .byte instruction. */
+ (*info->fprintf_func) (info->stream, ".byte 0x%x",
+ (unsigned int) priv.the_buffer[0]);
+ }
+
+ return 1;
+ }
+
+ return -1;
+ }
+
+ obufp = obuf;
+ ckprefix ();
+
+ insn_codep = codep;
+ sizeflag = priv.orig_sizeflag;
+
+ FETCH_DATA (info, codep + 1);
+ two_source_ops = (*codep == 0x62) || (*codep == 0xc8);
+
+ if ((prefixes & PREFIX_FWAIT)
+ && ((*codep < 0xd8) || (*codep > 0xdf)))
+ {
+ const char *name;
+
+ /* fwait not followed by floating point instruction. Print the
+ first prefix, which is probably fwait itself. */
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s", name);
+ return 1;
+ }
+
+ if (*codep == 0x0f)
+ {
+ FETCH_DATA (info, codep + 2);
+ dp = &dis386_twobyte[*++codep];
+ need_modrm = twobyte_has_modrm[*codep];
+ uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep];
+ }
+ else
+ {
+ dp = &dis386[*codep];
+ need_modrm = onebyte_has_modrm[*codep];
+ uses_SSE_prefix = 0;
+ }
+ codep++;
+
+ if (!uses_SSE_prefix && (prefixes & PREFIX_REPZ))
+ {
+ oappend ("repz ");
+ used_prefixes |= PREFIX_REPZ;
+ }
+ if (!uses_SSE_prefix && (prefixes & PREFIX_REPNZ))
+ {
+ oappend ("repnz ");
+ used_prefixes |= PREFIX_REPNZ;
+ }
+ if (prefixes & PREFIX_LOCK)
+ {
+ oappend ("lock ");
+ used_prefixes |= PREFIX_LOCK;
+ }
+
+ if (prefixes & PREFIX_ADDR)
+ {
+ sizeflag ^= AFLAG;
+ if (dp->bytemode3 != loop_jcxz_mode || intel_syntax)
+ {
+ if ((sizeflag & AFLAG) || mode_64bit)
+ oappend ("addr32 ");
+ else
+ oappend ("addr16 ");
+ used_prefixes |= PREFIX_ADDR;
+ }
+ }
+
+ if (!uses_SSE_prefix && (prefixes & PREFIX_DATA))
+ {
+ sizeflag ^= DFLAG;
+ if (dp->bytemode3 == cond_jump_mode
+ && dp->bytemode1 == v_mode
+ && !intel_syntax)
+ {
+ if (sizeflag & DFLAG)
+ oappend ("data32 ");
+ else
+ oappend ("data16 ");
+ used_prefixes |= PREFIX_DATA;
+ }
+ }
+
+ if (need_modrm)
+ {
+ FETCH_DATA (info, codep + 1);
+ mod = (*codep >> 6) & 3;
+ reg = (*codep >> 3) & 7;
+ rm = *codep & 7;
+ }
+
+ if (dp->name == NULL && dp->bytemode1 == FLOATCODE)
+ {
+ dofloat (sizeflag);
+ }
+ else
+ {
+ int index;
+ if (dp->name == NULL)
+ {
+ switch (dp->bytemode1)
+ {
+ case USE_GROUPS:
+ dp = &grps[dp->bytemode2][reg];
+ break;
+
+ case USE_PREFIX_USER_TABLE:
+ index = 0;
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ if (prefixes & PREFIX_REPZ)
+ index = 1;
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ index = 2;
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_REPNZ);
+ if (prefixes & PREFIX_REPNZ)
+ index = 3;
+ }
+ }
+ dp = &prefix_user_table[dp->bytemode2][index];
+ break;
+
+ case X86_64_SPECIAL:
+ dp = &x86_64_table[dp->bytemode2][mode_64bit];
+ break;
+
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+ }
+
+ if (putop (dp->name, sizeflag) == 0)
+ {
+ obufp = op1out;
+ op_ad = 2;
+ if (dp->op1)
+ (*dp->op1) (dp->bytemode1, sizeflag);
+
+ obufp = op2out;
+ op_ad = 1;
+ if (dp->op2)
+ (*dp->op2) (dp->bytemode2, sizeflag);
+
+ obufp = op3out;
+ op_ad = 0;
+ if (dp->op3)
+ (*dp->op3) (dp->bytemode3, sizeflag);
+ }
+ }
+
+ /* See if any prefixes were not used. If so, print the first one
+ separately. If we don't do this, we'll wind up printing an
+ instruction stream which does not precisely correspond to the
+ bytes we are disassembling. */
+ if ((prefixes & ~used_prefixes) != 0)
+ {
+ const char *name;
+
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s", name);
+ return 1;
+ }
+ if (rex & ~rex_used)
+ {
+ const char *name;
+ name = prefix_name (rex | 0x40, priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s ", name);
+ }
+
+ obufp = obuf + strlen (obuf);
+ for (i = strlen (obuf); i < 6; i++)
+ oappend (" ");
+ oappend (" ");
+ (*info->fprintf_func) (info->stream, "%s", obuf);
+
+ /* The enter and bound instructions are printed with operands in the same
+ order as the intel book; everything else is printed in reverse order. */
+ if (intel_syntax || two_source_ops)
+ {
+ first = op1out;
+ second = op2out;
+ third = op3out;
+ op_ad = op_index[0];
+ op_index[0] = op_index[2];
+ op_index[2] = op_ad;
+ }
+ else
+ {
+ first = op3out;
+ second = op2out;
+ third = op1out;
+ }
+ needcomma = 0;
+ if (*first)
+ {
+ if (op_index[0] != -1 && !op_riprel[0])
+ (*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", first);
+ needcomma = 1;
+ }
+ if (*second)
+ {
+ if (needcomma)
+ (*info->fprintf_func) (info->stream, ",");
+ if (op_index[1] != -1 && !op_riprel[1])
+ (*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", second);
+ needcomma = 1;
+ }
+ if (*third)
+ {
+ if (needcomma)
+ (*info->fprintf_func) (info->stream, ",");
+ if (op_index[2] != -1 && !op_riprel[2])
+ (*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", third);
+ }
+ for (i = 0; i < 3; i++)
+ if (op_index[i] != -1 && op_riprel[i])
+ {
+ (*info->fprintf_func) (info->stream, " # ");
+ (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep
+ + op_address[op_index[i]]), info);
+ }
+ return codep - priv.the_buffer;
+}
+
+static const char *float_mem[] = {
+ /* d8 */
+ "fadd{s||s|}",
+ "fmul{s||s|}",
+ "fcom{s||s|}",
+ "fcomp{s||s|}",
+ "fsub{s||s|}",
+ "fsubr{s||s|}",
+ "fdiv{s||s|}",
+ "fdivr{s||s|}",
+ /* d9 */
+ "fld{s||s|}",
+ "(bad)",
+ "fst{s||s|}",
+ "fstp{s||s|}",
+ "fldenv",
+ "fldcw",
+ "fNstenv",
+ "fNstcw",
+ /* da */
+ "fiadd{l||l|}",
+ "fimul{l||l|}",
+ "ficom{l||l|}",
+ "ficomp{l||l|}",
+ "fisub{l||l|}",
+ "fisubr{l||l|}",
+ "fidiv{l||l|}",
+ "fidivr{l||l|}",
+ /* db */
+ "fild{l||l|}",
+ "(bad)",
+ "fist{l||l|}",
+ "fistp{l||l|}",
+ "(bad)",
+ "fld{t||t|}",
+ "(bad)",
+ "fstp{t||t|}",
+ /* dc */
+ "fadd{l||l|}",
+ "fmul{l||l|}",
+ "fcom{l||l|}",
+ "fcomp{l||l|}",
+ "fsub{l||l|}",
+ "fsubr{l||l|}",
+ "fdiv{l||l|}",
+ "fdivr{l||l|}",
+ /* dd */
+ "fld{l||l|}",
+ "(bad)",
+ "fst{l||l|}",
+ "fstp{l||l|}",
+ "frstor",
+ "(bad)",
+ "fNsave",
+ "fNstsw",
+ /* de */
+ "fiadd",
+ "fimul",
+ "ficom",
+ "ficomp",
+ "fisub",
+ "fisubr",
+ "fidiv",
+ "fidivr",
+ /* df */
+ "fild",
+ "(bad)",
+ "fist",
+ "fistp",
+ "fbld",
+ "fild{ll||ll|}",
+ "fbstp",
+ "fistpll",
+};
+
+#define ST OP_ST, 0
+#define STi OP_STi, 0
+
+#define FGRPd9_2 NULL, NULL, 0, NULL, 0, NULL, 0
+#define FGRPd9_4 NULL, NULL, 1, NULL, 0, NULL, 0
+#define FGRPd9_5 NULL, NULL, 2, NULL, 0, NULL, 0
+#define FGRPd9_6 NULL, NULL, 3, NULL, 0, NULL, 0
+#define FGRPd9_7 NULL, NULL, 4, NULL, 0, NULL, 0
+#define FGRPda_5 NULL, NULL, 5, NULL, 0, NULL, 0
+#define FGRPdb_4 NULL, NULL, 6, NULL, 0, NULL, 0
+#define FGRPde_3 NULL, NULL, 7, NULL, 0, NULL, 0
+#define FGRPdf_4 NULL, NULL, 8, NULL, 0, NULL, 0
+
+static const struct dis386 float_reg[][8] = {
+ /* d8 */
+ {
+ { "fadd", ST, STi, XX },
+ { "fmul", ST, STi, XX },
+ { "fcom", STi, XX, XX },
+ { "fcomp", STi, XX, XX },
+ { "fsub", ST, STi, XX },
+ { "fsubr", ST, STi, XX },
+ { "fdiv", ST, STi, XX },
+ { "fdivr", ST, STi, XX },
+ },
+ /* d9 */
+ {
+ { "fld", STi, XX, XX },
+ { "fxch", STi, XX, XX },
+ { FGRPd9_2 },
+ { "(bad)", XX, XX, XX },
+ { FGRPd9_4 },
+ { FGRPd9_5 },
+ { FGRPd9_6 },
+ { FGRPd9_7 },
+ },
+ /* da */
+ {
+ { "fcmovb", ST, STi, XX },
+ { "fcmove", ST, STi, XX },
+ { "fcmovbe",ST, STi, XX },
+ { "fcmovu", ST, STi, XX },
+ { "(bad)", XX, XX, XX },
+ { FGRPda_5 },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* db */
+ {
+ { "fcmovnb",ST, STi, XX },
+ { "fcmovne",ST, STi, XX },
+ { "fcmovnbe",ST, STi, XX },
+ { "fcmovnu",ST, STi, XX },
+ { FGRPdb_4 },
+ { "fucomi", ST, STi, XX },
+ { "fcomi", ST, STi, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* dc */
+ {
+ { "fadd", STi, ST, XX },
+ { "fmul", STi, ST, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+#if UNIXWARE_COMPAT
+ { "fsub", STi, ST, XX },
+ { "fsubr", STi, ST, XX },
+ { "fdiv", STi, ST, XX },
+ { "fdivr", STi, ST, XX },
+#else
+ { "fsubr", STi, ST, XX },
+ { "fsub", STi, ST, XX },
+ { "fdivr", STi, ST, XX },
+ { "fdiv", STi, ST, XX },
+#endif
+ },
+ /* dd */
+ {
+ { "ffree", STi, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "fst", STi, XX, XX },
+ { "fstp", STi, XX, XX },
+ { "fucom", STi, XX, XX },
+ { "fucomp", STi, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ },
+ /* de */
+ {
+ { "faddp", STi, ST, XX },
+ { "fmulp", STi, ST, XX },
+ { "(bad)", XX, XX, XX },
+ { FGRPde_3 },
+#if UNIXWARE_COMPAT
+ { "fsubp", STi, ST, XX },
+ { "fsubrp", STi, ST, XX },
+ { "fdivp", STi, ST, XX },
+ { "fdivrp", STi, ST, XX },
+#else
+ { "fsubrp", STi, ST, XX },
+ { "fsubp", STi, ST, XX },
+ { "fdivrp", STi, ST, XX },
+ { "fdivp", STi, ST, XX },
+#endif
+ },
+ /* df */
+ {
+ { "ffreep", STi, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { "(bad)", XX, XX, XX },
+ { FGRPdf_4 },
+ { "fucomip",ST, STi, XX },
+ { "fcomip", ST, STi, XX },
+ { "(bad)", XX, XX, XX },
+ },
+};
+
+static char *fgrps[][8] = {
+ /* d9_2 0 */
+ {
+ "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* d9_4 1 */
+ {
+ "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
+ },
+
+ /* d9_5 2 */
+ {
+ "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
+ },
+
+ /* d9_6 3 */
+ {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
+ },
+
+ /* d9_7 4 */
+ {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
+ },
+
+ /* da_5 5 */
+ {
+ "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* db_4 6 */
+ {
+ "feni(287 only)","fdisi(287 only)","fNclex","fNinit",
+ "fNsetpm(287 only)","(bad)","(bad)","(bad)",
+ },
+
+ /* de_3 7 */
+ {
+ "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* df_4 8 */
+ {
+ "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+};
+
+static void
+dofloat (sizeflag)
+ int sizeflag;
+{
+ const struct dis386 *dp;
+ unsigned char floatop;
+
+ floatop = codep[-1];
+
+ if (mod != 3)
+ {
+ putop (float_mem[(floatop - 0xd8) * 8 + reg], sizeflag);
+ obufp = op1out;
+ if (floatop == 0xdb)
+ OP_E (x_mode, sizeflag);
+ else if (floatop == 0xdd)
+ OP_E (d_mode, sizeflag);
+ else
+ OP_E (v_mode, sizeflag);
+ return;
+ }
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+
+ dp = &float_reg[floatop - 0xd8][reg];
+ if (dp->name == NULL)
+ {
+ putop (fgrps[dp->bytemode1][rm], sizeflag);
+
+ /* Instruction fnstsw is only one with strange arg. */
+ if (floatop == 0xdf && codep[-1] == 0xe0)
+ strcpy (op1out, names16[0]);
+ }
+ else
+ {
+ putop (dp->name, sizeflag);
+
+ obufp = op1out;
+ if (dp->op1)
+ (*dp->op1) (dp->bytemode1, sizeflag);
+ obufp = op2out;
+ if (dp->op2)
+ (*dp->op2) (dp->bytemode2, sizeflag);
+ }
+}
+
+static void
+OP_ST (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ oappend ("%st");
+}
+
+static void
+OP_STi (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ sprintf (scratchbuf, "%%st(%d)", rm);
+ oappend (scratchbuf + intel_syntax);
+}
+
+/* Capital letters in template are macros. */
+static int
+putop (template, sizeflag)
+ const char *template;
+ int sizeflag;
+{
+ const char *p;
+ int alt;
+
+ for (p = template; *p; p++)
+ {
+ switch (*p)
+ {
+ default:
+ *obufp++ = *p;
+ break;
+ case '{':
+ alt = 0;
+ if (intel_syntax)
+ alt += 1;
+ if (mode_64bit)
+ alt += 2;
+ while (alt != 0)
+ {
+ while (*++p != '|')
+ {
+ if (*p == '}')
+ {
+ /* Alternative not valid. */
+ strcpy (obuf, "(bad)");
+ obufp = obuf + 5;
+ return 1;
+ }
+ else if (*p == '\0')
+ abort ();
+ }
+ alt--;
+ }
+ break;
+ case '|':
+ while (*++p != '}')
+ {
+ if (*p == '\0')
+ abort ();
+ }
+ break;
+ case '}':
+ break;
+ case 'A':
+ if (intel_syntax)
+ break;
+ if (mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+ *obufp++ = 'b';
+ break;
+ case 'B':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ *obufp++ = 'b';
+ break;
+ case 'E': /* For jcxz/jecxz */
+ if (mode_64bit)
+ {
+ if (sizeflag & AFLAG)
+ *obufp++ = 'r';
+ else
+ *obufp++ = 'e';
+ }
+ else
+ if (sizeflag & AFLAG)
+ *obufp++ = 'e';
+ used_prefixes |= (prefixes & PREFIX_ADDR);
+ break;
+ case 'F':
+ if (intel_syntax)
+ break;
+ if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS))
+ {
+ if (sizeflag & AFLAG)
+ *obufp++ = mode_64bit ? 'q' : 'l';
+ else
+ *obufp++ = mode_64bit ? 'l' : 'w';
+ used_prefixes |= (prefixes & PREFIX_ADDR);
+ }
+ break;
+ case 'H':
+ if (intel_syntax)
+ break;
+ if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS
+ || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS)
+ {
+ used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS);
+ *obufp++ = ',';
+ *obufp++ = 'p';
+ if (prefixes & PREFIX_DS)
+ *obufp++ = 't';
+ else
+ *obufp++ = 'n';
+ }
+ break;
+ case 'L':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ *obufp++ = 'l';
+ break;
+ case 'N':
+ if ((prefixes & PREFIX_FWAIT) == 0)
+ *obufp++ = 'n';
+ else
+ used_prefixes |= PREFIX_FWAIT;
+ break;
+ case 'O':
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ *obufp++ = 'o';
+ else
+ *obufp++ = 'd';
+ break;
+ case 'T':
+ if (intel_syntax)
+ break;
+ if (mode_64bit)
+ {
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'P':
+ if (intel_syntax)
+ break;
+ if ((prefixes & PREFIX_DATA)
+ || (rex & REX_MODE64)
+ || (sizeflag & SUFFIX_ALWAYS))
+ {
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ }
+ break;
+ case 'U':
+ if (intel_syntax)
+ break;
+ if (mode_64bit)
+ {
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'Q':
+ if (intel_syntax)
+ break;
+ USED_REX (REX_MODE64);
+ if (mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+ {
+ if (rex & REX_MODE64)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ }
+ break;
+ case 'R':
+ USED_REX (REX_MODE64);
+ if (intel_syntax)
+ {
+ if (rex & REX_MODE64)
+ {
+ *obufp++ = 'q';
+ *obufp++ = 't';
+ }
+ else if (sizeflag & DFLAG)
+ {
+ *obufp++ = 'd';
+ *obufp++ = 'q';
+ }
+ else
+ {
+ *obufp++ = 'w';
+ *obufp++ = 'd';
+ }
+ }
+ else
+ {
+ if (rex & REX_MODE64)
+ *obufp++ = 'q';
+ else if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ }
+ if (!(rex & REX_MODE64))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'S':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ {
+ if (rex & REX_MODE64)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ }
+ break;
+ case 'X':
+ if (prefixes & PREFIX_DATA)
+ *obufp++ = 'd';
+ else
+ *obufp++ = 's';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'Y':
+ if (intel_syntax)
+ break;
+ if (rex & REX_MODE64)
+ {
+ USED_REX (REX_MODE64);
+ *obufp++ = 'q';
+ }
+ break;
+ /* implicit operand size 'l' for i386 or 'q' for x86-64 */
+ case 'W':
+ /* operand size flag for cwtl, cbtw */
+ USED_REX (0);
+ if (rex)
+ *obufp++ = 'l';
+ else if (sizeflag & DFLAG)
+ *obufp++ = 'w';
+ else
+ *obufp++ = 'b';
+ if (intel_syntax)
+ {
+ if (rex)
+ {
+ *obufp++ = 'q';
+ *obufp++ = 'e';
+ }
+ if (sizeflag & DFLAG)
+ {
+ *obufp++ = 'd';
+ *obufp++ = 'e';
+ }
+ else
+ {
+ *obufp++ = 'w';
+ }
+ }
+ if (!rex)
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ }
+ }
+ *obufp = 0;
+ return 0;
+}
+
+static void
+oappend (s)
+ const char *s;
+{
+ strcpy (obufp, s);
+ obufp += strlen (s);
+}
+
+static void
+append_seg ()
+{
+ if (prefixes & PREFIX_CS)
+ {
+ used_prefixes |= PREFIX_CS;
+ oappend ("%cs:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_DS)
+ {
+ used_prefixes |= PREFIX_DS;
+ oappend ("%ds:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_SS)
+ {
+ used_prefixes |= PREFIX_SS;
+ oappend ("%ss:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_ES)
+ {
+ used_prefixes |= PREFIX_ES;
+ oappend ("%es:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_FS)
+ {
+ used_prefixes |= PREFIX_FS;
+ oappend ("%fs:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_GS)
+ {
+ used_prefixes |= PREFIX_GS;
+ oappend ("%gs:" + intel_syntax);
+ }
+}
+
+static void
+OP_indirE (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ if (!intel_syntax)
+ oappend ("*");
+ OP_E (bytemode, sizeflag);
+}
+
+static void
+print_operand_value (buf, hex, disp)
+ char *buf;
+ int hex;
+ bfd_vma disp;
+{
+ if (mode_64bit)
+ {
+ if (hex)
+ {
+ char tmp[30];
+ int i;
+ buf[0] = '0';
+ buf[1] = 'x';
+ sprintf_vma (tmp, disp);
+ for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++);
+ strcpy (buf + 2, tmp + i);
+ }
+ else
+ {
+ bfd_signed_vma v = disp;
+ char tmp[30];
+ int i;
+ if (v < 0)
+ {
+ *(buf++) = '-';
+ v = -disp;
+ /* Check for possible overflow on 0x8000000000000000. */
+ if (v < 0)
+ {
+ strcpy (buf, "9223372036854775808");
+ return;
+ }
+ }
+ if (!v)
+ {
+ strcpy (buf, "0");
+ return;
+ }
+
+ i = 0;
+ tmp[29] = 0;
+ while (v)
+ {
+ tmp[28 - i] = (v % 10) + '0';
+ v /= 10;
+ i++;
+ }
+ strcpy (buf, tmp + 29 - i);
+ }
+ }
+ else
+ {
+ if (hex)
+ sprintf (buf, "0x%x", (unsigned int) disp);
+ else
+ sprintf (buf, "%d", (int) disp);
+ }
+}
+
+static void
+OP_E (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_vma disp;
+ int add = 0;
+ int riprel = 0;
+ USED_REX (REX_EXTZ);
+ if (rex & REX_EXTZ)
+ add += 8;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+
+ if (mod == 3)
+ {
+ switch (bytemode)
+ {
+ case b_mode:
+ USED_REX (0);
+ if (rex)
+ oappend (names8rex[rm + add]);
+ else
+ oappend (names8[rm + add]);
+ break;
+ case w_mode:
+ oappend (names16[rm + add]);
+ break;
+ case d_mode:
+ oappend (names32[rm + add]);
+ break;
+ case q_mode:
+ oappend (names64[rm + add]);
+ break;
+ case m_mode:
+ if (mode_64bit)
+ oappend (names64[rm + add]);
+ else
+ oappend (names32[rm + add]);
+ break;
+ case v_mode:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ oappend (names64[rm + add]);
+ else if (sizeflag & DFLAG)
+ oappend (names32[rm + add]);
+ else
+ oappend (names16[rm + add]);
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 0:
+ if (!(codep[-2] == 0xAE && codep[-1] == 0xF8 /* sfence */)
+ && !(codep[-2] == 0xAE && codep[-1] == 0xF0 /* mfence */)
+ && !(codep[-2] == 0xAE && codep[-1] == 0xe8 /* lfence */))
+ BadOp (); /* bad sfence,lea,lds,les,lfs,lgs,lss modrm */
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+ return;
+ }
+
+ disp = 0;
+ append_seg ();
+
+ if ((sizeflag & AFLAG) || mode_64bit) /* 32 bit address mode */
+ {
+ int havesib;
+ int havebase;
+ int base;
+ int index = 0;
+ int scale = 0;
+
+ havesib = 0;
+ havebase = 1;
+ base = rm;
+
+ if (base == 4)
+ {
+ havesib = 1;
+ FETCH_DATA (the_info, codep + 1);
+ scale = (*codep >> 6) & 3;
+ index = (*codep >> 3) & 7;
+ base = *codep & 7;
+ USED_REX (REX_EXTY);
+ USED_REX (REX_EXTZ);
+ if (rex & REX_EXTY)
+ index += 8;
+ if (rex & REX_EXTZ)
+ base += 8;
+ codep++;
+ }
+
+ switch (mod)
+ {
+ case 0:
+ if ((base & 7) == 5)
+ {
+ havebase = 0;
+ if (mode_64bit && !havesib && (sizeflag & AFLAG))
+ riprel = 1;
+ disp = get32s ();
+ }
+ break;
+ case 1:
+ FETCH_DATA (the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case 2:
+ disp = get32s ();
+ break;
+ }
+
+ if (!intel_syntax)
+ if (mod != 0 || (base & 7) == 5)
+ {
+ print_operand_value (scratchbuf, !riprel, disp);
+ oappend (scratchbuf);
+ if (riprel)
+ {
+ set_op (disp, 1);
+ oappend ("(%rip)");
+ }
+ }
+
+ if (havebase || (havesib && (index != 4 || scale != 0)))
+ {
+ if (intel_syntax)
+ {
+ switch (bytemode)
+ {
+ case b_mode:
+ oappend ("BYTE PTR ");
+ break;
+ case w_mode:
+ oappend ("WORD PTR ");
+ break;
+ case v_mode:
+ oappend ("DWORD PTR ");
+ break;
+ case d_mode:
+ oappend ("QWORD PTR ");
+ break;
+ case m_mode:
+ if (mode_64bit)
+ oappend ("DWORD PTR ");
+ else
+ oappend ("QWORD PTR ");
+ break;
+ case x_mode:
+ oappend ("XWORD PTR ");
+ break;
+ default:
+ break;
+ }
+ }
+ *obufp++ = open_char;
+ if (intel_syntax && riprel)
+ oappend ("rip + ");
+ *obufp = '\0';
+ USED_REX (REX_EXTZ);
+ if (!havesib && (rex & REX_EXTZ))
+ base += 8;
+ if (havebase)
+ oappend (mode_64bit && (sizeflag & AFLAG)
+ ? names64[base] : names32[base]);
+ if (havesib)
+ {
+ if (index != 4)
+ {
+ if (intel_syntax)
+ {
+ if (havebase)
+ {
+ *obufp++ = separator_char;
+ *obufp = '\0';
+ }
+ sprintf (scratchbuf, "%s",
+ mode_64bit && (sizeflag & AFLAG)
+ ? names64[index] : names32[index]);
+ }
+ else
+ sprintf (scratchbuf, ",%s",
+ mode_64bit && (sizeflag & AFLAG)
+ ? names64[index] : names32[index]);
+ oappend (scratchbuf);
+ }
+ if (!intel_syntax
+ || (intel_syntax
+ && bytemode != b_mode
+ && bytemode != w_mode
+ && bytemode != v_mode))
+ {
+ *obufp++ = scale_char;
+ *obufp = '\0';
+ sprintf (scratchbuf, "%d", 1 << scale);
+ oappend (scratchbuf);
+ }
+ }
+ if (intel_syntax)
+ if (mod != 0 || (base & 7) == 5)
+ {
+ /* Don't print zero displacements. */
+ if (disp != 0)
+ {
+ if ((bfd_signed_vma) disp > 0)
+ {
+ *obufp++ = '+';
+ *obufp = '\0';
+ }
+
+ print_operand_value (scratchbuf, 0, disp);
+ oappend (scratchbuf);
+ }
+ }
+
+ *obufp++ = close_char;
+ *obufp = '\0';
+ }
+ else if (intel_syntax)
+ {
+ if (mod != 0 || (base & 7) == 5)
+ {
+ if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS))
+ ;
+ else
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ print_operand_value (scratchbuf, 1, disp);
+ oappend (scratchbuf);
+ }
+ }
+ }
+ else
+ { /* 16 bit address mode */
+ switch (mod)
+ {
+ case 0:
+ if ((rm & 7) == 6)
+ {
+ disp = get16 ();
+ if ((disp & 0x8000) != 0)
+ disp -= 0x10000;
+ }
+ break;
+ case 1:
+ FETCH_DATA (the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case 2:
+ disp = get16 ();
+ if ((disp & 0x8000) != 0)
+ disp -= 0x10000;
+ break;
+ }
+
+ if (!intel_syntax)
+ if (mod != 0 || (rm & 7) == 6)
+ {
+ print_operand_value (scratchbuf, 0, disp);
+ oappend (scratchbuf);
+ }
+
+ if (mod != 0 || (rm & 7) != 6)
+ {
+ *obufp++ = open_char;
+ *obufp = '\0';
+ oappend (index16[rm + add]);
+ *obufp++ = close_char;
+ *obufp = '\0';
+ }
+ }
+}
+
+static void
+OP_G (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ int add = 0;
+ USED_REX (REX_EXTX);
+ if (rex & REX_EXTX)
+ add += 8;
+ switch (bytemode)
+ {
+ case b_mode:
+ USED_REX (0);
+ if (rex)
+ oappend (names8rex[reg + add]);
+ else
+ oappend (names8[reg + add]);
+ break;
+ case w_mode:
+ oappend (names16[reg + add]);
+ break;
+ case d_mode:
+ oappend (names32[reg + add]);
+ break;
+ case q_mode:
+ oappend (names64[reg + add]);
+ break;
+ case v_mode:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ oappend (names64[reg + add]);
+ else if (sizeflag & DFLAG)
+ oappend (names32[reg + add]);
+ else
+ oappend (names16[reg + add]);
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+}
+
+static bfd_vma
+get64 ()
+{
+ bfd_vma x;
+#ifdef BFD64
+ unsigned int a;
+ unsigned int b;
+
+ FETCH_DATA (the_info, codep + 8);
+ a = *codep++ & 0xff;
+ a |= (*codep++ & 0xff) << 8;
+ a |= (*codep++ & 0xff) << 16;
+ a |= (*codep++ & 0xff) << 24;
+ b = *codep++ & 0xff;
+ b |= (*codep++ & 0xff) << 8;
+ b |= (*codep++ & 0xff) << 16;
+ b |= (*codep++ & 0xff) << 24;
+ x = a + ((bfd_vma) b << 32);
+#else
+ abort ();
+ x = 0;
+#endif
+ return x;
+}
+
+static bfd_signed_vma
+get32 ()
+{
+ bfd_signed_vma x = 0;
+
+ FETCH_DATA (the_info, codep + 4);
+ x = *codep++ & (bfd_signed_vma) 0xff;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+ return x;
+}
+
+static bfd_signed_vma
+get32s ()
+{
+ bfd_signed_vma x = 0;
+
+ FETCH_DATA (the_info, codep + 4);
+ x = *codep++ & (bfd_signed_vma) 0xff;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+
+ x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31);
+
+ return x;
+}
+
+static int
+get16 ()
+{
+ int x = 0;
+
+ FETCH_DATA (the_info, codep + 2);
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ return x;
+}
+
+static void
+set_op (op, riprel)
+ bfd_vma op;
+ int riprel;
+{
+ op_index[op_ad] = op_ad;
+ if (mode_64bit)
+ {
+ op_address[op_ad] = op;
+ op_riprel[op_ad] = riprel;
+ }
+ else
+ {
+ /* Mask to get a 32-bit address. */
+ op_address[op_ad] = op & 0xffffffff;
+ op_riprel[op_ad] = riprel & 0xffffffff;
+ }
+}
+
+static void
+OP_REG (code, sizeflag)
+ int code;
+ int sizeflag;
+{
+ const char *s;
+ int add = 0;
+ USED_REX (REX_EXTZ);
+ if (rex & REX_EXTZ)
+ add = 8;
+
+ switch (code)
+ {
+ case indir_dx_reg:
+ if (intel_syntax)
+ s = "[dx]";
+ else
+ s = "(%dx)";
+ break;
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg + add];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg + add];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ USED_REX (0);
+ if (rex)
+ s = names8rex[code - al_reg + add];
+ else
+ s = names8[code - al_reg];
+ break;
+ case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg:
+ case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg:
+ if (mode_64bit)
+ {
+ s = names64[code - rAX_reg + add];
+ break;
+ }
+ code += eAX_reg - rAX_reg;
+ /* Fall through. */
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ s = names64[code - eAX_reg + add];
+ else if (sizeflag & DFLAG)
+ s = names32[code - eAX_reg + add];
+ else
+ s = names16[code - eAX_reg + add];
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ s = INTERNAL_DISASSEMBLER_ERROR;
+ break;
+ }
+ oappend (s);
+}
+
+static void
+OP_IMREG (code, sizeflag)
+ int code;
+ int sizeflag;
+{
+ const char *s;
+
+ switch (code)
+ {
+ case indir_dx_reg:
+ if (intel_syntax)
+ s = "[dx]";
+ else
+ s = "(%dx)";
+ break;
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ USED_REX (0);
+ if (rex)
+ s = names8rex[code - al_reg];
+ else
+ s = names8[code - al_reg];
+ break;
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ s = names64[code - eAX_reg];
+ else if (sizeflag & DFLAG)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ s = INTERNAL_DISASSEMBLER_ERROR;
+ break;
+ }
+ oappend (s);
+}
+
+static void
+OP_I (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_signed_vma op;
+ bfd_signed_vma mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ op = *codep++;
+ mask = 0xff;
+ break;
+ case q_mode:
+ if (mode_64bit)
+ {
+ op = get32s ();
+ break;
+ }
+ /* Fall through. */
+ case v_mode:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ op = get32s ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32 ();
+ mask = 0xffffffff;
+ }
+ else
+ {
+ op = get16 ();
+ mask = 0xfffff;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ mask = 0xfffff;
+ op = get16 ();
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ op &= mask;
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+ scratchbuf[0] = '\0';
+}
+
+static void
+OP_I64 (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_signed_vma op;
+ bfd_signed_vma mask = -1;
+
+ if (!mode_64bit)
+ {
+ OP_I (bytemode, sizeflag);
+ return;
+ }
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ op = *codep++;
+ mask = 0xff;
+ break;
+ case v_mode:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ op = get64 ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32 ();
+ mask = 0xffffffff;
+ }
+ else
+ {
+ op = get16 ();
+ mask = 0xfffff;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ mask = 0xfffff;
+ op = get16 ();
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ op &= mask;
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+ scratchbuf[0] = '\0';
+}
+
+static void
+OP_sI (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_signed_vma op;
+ bfd_signed_vma mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ op = *codep++;
+ if ((op & 0x80) != 0)
+ op -= 0x100;
+ mask = 0xffffffff;
+ break;
+ case v_mode:
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ op = get32s ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32s ();
+ mask = 0xffffffff;
+ }
+ else
+ {
+ mask = 0xffffffff;
+ op = get16 ();
+ if ((op & 0x8000) != 0)
+ op -= 0x10000;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ op = get16 ();
+ mask = 0xffffffff;
+ if ((op & 0x8000) != 0)
+ op -= 0x10000;
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_J (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_vma disp;
+ bfd_vma mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case v_mode:
+ if (sizeflag & DFLAG)
+ disp = get32s ();
+ else
+ {
+ disp = get16 ();
+ /* For some reason, a data16 prefix on a jump instruction
+ means that the pc is masked to 16 bits after the
+ displacement is added! */
+ mask = 0xffff;
+ }
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+ disp = (start_pc + codep - start_codep + disp) & mask;
+ set_op (disp, 0);
+ print_operand_value (scratchbuf, 1, disp);
+ oappend (scratchbuf);
+}
+
+static void
+OP_SEG (dummy, sizeflag)
+ int dummy;
+ int sizeflag;
+{
+ oappend (names_seg[reg]);
+}
+
+static void
+OP_DIR (dummy, sizeflag)
+ int dummy;
+ int sizeflag;
+{
+ int seg, offset;
+
+ if (sizeflag & DFLAG)
+ {
+ offset = get32 ();
+ seg = get16 ();
+ }
+ else
+ {
+ offset = get16 ();
+ seg = get16 ();
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (intel_syntax)
+ sprintf (scratchbuf, "0x%x,0x%x", seg, offset);
+ else
+ sprintf (scratchbuf, "$0x%x,$0x%x", seg, offset);
+ oappend (scratchbuf);
+}
+
+static void
+OP_OFF (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_vma off;
+
+ append_seg ();
+
+ if ((sizeflag & AFLAG) || mode_64bit)
+ off = get32 ();
+ else
+ off = get16 ();
+
+ if (intel_syntax)
+ {
+ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ }
+ print_operand_value (scratchbuf, 1, off);
+ oappend (scratchbuf);
+}
+
+static void
+OP_OFF64 (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ bfd_vma off;
+
+ if (!mode_64bit)
+ {
+ OP_OFF (bytemode, sizeflag);
+ return;
+ }
+
+ append_seg ();
+
+ off = get64 ();
+
+ if (intel_syntax)
+ {
+ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ }
+ print_operand_value (scratchbuf, 1, off);
+ oappend (scratchbuf);
+}
+
+static void
+ptr_reg (code, sizeflag)
+ int code;
+ int sizeflag;
+{
+ const char *s;
+ if (intel_syntax)
+ oappend ("[");
+ else
+ oappend ("(");
+
+ USED_REX (REX_MODE64);
+ if (rex & REX_MODE64)
+ {
+ if (!(sizeflag & AFLAG))
+ s = names32[code - eAX_reg];
+ else
+ s = names64[code - eAX_reg];
+ }
+ else if (sizeflag & AFLAG)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ oappend (s);
+ if (intel_syntax)
+ oappend ("]");
+ else
+ oappend (")");
+}
+
+static void
+OP_ESreg (code, sizeflag)
+ int code;
+ int sizeflag;
+{
+ oappend ("%es:" + intel_syntax);
+ ptr_reg (code, sizeflag);
+}
+
+static void
+OP_DSreg (code, sizeflag)
+ int code;
+ int sizeflag;
+{
+ if ((prefixes
+ & (PREFIX_CS
+ | PREFIX_DS
+ | PREFIX_SS
+ | PREFIX_ES
+ | PREFIX_FS
+ | PREFIX_GS)) == 0)
+ prefixes |= PREFIX_DS;
+ append_seg ();
+ ptr_reg (code, sizeflag);
+}
+
+static void
+OP_C (dummy, sizeflag)
+ int dummy;
+ int sizeflag;
+{
+ int add = 0;
+ USED_REX (REX_EXTX);
+ if (rex & REX_EXTX)
+ add = 8;
+ sprintf (scratchbuf, "%%cr%d", reg + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_D (dummy, sizeflag)
+ int dummy;
+ int sizeflag;
+{
+ int add = 0;
+ USED_REX (REX_EXTX);
+ if (rex & REX_EXTX)
+ add = 8;
+ if (intel_syntax)
+ sprintf (scratchbuf, "db%d", reg + add);
+ else
+ sprintf (scratchbuf, "%%db%d", reg + add);
+ oappend (scratchbuf);
+}
+
+static void
+OP_T (dummy, sizeflag)
+ int dummy;
+ int sizeflag;
+{
+ sprintf (scratchbuf, "%%tr%d", reg);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_Rd (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ if (mod == 3)
+ OP_E (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static void
+OP_MMX (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ int add = 0;
+ USED_REX (REX_EXTX);
+ if (rex & REX_EXTX)
+ add = 8;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ sprintf (scratchbuf, "%%xmm%d", reg + add);
+ else
+ sprintf (scratchbuf, "%%mm%d", reg + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_XMM (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ int add = 0;
+ USED_REX (REX_EXTX);
+ if (rex & REX_EXTX)
+ add = 8;
+ sprintf (scratchbuf, "%%xmm%d", reg + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EM (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ int add = 0;
+ if (mod != 3)
+ {
+ OP_E (bytemode, sizeflag);
+ return;
+ }
+ USED_REX (REX_EXTZ);
+ if (rex & REX_EXTZ)
+ add = 8;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ sprintf (scratchbuf, "%%xmm%d", rm + add);
+ else
+ sprintf (scratchbuf, "%%mm%d", rm + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EX (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ int add = 0;
+ if (mod != 3)
+ {
+ OP_E (bytemode, sizeflag);
+ return;
+ }
+ USED_REX (REX_EXTZ);
+ if (rex & REX_EXTZ)
+ add = 8;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+ sprintf (scratchbuf, "%%xmm%d", rm + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_MS (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ if (mod == 3)
+ OP_EM (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static void
+OP_XS (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ if (mod == 3)
+ OP_EX (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static const char *Suffix3DNow[] = {
+/* 00 */ NULL, NULL, NULL, NULL,
+/* 04 */ NULL, NULL, NULL, NULL,
+/* 08 */ NULL, NULL, NULL, NULL,
+/* 0C */ "pi2fw", "pi2fd", NULL, NULL,
+/* 10 */ NULL, NULL, NULL, NULL,
+/* 14 */ NULL, NULL, NULL, NULL,
+/* 18 */ NULL, NULL, NULL, NULL,
+/* 1C */ "pf2iw", "pf2id", NULL, NULL,
+/* 20 */ NULL, NULL, NULL, NULL,
+/* 24 */ NULL, NULL, NULL, NULL,
+/* 28 */ NULL, NULL, NULL, NULL,
+/* 2C */ NULL, NULL, NULL, NULL,
+/* 30 */ NULL, NULL, NULL, NULL,
+/* 34 */ NULL, NULL, NULL, NULL,
+/* 38 */ NULL, NULL, NULL, NULL,
+/* 3C */ NULL, NULL, NULL, NULL,
+/* 40 */ NULL, NULL, NULL, NULL,
+/* 44 */ NULL, NULL, NULL, NULL,
+/* 48 */ NULL, NULL, NULL, NULL,
+/* 4C */ NULL, NULL, NULL, NULL,
+/* 50 */ NULL, NULL, NULL, NULL,
+/* 54 */ NULL, NULL, NULL, NULL,
+/* 58 */ NULL, NULL, NULL, NULL,
+/* 5C */ NULL, NULL, NULL, NULL,
+/* 60 */ NULL, NULL, NULL, NULL,
+/* 64 */ NULL, NULL, NULL, NULL,
+/* 68 */ NULL, NULL, NULL, NULL,
+/* 6C */ NULL, NULL, NULL, NULL,
+/* 70 */ NULL, NULL, NULL, NULL,
+/* 74 */ NULL, NULL, NULL, NULL,
+/* 78 */ NULL, NULL, NULL, NULL,
+/* 7C */ NULL, NULL, NULL, NULL,
+/* 80 */ NULL, NULL, NULL, NULL,
+/* 84 */ NULL, NULL, NULL, NULL,
+/* 88 */ NULL, NULL, "pfnacc", NULL,
+/* 8C */ NULL, NULL, "pfpnacc", NULL,
+/* 90 */ "pfcmpge", NULL, NULL, NULL,
+/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt",
+/* 98 */ NULL, NULL, "pfsub", NULL,
+/* 9C */ NULL, NULL, "pfadd", NULL,
+/* A0 */ "pfcmpgt", NULL, NULL, NULL,
+/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1",
+/* A8 */ NULL, NULL, "pfsubr", NULL,
+/* AC */ NULL, NULL, "pfacc", NULL,
+/* B0 */ "pfcmpeq", NULL, NULL, NULL,
+/* B4 */ "pfmul", NULL, "pfrcpit2", "pfmulhrw",
+/* B8 */ NULL, NULL, NULL, "pswapd",
+/* BC */ NULL, NULL, NULL, "pavgusb",
+/* C0 */ NULL, NULL, NULL, NULL,
+/* C4 */ NULL, NULL, NULL, NULL,
+/* C8 */ NULL, NULL, NULL, NULL,
+/* CC */ NULL, NULL, NULL, NULL,
+/* D0 */ NULL, NULL, NULL, NULL,
+/* D4 */ NULL, NULL, NULL, NULL,
+/* D8 */ NULL, NULL, NULL, NULL,
+/* DC */ NULL, NULL, NULL, NULL,
+/* E0 */ NULL, NULL, NULL, NULL,
+/* E4 */ NULL, NULL, NULL, NULL,
+/* E8 */ NULL, NULL, NULL, NULL,
+/* EC */ NULL, NULL, NULL, NULL,
+/* F0 */ NULL, NULL, NULL, NULL,
+/* F4 */ NULL, NULL, NULL, NULL,
+/* F8 */ NULL, NULL, NULL, NULL,
+/* FC */ NULL, NULL, NULL, NULL,
+};
+
+static void
+OP_3DNowSuffix (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ const char *mnemonic;
+
+ FETCH_DATA (the_info, codep + 1);
+ /* AMD 3DNow! instructions are specified by an opcode suffix in the
+ place where an 8-bit immediate would normally go. ie. the last
+ byte of the instruction. */
+ obufp = obuf + strlen (obuf);
+ mnemonic = Suffix3DNow[*codep++ & 0xff];
+ if (mnemonic)
+ oappend (mnemonic);
+ else
+ {
+ /* Since a variable sized modrm/sib chunk is between the start
+ of the opcode (0x0f0f) and the opcode suffix, we need to do
+ all the modrm processing first, and don't know until now that
+ we have a bad opcode. This necessitates some cleaning up. */
+ op1out[0] = '\0';
+ op2out[0] = '\0';
+ BadOp ();
+ }
+}
+
+static const char *simd_cmp_op[] = {
+ "eq",
+ "lt",
+ "le",
+ "unord",
+ "neq",
+ "nlt",
+ "nle",
+ "ord"
+};
+
+static void
+OP_SIMD_Suffix (bytemode, sizeflag)
+ int bytemode;
+ int sizeflag;
+{
+ unsigned int cmp_type;
+
+ FETCH_DATA (the_info, codep + 1);
+ obufp = obuf + strlen (obuf);
+ cmp_type = *codep++ & 0xff;
+ if (cmp_type < 8)
+ {
+ char suffix1 = 'p', suffix2 = 's';
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ if (prefixes & PREFIX_REPZ)
+ suffix1 = 's';
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ suffix2 = 'd';
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_REPNZ);
+ if (prefixes & PREFIX_REPNZ)
+ suffix1 = 's', suffix2 = 'd';
+ }
+ }
+ sprintf (scratchbuf, "cmp%s%c%c",
+ simd_cmp_op[cmp_type], suffix1, suffix2);
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ oappend (scratchbuf);
+ }
+ else
+ {
+ /* We have a bad extension byte. Clean up. */
+ op1out[0] = '\0';
+ op2out[0] = '\0';
+ BadOp ();
+ }
+}
+
+static void
+SIMD_Fixup (extrachar, sizeflag)
+ int extrachar;
+ int sizeflag;
+{
+ /* Change movlps/movhps to movhlps/movlhps for 2 register operand
+ forms of these instructions. */
+ if (mod == 3)
+ {
+ char *p = obuf + strlen (obuf);
+ *(p + 1) = '\0';
+ *p = *(p - 1);
+ *(p - 1) = *(p - 2);
+ *(p - 2) = *(p - 3);
+ *(p - 3) = extrachar;
+ }
+}
+
+static void
+BadOp (void)
+{
+ /* Throw away prefixes and 1st. opcode byte. */
+ codep = insn_codep + 1;
+ oappend ("(bad)");
+}
diff --git a/i386-vl.ld b/i386-vl.ld
new file mode 100644
index 0000000..428fe83
--- /dev/null
+++ b/i386-vl.ld
@@ -0,0 +1,140 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0xa8000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ __preinit_array_start = .;
+ .preinit_array : { *(.preinit_array) }
+ __preinit_array_end = .;
+ __init_array_start = .;
+ .init_array : { *(.init_array) }
+ __init_array_end = .;
+ __fini_array_start = .;
+ .fini_array : { *(.fini_array) }
+ __fini_array_end = .;
+
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/i386.ld b/i386.ld
new file mode 100644
index 0000000..d41c626
--- /dev/null
+++ b/i386.ld
@@ -0,0 +1,140 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/ia64.ld b/ia64.ld
new file mode 100644
index 0000000..8d2ede2
--- /dev/null
+++ b/ia64.ld
@@ -0,0 +1,211 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-ia64-little", "elf64-ia64-little",
+ "elf64-ia64-little")
+OUTPUT_ARCH(ia64)
+ENTRY(_start)
+SEARCH_DIR("/usr/ia64-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.IA_64.pltoff : { *(.rela.IA_64.pltoff) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x00300000010070000002000001000400
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x00300000010070000002000001000400
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x00300000010070000002000001000400
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .opd : { *(.opd) }
+ .IA_64.unwind_info : { *(.IA_64.unwind_info* .gnu.linkonce.ia64unwi.*) }
+ .IA_64.unwind : { *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x10000) + (. & (0x10000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ /* Ensure __gp is outside the range of any normal data. We need to
+ do this to avoid the linker optimizing the code in op.o and getting
+ it out of sync with the relocs that we read when processing that
+ file. A better solution might be to ensure that the dynamically
+ generated code and static qemu code share a single gp-value. */
+ __gp = . + 0x200000;
+ .got : { *(.got.plt) *(.got) }
+ .IA_64.pltoff : { *(.IA_64.pltoff) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ .bss :
+ {
+ . += 0x400000; /* ensure .bss stuff is out of reach of gp */
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(64 / 8);
+ }
+ . = ALIGN(64 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/keymaps.c b/keymaps.c
new file mode 100644
index 0000000..bd89328
--- /dev/null
+++ b/keymaps.c
@@ -0,0 +1,145 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static int get_keysym(const char *name)
+{
+ name2keysym_t *p;
+ for(p = name2keysym; p->name != NULL; p++) {
+ if (!strcmp(p->name, name))
+ return p->keysym;
+ }
+ return 0;
+}
+
+#define MAX_NORMAL_KEYCODE 512
+#define MAX_EXTRA_COUNT 256
+typedef struct {
+ uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+ struct {
+ int keysym;
+ uint16_t keycode;
+ } keysym2keycode_extra[MAX_EXTRA_COUNT];
+ int extra_count;
+} kbd_layout_t;
+
+static kbd_layout_t *parse_keyboard_layout(const char *language,
+ kbd_layout_t * k)
+{
+ FILE *f;
+ char file_name[1024];
+ char line[1024];
+ int len;
+
+ snprintf(file_name, sizeof(file_name),
+ "%s/keymaps/%s", bios_dir, language);
+
+ if (!k)
+ k = qemu_mallocz(sizeof(kbd_layout_t));
+ if (!k)
+ return 0;
+ if (!(f = fopen(file_name, "r"))) {
+ fprintf(stderr,
+ "Could not read keymap file: '%s'\n", file_name);
+ return 0;
+ }
+ for(;;) {
+ if (fgets(line, 1024, f) == NULL)
+ break;
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+ if (!strncmp(line, "map ", 4))
+ continue;
+ if (!strncmp(line, "include ", 8)) {
+ parse_keyboard_layout(line + 8, k);
+ } else {
+ char *end_of_keysym = line;
+ while (*end_of_keysym != 0 && *end_of_keysym != ' ')
+ end_of_keysym++;
+ if (*end_of_keysym) {
+ int keysym;
+ *end_of_keysym = 0;
+ keysym = get_keysym(line);
+ if (keysym == 0) {
+ // fprintf(stderr, "Warning: unknown keysym %s\n", line);
+ } else {
+ const char *rest = end_of_keysym + 1;
+ int keycode = strtol(rest, NULL, 0);
+ /* if(keycode&0x80)
+ keycode=(keycode<<8)^0x80e0; */
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
+ k->keysym2keycode[keysym] = keycode;
+ } else {
+ if (k->extra_count >= MAX_EXTRA_COUNT) {
+ fprintf(stderr,
+ "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n",
+ line, keysym);
+ } else {
+#if 0
+ fprintf(stderr, "Setting %d: %d,%d\n",
+ k->extra_count, keysym, keycode);
+#endif
+ k->keysym2keycode_extra[k->extra_count].
+ keysym = keysym;
+ k->keysym2keycode_extra[k->extra_count].
+ keycode = keycode;
+ k->extra_count++;
+ }
+ }
+ }
+ }
+ }
+ }
+ fclose(f);
+ return k;
+}
+
+static void *init_keyboard_layout(const char *language)
+{
+ return parse_keyboard_layout(language, 0);
+}
+
+static int keysym2scancode(void *kbd_layout, int keysym)
+{
+ kbd_layout_t *k = kbd_layout;
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ if (k->keysym2keycode[keysym] == 0)
+ fprintf(stderr, "Warning: no scancode found for keysym %d\n",
+ keysym);
+ return k->keysym2keycode[keysym];
+ } else {
+ int i;
+#ifdef XK_ISO_Left_Tab
+ if (keysym == XK_ISO_Left_Tab)
+ keysym = XK_Tab;
+#endif
+ for (i = 0; i < k->extra_count; i++)
+ if (k->keysym2keycode_extra[i].keysym == keysym)
+ return k->keysym2keycode_extra[i].keycode;
+ }
+ return 0;
+}
diff --git a/keymaps/ar b/keymaps/ar
new file mode 100644
index 0000000..c430c03
--- /dev/null
+++ b/keymaps/ar
@@ -0,0 +1,98 @@
+# generated from XKB map ar
+include common
+map 0x401
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Arabic_dad 0x10 altgr
+Arabic_fatha 0x10 shift altgr
+Arabic_sad 0x11 altgr
+Arabic_fathatan 0x11 shift altgr
+Arabic_theh 0x12 altgr
+Arabic_damma 0x12 shift altgr
+Arabic_qaf 0x13 altgr
+Arabic_dammatan 0x13 shift altgr
+Arabic_feh 0x14 altgr
+UFEF9 0x14 shift altgr
+Arabic_ghain 0x15 altgr
+Arabic_hamzaunderalef 0x15 shift altgr
+Arabic_ain 0x16 altgr
+grave 0x16 shift altgr
+Arabic_ha 0x17 altgr
+division 0x17 shift altgr
+Arabic_khah 0x18 altgr
+multiply 0x18 shift altgr
+Arabic_hah 0x19 altgr
+Arabic_semicolon 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Arabic_jeem 0x1a altgr
+bracketright 0x1b
+braceright 0x1b shift
+Arabic_dal 0x1b altgr
+Arabic_sheen 0x1e altgr
+backslash 0x1e shift altgr
+Arabic_seen 0x1f altgr
+Arabic_yeh 0x20 altgr
+bracketleft 0x20 shift altgr
+Arabic_beh 0x21 altgr
+bracketright 0x21 shift altgr
+Arabic_lam 0x22 altgr
+UFEF7 0x22 shift altgr
+Arabic_alef 0x23 altgr
+Arabic_hamzaonalef 0x23 shift altgr
+Arabic_teh 0x24 altgr
+Arabic_tatweel 0x24 shift altgr
+Arabic_noon 0x25 altgr
+Arabic_comma 0x25 shift altgr
+Arabic_meem 0x26 altgr
+slash 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Arabic_kaf 0x27 altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Arabic_tah 0x28 altgr
+grave 0x29
+asciitilde 0x29 shift
+Arabic_thal 0x29 altgr
+Arabic_shadda 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+less 0x2b altgr
+greater 0x2b shift altgr
+Arabic_hamzaonyeh 0x2c altgr
+asciitilde 0x2c shift altgr
+Arabic_hamza 0x2d altgr
+Arabic_sukun 0x2d shift altgr
+Arabic_hamzaonwaw 0x2e altgr
+Arabic_kasra 0x2e shift altgr
+Arabic_ra 0x2f altgr
+Arabic_kasratan 0x2f shift altgr
+UFEFB 0x30 altgr
+UFEF5 0x30 shift altgr
+Arabic_alefmaksura 0x31 altgr
+Arabic_maddaonalef 0x31 shift altgr
+Arabic_tehmarbuta 0x32 altgr
+apostrophe 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Arabic_waw 0x33 altgr
+period 0x34
+greater 0x34 shift
+Arabic_zain 0x34 altgr
+slash 0x35
+question 0x35 shift
+Arabic_zah 0x35 altgr
+Arabic_question_mark 0x35 shift altgr
diff --git a/keymaps/common b/keymaps/common
new file mode 100644
index 0000000..0b53f1c
--- /dev/null
+++ b/keymaps/common
@@ -0,0 +1,157 @@
+include modifiers
+
+#
+# Top row
+#
+1 0x2
+2 0x3
+3 0x4
+4 0x5
+5 0x6
+6 0x7
+7 0x8
+8 0x9
+9 0xa
+0 0xb
+BackSpace 0xe
+
+#
+# QWERTY first row
+#
+Tab 0xf localstate
+ISO_Left_Tab 0xf shift
+q 0x10 addupper
+w 0x11 addupper
+e 0x12 addupper
+r 0x13 addupper
+t 0x14 addupper
+y 0x15 addupper
+u 0x16 addupper
+i 0x17 addupper
+o 0x18 addupper
+p 0x19 addupper
+
+#
+# QWERTY second row
+#
+a 0x1e addupper
+s 0x1f addupper
+d 0x20 addupper
+f 0x21 addupper
+g 0x22 addupper
+h 0x23 addupper
+j 0x24 addupper
+k 0x25 addupper
+l 0x26 addupper
+Return 0x1c localstate
+
+#
+# QWERTY third row
+#
+z 0x2c addupper
+x 0x2d addupper
+c 0x2e addupper
+v 0x2f addupper
+b 0x30 addupper
+n 0x31 addupper
+m 0x32 addupper
+
+space 0x39 localstate
+
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+brokenbar 0x56 shift altgr
+
+#
+# Esc and Function keys
+#
+Escape 0x1 localstate
+F1 0x3b localstate
+F2 0x3c localstate
+F3 0x3d localstate
+F4 0x3e localstate
+F5 0x3f localstate
+F6 0x40 localstate
+F7 0x41 localstate
+F8 0x42 localstate
+F9 0x43 localstate
+F10 0x44 localstate
+F11 0x57 localstate
+F12 0x58 localstate
+
+# Printscreen, Scrollock and Pause
+# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37),
+# but (0xe0, 0x37) seems to work.
+Print 0xb7 localstate
+Sys_Req 0xb7 localstate
+Execute 0xb7 localstate
+Scroll_Lock 0x46
+
+#
+# Insert - PgDown
+#
+Insert 0xd2 localstate
+Delete 0xd3 localstate
+Home 0xc7 localstate
+End 0xcf localstate
+Page_Up 0xc9 localstate
+Page_Down 0xd1 localstate
+
+#
+# Arrow keys
+#
+Left 0xcb localstate
+Up 0xc8 localstate
+Down 0xd0 localstate
+Right 0xcd localstate
+
+#
+# Numpad
+#
+Num_Lock 0x45
+KP_Divide 0xb5
+KP_Multiply 0x37
+KP_Subtract 0x4a
+KP_Add 0x4e
+KP_Enter 0x9c
+
+KP_Decimal 0x53 numlock
+KP_Separator 0x53 numlock
+KP_Delete 0x53
+
+KP_0 0x52 numlock
+KP_Insert 0x52
+
+KP_1 0x4f numlock
+KP_End 0x4f
+
+KP_2 0x50 numlock
+KP_Down 0x50
+
+KP_3 0x51 numlock
+KP_Next 0x51
+
+KP_4 0x4b numlock
+KP_Left 0x4b
+
+KP_5 0x4c numlock
+KP_Begin 0x4c
+
+KP_6 0x4d numlock
+KP_Right 0x4d
+
+KP_7 0x47 numlock
+KP_Home 0x47
+
+KP_8 0x48 numlock
+KP_Up 0x48
+
+KP_9 0x49 numlock
+KP_Prior 0x49
+
+Caps_Lock 0x3a
+#
+# Inhibited keys
+#
+Multi_key 0x0 inhibit
diff --git a/keymaps/da b/keymaps/da
new file mode 100644
index 0000000..3884dcf
--- /dev/null
+++ b/keymaps/da
@@ -0,0 +1,120 @@
+# generated from XKB map dk
+include common
+map 0x406
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+bar 0x0d altgr
+brokenbar 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ae 0x27
+AE 0x27 shift
+oslash 0x28
+Ooblique 0x28 shift
+dead_caron 0x28 shift altgr
+onehalf 0x29
+section 0x29 shift
+threequarters 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+dead_doubleacute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+notsign 0x56 shift altgr
diff --git a/keymaps/de b/keymaps/de
new file mode 100644
index 0000000..ed929c7
--- /dev/null
+++ b/keymaps/de
@@ -0,0 +1,114 @@
+# generated from XKB map de
+include common
+map 0x407
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/de-ch b/keymaps/de-ch
new file mode 100644
index 0000000..f83837b
--- /dev/null
+++ b/keymaps/de-ch
@@ -0,0 +1,169 @@
+# rdesktop Swiss-German (de-ch) keymap file
+# 2003-06-03 by noldi@tristar.ch
+#
+include common
+map 0x00000807
+#
+# Scan Code 1
+section 0x29
+degree 0x29 shift
+notsign 0x29 altgr inhibit
+#
+# Scan Code 2
+plus 0x2 shift
+brokenbar 0x02 altgr
+#
+# Scan Code 3
+quotedbl 0x03 shift
+at 0x03 altgr
+#
+# Scan Code 4
+asterisk 0x04 shift
+numbersign 0x04 altgr
+#
+# Scan Code 5
+ccedilla 0x05 shift
+onequarter 0x05 altgr inhibit
+#
+# Scan Code 6
+percent 0x06 shift
+onehalf 0x06 altgr inhibit
+#
+# Scan Code 7
+ampersand 0x07 shift
+notsign 0x07 altgr
+#
+# Scan Code 8
+slash 0x08 shift
+bar 0x08 altgr
+#
+# Scan Code 9
+parenleft 0x09 shift
+cent 0x09 altgr
+#
+# Scan Code 10
+parenright 0x0a shift
+#
+# Scan Code 11
+equal 0x0b shift
+braceright 0x0b altgr inhibit
+#
+# Scan Code 12
+apostrophe 0x0c
+question 0x0c shift
+dead_acute 0x0c altgr
+#
+# Scan Code 13
+dead_circumflex 0x0d
+dead_grave 0x0d shift
+dead_tilde 0x0d altgr
+#
+# Scan Code 19
+EuroSign 0x12 altgr
+#
+# Scan Code 22
+z 0x15 addupper
+#
+# Scan Code 27
+udiaeresis 0x1a
+egrave 0x1a shift
+bracketleft 0x1a altgr
+#
+# Scan Code 28
+dead_diaeresis 0x1b
+exclam 0x1b shift
+bracketright 0x1b altgr
+#
+# Scan Code 40
+odiaeresis 0x27
+eacute 0x27 shift
+#
+# Scan Code 41
+adiaeresis 0x28
+agrave 0x28 shift
+braceleft 0x28 altgr
+#
+# Scan Code 42 (only on international keyboards)
+dollar 0x2b
+sterling 0x2b shift
+braceright 0x2b altgr
+#
+# Scan Code 45 (only on international keyboards)
+backslash 0x56 altgr
+#
+# Scan Code 46
+y 0x2c addupper
+#
+# Scan Code 53
+comma 0x33
+semicolon 0x33 shift
+#
+# Scan Code 54
+period 0x34
+colon 0x34 shift
+#
+# Scan Code 55
+minus 0x35
+underscore 0x35 shift
+#
+# Suppress Windows unsupported AltGr keys
+#
+# Scan Code 17
+paragraph 0x10 altgr inhibit
+#
+# Scan Code 21
+tslash 0x14 altgr inhibit
+#
+# Scan Code 22
+leftarrow 0x15 altgr inhibit
+#
+# Scan Code 23
+downarrow 0x16 altgr inhibit
+#
+# Scan Code 24
+rightarrow 0x17 altgr inhibit
+#
+# Scan Code 25
+oslash 0x18 altgr inhibit
+#
+# Scan Code 26
+thorn 0x19 altgr inhibit
+#
+# Scan Code 31
+ae 0x1e altgr inhibit
+#
+# Scan Code 32
+ssharp 0x1f altgr inhibit
+#
+# Scan Code 33
+eth 0x20 altgr inhibit
+#
+# Scan Code 34
+dstroke 0x21 altgr inhibit
+#
+# Scan Code 35
+eng 0x22 altgr inhibit
+#
+# Scan Code 36
+hstroke 0x23 altgr inhibit
+#
+# Scan Code 38
+kra 0x25 altgr inhibit
+#
+# Scan Code 39
+lstroke 0x26 altgr inhibit
+#
+# Scan Code 46
+guillemotleft 0x2c altgr inhibit
+#
+# Scan Code 47
+guillemotright 0x2d altgr inhibit
+#
+# Scan Code 49
+leftdoublequotemark 0x2f altgr inhibit
+#
+# Scan Code 50
+rightdoublequotemark 0x30 altgr inhibit
+#
+# Scan Code 52
+mu 0x32 altgr inhibit
diff --git a/keymaps/en-gb b/keymaps/en-gb
new file mode 100644
index 0000000..b45f06c
--- /dev/null
+++ b/keymaps/en-gb
@@ -0,0 +1,119 @@
+# generated from XKB map gb
+include common
+map 0x809
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+at 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+notsign 0x29 shift
+bar 0x29 altgr
+numbersign 0x2b
+asciitilde 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56
+bar 0x56 shift
diff --git a/keymaps/en-us b/keymaps/en-us
new file mode 100644
index 0000000..f5784bb
--- /dev/null
+++ b/keymaps/en-us
@@ -0,0 +1,35 @@
+# generated from XKB map us
+include common
+map 0x409
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
diff --git a/keymaps/es b/keymaps/es
new file mode 100644
index 0000000..0c29eec
--- /dev/null
+++ b/keymaps/es
@@ -0,0 +1,105 @@
+# generated from XKB map es
+include common
+map 0x40a
+exclam 0x02 shift
+bar 0x02 altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+periodcentered 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+asciitilde 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+notsign 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+exclamdown 0x0d
+questiondown 0x0d shift
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_grave 0x1a
+dead_circumflex 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ntilde 0x27
+Ntilde 0x27 shift
+dead_doubleacute 0x27 shift altgr
+dead_acute 0x28
+dead_diaeresis 0x28 shift
+braceleft 0x28 altgr
+masculine 0x29
+ordfeminine 0x29 shift
+backslash 0x29 altgr
+ccedilla 0x2b
+Ccedilla 0x2b shift
+braceright 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x56
+greater 0x56 shift
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/et b/keymaps/et
new file mode 100644
index 0000000..b5a73fe
--- /dev/null
+++ b/keymaps/et
@@ -0,0 +1,86 @@
+map 0x00000425
+include common
+
+#
+# Top row
+#
+dead_caron 0x29
+dead_tilde 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+otilde 0x1b
+Otilde 0x1b shift
+section 0x1b altgr
+
+#
+# QWERTY second row
+#
+scaron 0x1f altgr
+Scaron 0x1f altgr shift
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+asciicircum 0x28 altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+onehalf 0x2b altgr
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+zcaron 0x2c altgr
+Zcaron 0x2c altgr shift
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
+
diff --git a/keymaps/fi b/keymaps/fi
new file mode 100644
index 0000000..2a4e0f0
--- /dev/null
+++ b/keymaps/fi
@@ -0,0 +1,124 @@
+# generated from XKB map se_FI
+include common
+map 0x40b
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+plusminus 0x0d altgr
+notsign 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+oslash 0x27 altgr
+Ooblique 0x27 shift altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+ae 0x28 altgr
+AE 0x28 shift altgr
+section 0x29
+onehalf 0x29 shift
+paragraph 0x29 altgr
+threequarters 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+acute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/keymaps/fo b/keymaps/fo
new file mode 100644
index 0000000..83add42
--- /dev/null
+++ b/keymaps/fo
@@ -0,0 +1,77 @@
+map 0x438
+include common
+
+#
+# Top row
+#
+onehalf 0x29
+section 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+plusminus 0xc altgr
+
+bar 0xd altgr
+dead_acute 0xd
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+eth 0x1b addupper
+asciitilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+ae 0x27 addupper
+oslash 0x28
+Ooblique 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
+
diff --git a/keymaps/fr b/keymaps/fr
new file mode 100644
index 0000000..cbb4591
--- /dev/null
+++ b/keymaps/fr
@@ -0,0 +1,181 @@
+include common
+map 0x40c
+#
+# Top row
+#
+twosuperior 0x29
+notsign 0x29 altgr
+
+ampersand 0x02
+1 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+
+eacute 0x03
+2 0x03 shift
+asciitilde 0x03 altgr
+oneeighth 0x03 shift altgr
+
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+
+apostrophe 0x05
+4 0x05 shift
+braceleft 0x05 altgr
+
+parenleft 0x06
+5 0x06 shift
+bracketleft 0x06 altgr
+threeeighths 0x06 shift altgr
+
+minus 0x07
+6 0x07 shift
+bar 0x07 altgr
+fiveeighths 0x07 shift altgr
+
+egrave 0x08
+7 0x08 shift
+grave 0x08 altgr
+seveneighths 0x08 shift altgr
+
+underscore 0x09
+8 0x09 shift
+backslash 0x09 altgr
+trademark 0x09 shift altgr
+
+ccedilla 0x0a
+9 0x0a shift
+asciicircum 0x0a altgr
+plusminus 0x0a shift altgr
+
+agrave 0x0b
+0 0x0b shift
+at 0x0b altgr
+
+parenright 0x0c
+degree 0x0c shift
+bracketright 0x0c altgr
+questiondown 0x0c shift altgr
+
+equal 0x0d
+plus 0x0d shift
+braceright 0x0d altgr
+dead_ogonek 0x0d shift altgr
+
+#
+# AZERTY first row
+#
+
+a 0x10 addupper
+ae 0x10 altgr
+AE 0x10 shift altgr
+
+z 0x11 addupper
+guillemotleft 0x11 altgr
+
+EuroSign 0x12 altgr
+
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+dead_abovering 0x1a shift altgr
+
+dollar 0x1b
+sterling 0x1b shift
+currency 0x1b altgr
+dead_macron 0x1b shift altgr
+
+#
+# AZERTY second row
+#
+q 0x1e addupper
+Greek_OMEGA 0x1e shift altgr
+
+ssharp 0x1f altgr
+
+eth 0x20 altgr
+ETH 0x20 shift altgr
+
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+
+eng 0x22 altgr
+ENG 0x22 shift altgr
+
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+
+kra 0x25 altgr
+
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+
+m 0x27 addupper
+masculine 0x27 shift altgr
+
+ugrave 0x28
+percent 0x28 shift
+dead_caron 0x28 shift altgr
+
+asterisk 0x2b
+mu 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+
+#
+# AZERTY third row
+#
+less 0x56
+greater 0x56 shift
+
+w 0x2c addupper
+
+guillemotright 0x2d altgr
+
+cent 0x2e altgr
+copyright 0x2e shift altgr
+
+leftdoublequotemark 0x2f altgr
+
+rightdoublequotemark 0x30 altgr
+
+comma 0x32
+question 0x32 shift
+dead_acute 0x32 altgr
+dead_doubleacute 0x32 shift altgr
+
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+
+exclam 0x35
+section 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/fr-be b/keymaps/fr-be
new file mode 100644
index 0000000..92d668e
--- /dev/null
+++ b/keymaps/fr-be
@@ -0,0 +1,140 @@
+# generated from XKB map be
+include common
+map 0x80c
+ampersand 0x02
+1 0x02 shift
+bar 0x02 altgr
+exclamdown 0x02 shift altgr
+eacute 0x03
+2 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+apostrophe 0x05
+4 0x05 shift
+onequarter 0x05 altgr
+dollar 0x05 shift altgr
+parenleft 0x06
+5 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+section 0x07
+6 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+egrave 0x08
+7 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+exclam 0x09
+8 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+ccedilla 0x0a
+9 0x0a shift
+braceleft 0x0a altgr
+plusminus 0x0a shift altgr
+agrave 0x0b
+0 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+parenright 0x0c
+degree 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+a 0x10 addupper
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+z 0x11 addupper
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+dollar 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+q 0x1e addupper
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+m 0x27 addupper
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+ugrave 0x28
+percent 0x28 shift
+dead_acute 0x28 altgr
+dead_caron 0x28 shift altgr
+twosuperior 0x29
+threesuperior 0x29 shift
+notsign 0x29 altgr
+mu 0x2b
+sterling 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+w 0x2c addupper
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+comma 0x32
+question 0x32 shift
+dead_cedilla 0x32 altgr
+masculine 0x32 shift altgr
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+equal 0x35
+plus 0x35 shift
+dead_tilde 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56 altgr
diff --git a/keymaps/fr-ca b/keymaps/fr-ca
new file mode 100644
index 0000000..b645208
--- /dev/null
+++ b/keymaps/fr-ca
@@ -0,0 +1,50 @@
+# Canadian French
+# By Simon Germain
+include common
+map 0xc0c
+
+backslash 0x29 altgr
+plusminus 0x2 altgr
+at 0x3 altgr
+sterling 0x4 altgr
+cent 0x5 altgr
+currency 0x6 altgr
+notsign 0x7 altgr
+bar 0x29 shift
+twosuperior 0x9 altgr
+threesuperior 0xa altgr
+onequarter 0xb altgr
+onehalf 0xc altgr
+threequarters 0xd altgr
+section 0x18 altgr
+paragraph 0x19 altgr
+bracketleft 0x1a altgr
+bracketright 0x1b altgr
+asciitilde 0x27 altgr
+braceleft 0x28 altgr
+braceright 0x2b altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x56
+guillemotright 0x56 shift
+degree 0x56 altgr
+mu 0x32 altgr
+eacute 0x35
+dead_acute 0x35 altgr
+dead_grave 0x28
+dead_circumflex 0x1a
+dead_circumflex 0x1a shift
+dead_cedilla 0x1b
+dead_diaeresis 0x1b shift
+exclam 0x2 shift
+quotedbl 0x3 shift
+slash 0x4 shift
+dollar 0x5 shift
+percent 0x6 shift
+question 0x7 shift
+ampersand 0x8 shift
+asterisk 0x9 shift
+parenleft 0xa shift
+parenright 0xb shift
+underscore 0xc shift
+plus 0xd shift
diff --git a/keymaps/fr-ch b/keymaps/fr-ch
new file mode 100644
index 0000000..4620d20
--- /dev/null
+++ b/keymaps/fr-ch
@@ -0,0 +1,114 @@
+# generated from XKB map fr_CH
+include common
+map 0x100c
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/hr b/keymaps/hr
new file mode 100644
index 0000000..613aa69
--- /dev/null
+++ b/keymaps/hr
@@ -0,0 +1,125 @@
+# generated from XKB map hr
+include common
+map 0x41a
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+apostrophe 0x0a shift altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dead_abovering 0x1a shift altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+ampersand 0x25 shift altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_caron 0x28 shift altgr
+dead_cedilla 0x29
+dead_diaeresis 0x29 shift
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+grave 0x2f shift altgr
+braceleft 0x30 altgr
+apostrophe 0x30 shift altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/hu b/keymaps/hu
new file mode 100644
index 0000000..8aba444
--- /dev/null
+++ b/keymaps/hu
@@ -0,0 +1,115 @@
+# Hungarian keyboard layout (QWERTZ)
+# Created by: The NeverGone <never@delfin.klte.hu>
+
+include common
+map 0x40e
+
+
+# AltGr keys:
+notsign 0x29 altgr
+asciitilde 0x02 altgr
+caron 0x03 altgr
+asciicircum 0x04 altgr
+breve 0x05 altgr
+degree 0x06 altgr
+ogonek 0x07 altgr
+grave 0x08 altgr
+abovedot 0x09 altgr
+acute 0x0a altgr
+doubleacute 0x0b altgr
+diaeresis 0x0c altgr
+cedilla 0x0d altgr
+backslash 0x10 altgr
+bar 0x11 altgr
+EuroSign 0x12 altgr
+Iacute 0x17 altgr
+division 0x1a altgr
+multiply 0x1b altgr
+dstroke 0x1f altgr
+Dstroke 0x20 altgr
+bracketleft 0x21 altgr
+bracketright 0x22 altgr
+iacute 0x24 altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+dollar 0x27 altgr
+ssharp 0x28 altgr
+currency 0x2b altgr
+less 0x56 altgr
+greater 0x2c altgr
+numbersign 0x2d altgr
+ampersand 0x2e altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+semicolon 0x33 altgr
+asterisk 0x35 altgr
+
+
+# Shift keys:
+section 0x29 shift
+apostrophe 0x02 shift
+quotedbl 0x03 shift
+plus 0x04 shift
+exclam 0x05 shift
+percent 0x06 shift
+slash 0x07 shift
+equal 0x08 shift
+parenleft 0x09 shift
+parenright 0x0a shift
+Odiaeresis 0x0b shift
+Udiaeresis 0x0c shift
+Oacute 0x0d shift
+Z 0x15 shift
+Odoubleacute 0x1a shift
+Uacute 0x1b shift
+Eacute 0x27 shift
+Aacute 0x28 shift
+Udoubleacute 0x2b shift
+Y 0x2c shift
+question 0x33 shift
+colon 0x34 shift
+underscore 0x35 shift
+F13 0x3b shift
+F14 0x3c shift
+F15 0x3d shift
+F16 0x3e shift
+F17 0x3f shift
+F18 0x40 shift
+F19 0x41 shift
+F20 0x42 shift
+F21 0x43 shift
+F22 0x44 shift
+F23 0x57 shift
+F24 0x58 shift
+
+
+# Ctrl keys:
+F25 0x3b ctrl
+F26 0x3c ctrl
+F27 0x3d ctrl
+F28 0x3e ctrl
+F29 0x3f ctrl
+F30 0x40 ctrl
+F31 0x41 ctrl
+F32 0x42 ctrl
+F33 0x43 ctrl
+F34 0x44 ctrl
+F35 0x57 ctrl
+#NoSymbol 0x58 ctrl
+
+
+0 0x29
+odiaeresis 0x0b
+udiaeresis 0x0c
+oacute 0x0d
+z 0x15
+odoubleacute 0x1a
+uacute 0x1b
+eacute 0x27
+aacute 0x28
+udoubleacute 0x2b
+y 0x2c
+comma 0x33
+period 0x34
+minus 0x35
diff --git a/keymaps/is b/keymaps/is
new file mode 100644
index 0000000..8fde40f
--- /dev/null
+++ b/keymaps/is
@@ -0,0 +1,140 @@
+# 2004-03-16 Halldór Guðmundsson and Morten Lange
+# Keyboard definition file for the Icelandic keyboard
+# to be used in rdesktop 1.3.x ( See rdesktop.org)
+# generated from XKB map de, and changed manually
+# Location for example /usr/local/share/rdesktop/keymaps/is
+include common
+map 0x40f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+#section 0x04 shift
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+#ssharp 0x0c
+odiaeresis 0x0c
+#question 0x0c shift
+Odiaeresis 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+#acute 0x0d
+minus 0x0d
+#dead_acute 0x0d
+#grave 0x0d shift
+#dead_grave 0x0d shift
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+#z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+#thorn 0x19 altgr
+#THORN 0x19 shift altgr
+#udiaeresis 0x1a
+#Udiaeresis 0x1a shift
+#dead_diaeresis 0x1a altgr
+#dead_abovering 0x1a shift altgr
+eth 0x1a
+ETH 0x1a shift
+apostrophe 0x1b
+question 0x1b shift
+#plus 0x1b
+#asterisk 0x1b shift
+asciitilde 0x1b altgr
+#grave 0x1b altgr
+#dead_tilde 0x1b altgr
+#dead_macron 0x1b shift altgr
+#ae 0x1e altgr
+#AE 0x1e shift altgr
+#eth 0x20 altgr
+#eth 0x20
+#ETH 0x20 shift altgr
+#ETH 0x20 shift
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+#adiaeresis 0x27
+#Adiaeresis 0x27 shift
+ae 0x27
+AE 0x27 shift
+dead_doubleacute 0x27 altgr
+#adiaeresis 0x28
+#Adiaeresis 0x28 shift
+#dead_caron 0x28 shift altgr
+#asciicircum 0x29
+acute 0x28
+dead_acute 0x28
+#dead_circumflex 0x29
+#degree 0x29 shift
+#notsign 0x29 altgr
+plus 0x2b
+asterisk 0x2b shift
+grave 0x2b altgr
+#numbersign 0x2b
+#apostrophe 0x2b shift
+#dead_breve 0x2b shift altgr
+#y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+#minus 0x35
+#underscore 0x35 shift
+thorn 0x35
+THORN 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+
diff --git a/keymaps/it b/keymaps/it
new file mode 100644
index 0000000..00ca73a
--- /dev/null
+++ b/keymaps/it
@@ -0,0 +1,115 @@
+# generated from XKB map it
+include common
+map 0x410
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+grave 0x0c altgr
+questiondown 0x0c shift altgr
+igrave 0x0d
+asciicircum 0x0d shift
+asciitilde 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+egrave 0x1a
+eacute 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ograve 0x27
+ccedilla 0x27 shift
+at 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+agrave 0x28
+degree 0x28 shift
+numbersign 0x28 altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+ugrave 0x2b
+section 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/ja b/keymaps/ja
new file mode 100644
index 0000000..8fd0b9e
--- /dev/null
+++ b/keymaps/ja
@@ -0,0 +1,104 @@
+# generated from XKB map jp106
+include common
+map 0x411
+exclam 0x02 shift
+kana_NU 0x02 altgr
+quotedbl 0x03 shift
+kana_FU 0x03 altgr
+numbersign 0x04 shift
+kana_A 0x04 altgr
+kana_a 0x04 shift altgr
+dollar 0x05 shift
+kana_U 0x05 altgr
+kana_u 0x05 shift altgr
+percent 0x06 shift
+kana_E 0x06 altgr
+kana_e 0x06 shift altgr
+ampersand 0x07 shift
+kana_O 0x07 altgr
+kana_o 0x07 shift altgr
+apostrophe 0x08 shift
+kana_YA 0x08 altgr
+kana_ya 0x08 shift altgr
+parenleft 0x09 shift
+kana_YU 0x09 altgr
+kana_yu 0x09 shift altgr
+parenright 0x0a shift
+kana_YO 0x0a altgr
+kana_yo 0x0a shift altgr
+asciitilde 0x0b shift
+kana_WA 0x0b altgr
+kana_WO 0x0b shift altgr
+minus 0x0c
+equal 0x0c shift
+kana_HO 0x0c altgr
+asciicircum 0x0d
+asciitilde 0x0d shift
+kana_HE 0x0d altgr
+kana_TA 0x10 altgr
+kana_TE 0x11 altgr
+kana_I 0x12 altgr
+kana_i 0x12 shift altgr
+kana_SU 0x13 altgr
+kana_KA 0x14 altgr
+kana_N 0x15 altgr
+kana_NA 0x16 altgr
+kana_NI 0x17 altgr
+kana_RA 0x18 altgr
+kana_SE 0x19 altgr
+at 0x1a
+grave 0x1a shift
+voicedsound 0x1a altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+semivoicedsound 0x1b altgr
+kana_openingbracket 0x1b shift altgr
+kana_CHI 0x1e altgr
+kana_TO 0x1f altgr
+kana_SHI 0x20 altgr
+kana_HA 0x21 altgr
+kana_KI 0x22 altgr
+kana_KU 0x23 altgr
+kana_MA 0x24 altgr
+kana_NO 0x25 altgr
+kana_RI 0x26 altgr
+semicolon 0x27
+plus 0x27 shift
+kana_RE 0x27 altgr
+colon 0x28
+asterisk 0x28 shift
+kana_KE 0x28 altgr
+Zenkaku_Hankaku 0x29
+bracketright 0x2b
+braceright 0x2b shift
+kana_MU 0x2b altgr
+kana_closingbracket 0x2b shift altgr
+kana_TSU 0x2c altgr
+kana_tsu 0x2c shift altgr
+kana_SA 0x2d altgr
+kana_SO 0x2e altgr
+kana_HI 0x2f altgr
+kana_KO 0x30 altgr
+kana_MI 0x31 altgr
+kana_MO 0x32 altgr
+comma 0x33
+less 0x33 shift
+kana_NE 0x33 altgr
+kana_comma 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+kana_RU 0x34 altgr
+kana_fullstop 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+kana_ME 0x35 altgr
+kana_conjunctive 0x35 shift altgr
+Eisu_toggle 0x3a shift
+Execute 0x54 shift
+Kanji 0x70
+backslash 0x73
+bar 0x7d shift
+underscore 0x73 shift
+Henkan_Mode 0x79
+Katakana 0x70
+Muhenkan 0x7b
diff --git a/keymaps/lt b/keymaps/lt
new file mode 100644
index 0000000..3d9d619
--- /dev/null
+++ b/keymaps/lt
@@ -0,0 +1,57 @@
+# generated from XKB map lt
+include common
+map 0x427
+exclam 0x02 shift
+aogonek 0x02 altgr
+Aogonek 0x02 shift altgr
+at 0x03 shift
+ccaron 0x03 altgr
+Ccaron 0x03 shift altgr
+numbersign 0x04 shift
+eogonek 0x04 altgr
+Eogonek 0x04 shift altgr
+dollar 0x05 shift
+eabovedot 0x05 altgr
+Eabovedot 0x05 shift altgr
+percent 0x06 shift
+iogonek 0x06 altgr
+Iogonek 0x06 shift altgr
+asciicircum 0x07 shift
+scaron 0x07 altgr
+Scaron 0x07 shift altgr
+ampersand 0x08 shift
+uogonek 0x08 altgr
+Uogonek 0x08 shift altgr
+asterisk 0x09 shift
+umacron 0x09 altgr
+Umacron 0x09 shift altgr
+parenleft 0x0a shift
+doublelowquotemark 0x0a altgr
+parenright 0x0b shift
+leftdoublequotemark 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+zcaron 0x0d altgr
+Zcaron 0x0d shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
+endash 0x56
+EuroSign 0x56 shift
diff --git a/keymaps/lv b/keymaps/lv
new file mode 100644
index 0000000..1d91727
--- /dev/null
+++ b/keymaps/lv
@@ -0,0 +1,128 @@
+# generated from XKB map lv
+include common
+map 0x426
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+cent 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+emacron 0x12 altgr
+Emacron 0x12 shift altgr
+rcedilla 0x13 altgr
+Rcedilla 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+umacron 0x16 altgr
+Umacron 0x16 shift altgr
+imacron 0x17 altgr
+Imacron 0x17 shift altgr
+omacron 0x18 altgr
+Omacron 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ISO_Next_Group 0x1c shift
+amacron 0x1e altgr
+Amacron 0x1e shift altgr
+scaron 0x1f altgr
+Scaron 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+gcedilla 0x22 altgr
+Gcedilla 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kcedilla 0x25 altgr
+Kcedilla 0x25 shift altgr
+lcedilla 0x26 altgr
+Lcedilla 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+leftdoublequotemark 0x28 altgr
+doublelowquotemark 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zcaron 0x2c altgr
+Zcaron 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+ccaron 0x2e altgr
+Ccaron 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+ncedilla 0x31 altgr
+Ncedilla 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/keymaps/mk b/keymaps/mk
new file mode 100644
index 0000000..18c1504
--- /dev/null
+++ b/keymaps/mk
@@ -0,0 +1,101 @@
+# generated from XKB map mk
+include common
+map 0x42f
+exclam 0x02 shift
+at 0x03 shift
+doublelowquotemark 0x03 shift altgr
+numbersign 0x04 shift
+leftdoublequotemark 0x04 shift altgr
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_lje 0x10 altgr
+Cyrillic_LJE 0x10 shift altgr
+Cyrillic_nje 0x11 altgr
+Cyrillic_NJE 0x11 shift altgr
+Cyrillic_ie 0x12 altgr
+Cyrillic_IE 0x12 shift altgr
+Cyrillic_er 0x13 altgr
+Cyrillic_ER 0x13 shift altgr
+Cyrillic_te 0x14 altgr
+Cyrillic_TE 0x14 shift altgr
+Macedonia_dse 0x15 altgr
+Macedonia_DSE 0x15 shift altgr
+Cyrillic_u 0x16 altgr
+Cyrillic_U 0x16 shift altgr
+Cyrillic_i 0x17 altgr
+Cyrillic_I 0x17 shift altgr
+Cyrillic_o 0x18 altgr
+Cyrillic_O 0x18 shift altgr
+Cyrillic_pe 0x19 altgr
+Cyrillic_PE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_sha 0x1a altgr
+Cyrillic_SHA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Macedonia_gje 0x1b altgr
+Macedonia_GJE 0x1b shift altgr
+Cyrillic_a 0x1e altgr
+Cyrillic_A 0x1e shift altgr
+Cyrillic_es 0x1f altgr
+Cyrillic_ES 0x1f shift altgr
+Cyrillic_de 0x20 altgr
+Cyrillic_DE 0x20 shift altgr
+Cyrillic_ef 0x21 altgr
+Cyrillic_EF 0x21 shift altgr
+Cyrillic_ghe 0x22 altgr
+Cyrillic_GHE 0x22 shift altgr
+Cyrillic_ha 0x23 altgr
+Cyrillic_HA 0x23 shift altgr
+Cyrillic_je 0x24 altgr
+Cyrillic_JE 0x24 shift altgr
+Cyrillic_ka 0x25 altgr
+Cyrillic_KA 0x25 shift altgr
+Cyrillic_el 0x26 altgr
+Cyrillic_EL 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_che 0x27 altgr
+Cyrillic_CHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Macedonia_kje 0x28 altgr
+Macedonia_KJE 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_zhe 0x2b altgr
+Cyrillic_ZHE 0x2b shift altgr
+Cyrillic_ze 0x2c altgr
+Cyrillic_ZE 0x2c shift altgr
+Cyrillic_dzhe 0x2d altgr
+Cyrillic_DZHE 0x2d shift altgr
+Cyrillic_tse 0x2e altgr
+Cyrillic_TSE 0x2e shift altgr
+Cyrillic_ve 0x2f altgr
+Cyrillic_VE 0x2f shift altgr
+Cyrillic_be 0x30 altgr
+Cyrillic_BE 0x30 shift altgr
+Cyrillic_en 0x31 altgr
+Cyrillic_EN 0x31 shift altgr
+Cyrillic_em 0x32 altgr
+Cyrillic_EM 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+semicolon 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+colon 0x34 shift altgr
+slash 0x35
+question 0x35 shift
diff --git a/keymaps/modifiers b/keymaps/modifiers
new file mode 100644
index 0000000..d8b019f
--- /dev/null
+++ b/keymaps/modifiers
@@ -0,0 +1,17 @@
+Shift_R 0x36
+Shift_L 0x2a
+
+Alt_R 0xb8
+Mode_switch 0xb8
+Alt_L 0x38
+
+Control_R 0x9d
+Control_L 0x1d
+
+# Translate Super to Windows keys.
+# This is hardcoded. See documentation for details.
+Super_R 0xdb
+Super_L 0xdc
+
+# Translate Menu to the Windows Application key.
+Menu 0xdd
diff --git a/keymaps/nl b/keymaps/nl
new file mode 100644
index 0000000..bc823bd
--- /dev/null
+++ b/keymaps/nl
@@ -0,0 +1,60 @@
+# Dutch (Netherlands)
+include common
+map 0x413
+
+exclam 0x02 shift
+onesuperior 0x02 altgr
+quotebl 0x03 shift
+twosuperior 0x03 altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+underscore 0x08 shift
+sterling 0x08 altgr
+parenleft 0x09 shift
+braceleft 0x09 altgr
+parenright 0x0a shift
+braceright 0x0a altgr
+apostrophe 0x0b shift
+slash 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+degree 0x0d
+dead_tilde 0x0d shift
+dead_cedilla 0x0d altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+dead_diaeresis 0x1a
+dead_circumflex 0x1a shift
+asterisk 0x1b
+bar 0x1b shift
+ssharp 0x1f altgr
+plus 0x27
+plusminus 0x27 shift
+dead_acute 0x28
+dead_grave 0x28 shift
+at 0x29
+section 0x29 shift
+notsign 0x29 altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+hyphen 0x35
+equal 0x35 shift
+bracketright 0x56
+bracketleft 0x56 shift
+brokenbar 0x56 altgr
+
diff --git a/keymaps/nl-be b/keymaps/nl-be
new file mode 100644
index 0000000..34fc881
--- /dev/null
+++ b/keymaps/nl-be
@@ -0,0 +1,3 @@
+# Dutch (Belgium)
+map 0x813
+include common
diff --git a/keymaps/no b/keymaps/no
new file mode 100644
index 0000000..40a6479
--- /dev/null
+++ b/keymaps/no
@@ -0,0 +1,119 @@
+# generated from XKB map no
+include common
+map 0x414
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+backslash 0x0d
+dead_grave 0x0d shift
+dead_acute 0x0d altgr
+notsign 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+asciicircum 0x01b shift
+dead_tilde 0x1b altgr
+asciitilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+oslash 0x27
+Ooblique 0x27 shift
+dead_doubleacute 0x27 shift altgr
+ae 0x28
+AE 0x28 shift
+dead_caron 0x28 shift altgr
+bar 0x29
+section 0x29 shift
+brokenbar 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+onehalf 0x56 altgr
+threequarters 0x56 shift altgr
diff --git a/keymaps/pl b/keymaps/pl
new file mode 100644
index 0000000..09c600d
--- /dev/null
+++ b/keymaps/pl
@@ -0,0 +1,122 @@
+# generated from XKB map pl
+include common
+map 0x415
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+eogonek 0x12 altgr
+Eogonek 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+EuroSign 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oacute 0x18 altgr
+Oacute 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+aogonek 0x1e altgr
+Aogonek 0x1e shift altgr
+sacute 0x1f altgr
+Sacute 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zabovedot 0x2c altgr
+Zabovedot 0x2c shift altgr
+zacute 0x2d altgr
+Zacute 0x2d shift altgr
+cacute 0x2e altgr
+Cacute 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+nacute 0x31 altgr
+Nacute 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/pt b/keymaps/pt
new file mode 100644
index 0000000..c6941f6
--- /dev/null
+++ b/keymaps/pt
@@ -0,0 +1,113 @@
+# generated from XKB map pt
+include common
+map 0x816
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+dollar 0x05 shift
+section 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+guillemotleft 0x0d
+guillemotright 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+plus 0x1a
+asterisk 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_acute 0x1b
+dead_grave 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_doubleacute 0x27 shift altgr
+masculine 0x28
+ordfeminine 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+dead_tilde 0x2b
+dead_circumflex 0x2b shift
+dead_breve 0x2b shift altgr
+less 0x56
+greater 0x56 shift
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/keymaps/pt-br b/keymaps/pt-br
new file mode 100644
index 0000000..54bafc5
--- /dev/null
+++ b/keymaps/pt-br
@@ -0,0 +1,69 @@
+# generated from XKB map br
+include common
+map 0x416
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+onehalf 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+threequarters 0x04 shift altgr
+dollar 0x05 shift
+sterling 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+cent 0x06 altgr
+dead_diaeresis 0x07 shift
+notsign 0x07 altgr
+diaeresis 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+equal 0x0d
+plus 0x0d shift
+section 0x0d altgr
+EuroSign 0x12 altgr
+registered 0x13 altgr
+dead_acute 0x1a
+dead_grave 0x1a shift
+acute 0x1a altgr
+grave 0x1a shift altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+ordfeminine 0x1b altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_tilde 0x28
+dead_circumflex 0x28 shift
+asciitilde 0x28 altgr
+asciicircum 0x28 shift altgr
+apostrophe 0x29
+quotedbl 0x29 shift
+bracketright 0x2b
+braceright 0x2b shift
+masculine 0x2b altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+semicolon 0x35
+colon 0x35 shift
+comma 0x53 numlock
+backslash 0x56
+bar 0x56 shift
+slash 0x73
+question 0x73 shift
+degree 0x73 altgr
+KP_Decimal 0x34
diff --git a/keymaps/ru b/keymaps/ru
new file mode 100644
index 0000000..b3e7d24
--- /dev/null
+++ b/keymaps/ru
@@ -0,0 +1,109 @@
+# generated from XKB map ru
+include common
+map 0x419
+exclam 0x02 shift
+at 0x03 shift
+quotedbl 0x03 shift altgr
+numbersign 0x04 shift
+dollar 0x05 shift
+asterisk 0x05 shift altgr
+percent 0x06 shift
+colon 0x06 shift altgr
+asciicircum 0x07 shift
+comma 0x07 shift altgr
+ampersand 0x08 shift
+period 0x08 shift altgr
+asterisk 0x09 shift
+semicolon 0x09 shift altgr
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_shorti 0x10 altgr
+Cyrillic_SHORTI 0x10 shift altgr
+Cyrillic_tse 0x11 altgr
+Cyrillic_TSE 0x11 shift altgr
+Cyrillic_u 0x12 altgr
+Cyrillic_U 0x12 shift altgr
+Cyrillic_ka 0x13 altgr
+Cyrillic_KA 0x13 shift altgr
+Cyrillic_ie 0x14 altgr
+Cyrillic_IE 0x14 shift altgr
+Cyrillic_en 0x15 altgr
+Cyrillic_EN 0x15 shift altgr
+Cyrillic_ghe 0x16 altgr
+Cyrillic_GHE 0x16 shift altgr
+Cyrillic_sha 0x17 altgr
+Cyrillic_SHA 0x17 shift altgr
+Cyrillic_shcha 0x18 altgr
+Cyrillic_SHCHA 0x18 shift altgr
+Cyrillic_ze 0x19 altgr
+Cyrillic_ZE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_ha 0x1a altgr
+Cyrillic_HA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Cyrillic_hardsign 0x1b altgr
+Cyrillic_HARDSIGN 0x1b shift altgr
+Cyrillic_ef 0x1e altgr
+Cyrillic_EF 0x1e shift altgr
+Cyrillic_yeru 0x1f altgr
+Cyrillic_YERU 0x1f shift altgr
+Cyrillic_ve 0x20 altgr
+Cyrillic_VE 0x20 shift altgr
+Cyrillic_a 0x21 altgr
+Cyrillic_A 0x21 shift altgr
+Cyrillic_pe 0x22 altgr
+Cyrillic_PE 0x22 shift altgr
+Cyrillic_er 0x23 altgr
+Cyrillic_ER 0x23 shift altgr
+Cyrillic_o 0x24 altgr
+Cyrillic_O 0x24 shift altgr
+Cyrillic_el 0x25 altgr
+Cyrillic_EL 0x25 shift altgr
+Cyrillic_de 0x26 altgr
+Cyrillic_DE 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_zhe 0x27 altgr
+Cyrillic_ZHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Cyrillic_e 0x28 altgr
+Cyrillic_E 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+Cyrillic_io 0x29 altgr
+Cyrillic_IO 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_ya 0x2c altgr
+Cyrillic_YA 0x2c shift altgr
+Cyrillic_che 0x2d altgr
+Cyrillic_CHE 0x2d shift altgr
+Cyrillic_es 0x2e altgr
+Cyrillic_ES 0x2e shift altgr
+Cyrillic_em 0x2f altgr
+Cyrillic_EM 0x2f shift altgr
+Cyrillic_i 0x30 altgr
+Cyrillic_I 0x30 shift altgr
+Cyrillic_te 0x31 altgr
+Cyrillic_TE 0x31 shift altgr
+Cyrillic_softsign 0x32 altgr
+Cyrillic_SOFTSIGN 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Cyrillic_be 0x33 altgr
+Cyrillic_BE 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Cyrillic_yu 0x34 altgr
+Cyrillic_YU 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+slash 0x56 altgr
+bar 0x56 shift altgr
diff --git a/keymaps/sl b/keymaps/sl
new file mode 100644
index 0000000..56835a9
--- /dev/null
+++ b/keymaps/sl
@@ -0,0 +1,110 @@
+# generated from XKB map sl
+include common
+map 0x424
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_cedilla 0x29
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
diff --git a/keymaps/sv b/keymaps/sv
new file mode 100644
index 0000000..736d637
--- /dev/null
+++ b/keymaps/sv
@@ -0,0 +1,82 @@
+map 0x0000041d
+include common
+
+#
+# Top row
+#
+section 0x29
+onehalf 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
+
diff --git a/keymaps/th b/keymaps/th
new file mode 100644
index 0000000..b65b6da
--- /dev/null
+++ b/keymaps/th
@@ -0,0 +1,131 @@
+# generated from XKB map th
+include common
+map 0x41e
+exclam 0x02 shift
+Thai_lakkhangyao 0x02 altgr
+plus 0x02 shift altgr
+at 0x03 shift
+slash 0x03 altgr
+Thai_leknung 0x03 shift altgr
+numbersign 0x04 shift
+minus 0x04 altgr
+Thai_leksong 0x04 shift altgr
+dollar 0x05 shift
+Thai_phosamphao 0x05 altgr
+Thai_leksam 0x05 shift altgr
+percent 0x06 shift
+Thai_thothung 0x06 altgr
+Thai_leksi 0x06 shift altgr
+asciicircum 0x07 shift
+Thai_sarau 0x07 altgr
+Thai_sarauu 0x07 shift altgr
+ampersand 0x08 shift
+Thai_saraue 0x08 altgr
+Thai_baht 0x08 shift altgr
+asterisk 0x09 shift
+Thai_khokhwai 0x09 altgr
+Thai_lekha 0x09 shift altgr
+parenleft 0x0a shift
+Thai_totao 0x0a altgr
+Thai_lekhok 0x0a shift altgr
+parenright 0x0b shift
+Thai_chochan 0x0b altgr
+Thai_lekchet 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+Thai_khokhai 0x0c altgr
+Thai_lekpaet 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+Thai_chochang 0x0d altgr
+Thai_lekkao 0x0d shift altgr
+Thai_maiyamok 0x10 altgr
+Thai_leksun 0x10 shift altgr
+Thai_saraaimaimalai 0x11 altgr
+quotedbl 0x11 shift altgr
+Thai_saraam 0x12 altgr
+Thai_dochada 0x12 shift altgr
+Thai_phophan 0x13 altgr
+Thai_thonangmontho 0x13 shift altgr
+Thai_saraa 0x14 altgr
+Thai_thothong 0x14 shift altgr
+Thai_maihanakat 0x15 altgr
+Thai_nikhahit 0x15 shift altgr
+Thai_saraii 0x16 altgr
+Thai_maitri 0x16 shift altgr
+Thai_rorua 0x17 altgr
+Thai_nonen 0x17 shift altgr
+Thai_nonu 0x18 altgr
+Thai_paiyannoi 0x18 shift altgr
+Thai_yoyak 0x19 altgr
+Thai_yoying 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Thai_bobaimai 0x1a altgr
+Thai_thothan 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Thai_loling 0x1b altgr
+comma 0x1b shift altgr
+Thai_fofan 0x1e altgr
+Thai_ru 0x1e shift altgr
+Thai_hohip 0x1f altgr
+Thai_khorakhang 0x1f shift altgr
+Thai_kokai 0x20 altgr
+Thai_topatak 0x20 shift altgr
+Thai_dodek 0x21 altgr
+Thai_sarao 0x21 shift altgr
+Thai_sarae 0x22 altgr
+Thai_chochoe 0x22 shift altgr
+Thai_maitho 0x23 altgr
+Thai_maitaikhu 0x23 shift altgr
+Thai_maiek 0x24 altgr
+Thai_maichattawa 0x24 shift altgr
+Thai_saraaa 0x25 altgr
+Thai_sorusi 0x25 shift altgr
+Thai_sosua 0x26 altgr
+Thai_sosala 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Thai_wowaen 0x27 altgr
+Thai_soso 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Thai_ngongu 0x28 altgr
+period 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+underscore 0x29 altgr
+percent 0x29 shift altgr
+ISO_First_Group 0x2a shift
+backslash 0x2b
+bar 0x2b shift
+Thai_khokhuat 0x2b altgr
+Thai_khokhon 0x2b shift altgr
+Thai_phophung 0x2c altgr
+parenleft 0x2c shift altgr
+Thai_popla 0x2d altgr
+parenright 0x2d shift altgr
+Thai_saraae 0x2e altgr
+Thai_choching 0x2e shift altgr
+Thai_oang 0x2f altgr
+Thai_honokhuk 0x2f shift altgr
+Thai_sarai 0x30 altgr
+Thai_phinthu 0x30 shift altgr
+Thai_sarauee 0x31 altgr
+Thai_thanthakhat 0x31 shift altgr
+Thai_thothahan 0x32 altgr
+question 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Thai_moma 0x33 altgr
+Thai_thophuthao 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Thai_saraaimaimuan 0x34 altgr
+Thai_lochula 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+Thai_fofa 0x35 altgr
+Thai_lu 0x35 shift altgr
+ISO_Last_Group 0x36 shift
diff --git a/keymaps/tr b/keymaps/tr
new file mode 100644
index 0000000..5650e1e
--- /dev/null
+++ b/keymaps/tr
@@ -0,0 +1,123 @@
+# generated from XKB map tr
+include common
+map 0x41f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+apostrophe 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+dead_circumflex 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+plus 0x05 shift
+dollar 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+asterisk 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+idotless 0x17
+I 0x17 shift
+rightarrow 0x17 altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+gbreve 0x1a
+Gbreve 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+udiaeresis 0x1b
+Udiaeresis 0x1b shift
+asciitilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+scedilla 0x27
+Scedilla 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+i 0x28
+Iabovedot 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+quotedbl 0x29 shift
+asciitilde 0x29 altgr
+comma 0x2b
+semicolon 0x2b shift
+bar 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+odiaeresis 0x33
+Odiaeresis 0x33 shift
+less 0x33 altgr
+multiply 0x33 shift altgr
+ccedilla 0x34
+Ccedilla 0x34 shift
+greater 0x34 altgr
+division 0x34 shift altgr
+period 0x35
+colon 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/kqemu.c b/kqemu.c
new file mode 100644
index 0000000..b7a93b0
--- /dev/null
+++ b/kqemu.c
@@ -0,0 +1,905 @@
+/*
+ * KQEMU support
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#ifdef _WIN32
+#include <windows.h>
+#include <winioctl.h>
+#else
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#ifdef USE_KQEMU
+
+#define DEBUG
+//#define PROFILE
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "kqemu.h"
+
+/* compatibility stuff */
+#ifndef KQEMU_RET_SYSCALL
+#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */
+#endif
+#ifndef KQEMU_MAX_RAM_PAGES_TO_UPDATE
+#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512
+#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1)
+#endif
+#ifndef KQEMU_MAX_MODIFIED_RAM_PAGES
+#define KQEMU_MAX_MODIFIED_RAM_PAGES 512
+#endif
+
+#ifdef _WIN32
+#define KQEMU_DEVICE "\\\\.\\kqemu"
+#else
+#define KQEMU_DEVICE "/dev/kqemu"
+#endif
+
+#ifdef _WIN32
+#define KQEMU_INVALID_FD INVALID_HANDLE_VALUE
+HANDLE kqemu_fd = KQEMU_INVALID_FD;
+#define kqemu_closefd(x) CloseHandle(x)
+#else
+#define KQEMU_INVALID_FD -1
+int kqemu_fd = KQEMU_INVALID_FD;
+#define kqemu_closefd(x) close(x)
+#endif
+
+/* 0 = not allowed
+ 1 = user kqemu
+ 2 = kernel kqemu
+*/
+int kqemu_allowed = 1;
+unsigned long *pages_to_flush;
+unsigned int nb_pages_to_flush;
+unsigned long *ram_pages_to_update;
+unsigned int nb_ram_pages_to_update;
+unsigned long *modified_ram_pages;
+unsigned int nb_modified_ram_pages;
+uint8_t *modified_ram_pages_table;
+extern uint32_t **l1_phys_map;
+
+#define cpuid(index, eax, ebx, ecx, edx) \
+ asm volatile ("cpuid" \
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \
+ : "0" (index))
+
+#ifdef __x86_64__
+static int is_cpuid_supported(void)
+{
+ return 1;
+}
+#else
+static int is_cpuid_supported(void)
+{
+ int v0, v1;
+ asm volatile ("pushf\n"
+ "popl %0\n"
+ "movl %0, %1\n"
+ "xorl $0x00200000, %0\n"
+ "pushl %0\n"
+ "popf\n"
+ "pushf\n"
+ "popl %0\n"
+ : "=a" (v0), "=d" (v1)
+ :
+ : "cc");
+ return (v0 != v1);
+}
+#endif
+
+static void kqemu_update_cpuid(CPUState *env)
+{
+ int critical_features_mask, features, ext_features, ext_features_mask;
+ uint32_t eax, ebx, ecx, edx;
+
+ /* the following features are kept identical on the host and
+ target cpus because they are important for user code. Strictly
+ speaking, only SSE really matters because the OS must support
+ it if the user code uses it. */
+ critical_features_mask =
+ CPUID_CMOV | CPUID_CX8 |
+ CPUID_FXSR | CPUID_MMX | CPUID_SSE |
+ CPUID_SSE2 | CPUID_SEP;
+ ext_features_mask = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR;
+ if (!is_cpuid_supported()) {
+ features = 0;
+ ext_features = 0;
+ } else {
+ cpuid(1, eax, ebx, ecx, edx);
+ features = edx;
+ ext_features = ecx;
+ }
+#ifdef __x86_64__
+ /* NOTE: on x86_64 CPUs, SYSENTER is not supported in
+ compatibility mode, so in order to have the best performances
+ it is better not to use it */
+ features &= ~CPUID_SEP;
+#endif
+ env->cpuid_features = (env->cpuid_features & ~critical_features_mask) |
+ (features & critical_features_mask);
+ env->cpuid_ext_features = (env->cpuid_ext_features & ~ext_features_mask) |
+ (ext_features & ext_features_mask);
+ /* XXX: we could update more of the target CPUID state so that the
+ non accelerated code sees exactly the same CPU features as the
+ accelerated code */
+}
+
+int kqemu_init(CPUState *env)
+{
+ struct kqemu_init init;
+ int ret, version;
+#ifdef _WIN32
+ DWORD temp;
+#endif
+
+ if (!kqemu_allowed)
+ return -1;
+
+#ifdef _WIN32
+ kqemu_fd = CreateFile(KQEMU_DEVICE, GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+#else
+ kqemu_fd = open(KQEMU_DEVICE, O_RDWR);
+#endif
+ if (kqemu_fd == KQEMU_INVALID_FD) {
+ fprintf(stderr, "Could not open '%s' - QEMU acceleration layer not activated\n", KQEMU_DEVICE);
+ return -1;
+ }
+ version = 0;
+#ifdef _WIN32
+ DeviceIoControl(kqemu_fd, KQEMU_GET_VERSION, NULL, 0,
+ &version, sizeof(version), &temp, NULL);
+#else
+ ioctl(kqemu_fd, KQEMU_GET_VERSION, &version);
+#endif
+ if (version != KQEMU_VERSION) {
+ fprintf(stderr, "Version mismatch between kqemu module and qemu (%08x %08x) - disabling kqemu use\n",
+ version, KQEMU_VERSION);
+ goto fail;
+ }
+
+ pages_to_flush = qemu_vmalloc(KQEMU_MAX_PAGES_TO_FLUSH *
+ sizeof(unsigned long));
+ if (!pages_to_flush)
+ goto fail;
+
+ ram_pages_to_update = qemu_vmalloc(KQEMU_MAX_RAM_PAGES_TO_UPDATE *
+ sizeof(unsigned long));
+ if (!ram_pages_to_update)
+ goto fail;
+
+ modified_ram_pages = qemu_vmalloc(KQEMU_MAX_MODIFIED_RAM_PAGES *
+ sizeof(unsigned long));
+ if (!modified_ram_pages)
+ goto fail;
+ modified_ram_pages_table = qemu_mallocz(phys_ram_size >> TARGET_PAGE_BITS);
+ if (!modified_ram_pages_table)
+ goto fail;
+
+ init.ram_base = phys_ram_base;
+ init.ram_size = phys_ram_size;
+ init.ram_dirty = phys_ram_dirty;
+ init.phys_to_ram_map = l1_phys_map;
+ init.pages_to_flush = pages_to_flush;
+#if KQEMU_VERSION >= 0x010200
+ init.ram_pages_to_update = ram_pages_to_update;
+#endif
+#if KQEMU_VERSION >= 0x010300
+ init.modified_ram_pages = modified_ram_pages;
+#endif
+#ifdef _WIN32
+ ret = DeviceIoControl(kqemu_fd, KQEMU_INIT, &init, sizeof(init),
+ NULL, 0, &temp, NULL) == TRUE ? 0 : -1;
+#else
+ ret = ioctl(kqemu_fd, KQEMU_INIT, &init);
+#endif
+ if (ret < 0) {
+ fprintf(stderr, "Error %d while initializing QEMU acceleration layer - disabling it for now\n", ret);
+ fail:
+ kqemu_closefd(kqemu_fd);
+ kqemu_fd = KQEMU_INVALID_FD;
+ return -1;
+ }
+ kqemu_update_cpuid(env);
+ env->kqemu_enabled = kqemu_allowed;
+ nb_pages_to_flush = 0;
+ nb_ram_pages_to_update = 0;
+ return 0;
+}
+
+void kqemu_flush_page(CPUState *env, target_ulong addr)
+{
+#if defined(DEBUG)
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu_flush_page: addr=" TARGET_FMT_lx "\n", addr);
+ }
+#endif
+ if (nb_pages_to_flush >= KQEMU_MAX_PAGES_TO_FLUSH)
+ nb_pages_to_flush = KQEMU_FLUSH_ALL;
+ else
+ pages_to_flush[nb_pages_to_flush++] = addr;
+}
+
+void kqemu_flush(CPUState *env, int global)
+{
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu_flush:\n");
+ }
+#endif
+ nb_pages_to_flush = KQEMU_FLUSH_ALL;
+}
+
+void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr)
+{
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu_set_notdirty: addr=%08lx\n", ram_addr);
+ }
+#endif
+ /* we only track transitions to dirty state */
+ if (phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] != 0xff)
+ return;
+ if (nb_ram_pages_to_update >= KQEMU_MAX_RAM_PAGES_TO_UPDATE)
+ nb_ram_pages_to_update = KQEMU_RAM_PAGES_UPDATE_ALL;
+ else
+ ram_pages_to_update[nb_ram_pages_to_update++] = ram_addr;
+}
+
+static void kqemu_reset_modified_ram_pages(void)
+{
+ int i;
+ unsigned long page_index;
+
+ for(i = 0; i < nb_modified_ram_pages; i++) {
+ page_index = modified_ram_pages[i] >> TARGET_PAGE_BITS;
+ modified_ram_pages_table[page_index] = 0;
+ }
+ nb_modified_ram_pages = 0;
+}
+
+void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr)
+{
+ unsigned long page_index;
+ int ret;
+#ifdef _WIN32
+ DWORD temp;
+#endif
+
+ page_index = ram_addr >> TARGET_PAGE_BITS;
+ if (!modified_ram_pages_table[page_index]) {
+#if 0
+ printf("%d: modify_page=%08lx\n", nb_modified_ram_pages, ram_addr);
+#endif
+ modified_ram_pages_table[page_index] = 1;
+ modified_ram_pages[nb_modified_ram_pages++] = ram_addr;
+ if (nb_modified_ram_pages >= KQEMU_MAX_MODIFIED_RAM_PAGES) {
+ /* flush */
+#ifdef _WIN32
+ ret = DeviceIoControl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES,
+ &nb_modified_ram_pages,
+ sizeof(nb_modified_ram_pages),
+ NULL, 0, &temp, NULL);
+#else
+ ret = ioctl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES,
+ &nb_modified_ram_pages);
+#endif
+ kqemu_reset_modified_ram_pages();
+ }
+ }
+}
+
+struct fpstate {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+
+ uint32_t fpip;
+ uint32_t fpcs;
+ uint32_t fpoo;
+ uint32_t fpos;
+ uint8_t fpregs1[8 * 10];
+};
+
+struct fpxstate {
+ uint16_t fpuc;
+ uint16_t fpus;
+ uint16_t fptag;
+ uint16_t fop;
+ uint32_t fpuip;
+ uint16_t cs_sel;
+ uint16_t dummy0;
+ uint32_t fpudp;
+ uint16_t ds_sel;
+ uint16_t dummy1;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint8_t fpregs1[8 * 16];
+ uint8_t xmm_regs[16 * 16];
+ uint8_t dummy2[96];
+};
+
+static struct fpxstate fpx1 __attribute__((aligned(16)));
+
+static void restore_native_fp_frstor(CPUState *env)
+{
+ int fptag, i, j;
+ struct fpstate fp1, *fp = &fp1;
+
+ fp->fpuc = env->fpuc;
+ fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for (i=7; i>=0; i--) {
+ fptag <<= 2;
+ if (env->fptags[i]) {
+ fptag |= 3;
+ } else {
+ /* the FPU automatically computes it */
+ }
+ }
+ fp->fptag = fptag;
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&fp->fpregs1[i * 10], &env->fpregs[j].d, 10);
+ j = (j + 1) & 7;
+ }
+ asm volatile ("frstor %0" : "=m" (*fp));
+}
+
+static void save_native_fp_fsave(CPUState *env)
+{
+ int fptag, i, j;
+ uint16_t fpuc;
+ struct fpstate fp1, *fp = &fp1;
+
+ asm volatile ("fsave %0" : : "m" (*fp));
+ env->fpuc = fp->fpuc;
+ env->fpstt = (fp->fpus >> 11) & 7;
+ env->fpus = fp->fpus & ~0x3800;
+ fptag = fp->fptag;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag & 3) == 3);
+ fptag >>= 2;
+ }
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 10], 10);
+ j = (j + 1) & 7;
+ }
+ /* we must restore the default rounding state */
+ fpuc = 0x037f | (env->fpuc & (3 << 10));
+ asm volatile("fldcw %0" : : "m" (fpuc));
+}
+
+static void restore_native_fp_fxrstor(CPUState *env)
+{
+ struct fpxstate *fp = &fpx1;
+ int i, j, fptag;
+
+ fp->fpuc = env->fpuc;
+ fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for(i = 0; i < 8; i++)
+ fptag |= (env->fptags[i] << i);
+ fp->fptag = fptag ^ 0xff;
+
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&fp->fpregs1[i * 16], &env->fpregs[j].d, 10);
+ j = (j + 1) & 7;
+ }
+ if (env->cpuid_features & CPUID_SSE) {
+ fp->mxcsr = env->mxcsr;
+ /* XXX: check if DAZ is not available */
+ fp->mxcsr_mask = 0xffff;
+ memcpy(fp->xmm_regs, env->xmm_regs, CPU_NB_REGS * 16);
+ }
+ asm volatile ("fxrstor %0" : "=m" (*fp));
+}
+
+static void save_native_fp_fxsave(CPUState *env)
+{
+ struct fpxstate *fp = &fpx1;
+ int fptag, i, j;
+ uint16_t fpuc;
+
+ asm volatile ("fxsave %0" : : "m" (*fp));
+ env->fpuc = fp->fpuc;
+ env->fpstt = (fp->fpus >> 11) & 7;
+ env->fpus = fp->fpus & ~0x3800;
+ fptag = fp->fptag ^ 0xff;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = (fptag >> i) & 1;
+ }
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 16], 10);
+ j = (j + 1) & 7;
+ }
+ if (env->cpuid_features & CPUID_SSE) {
+ env->mxcsr = fp->mxcsr;
+ memcpy(env->xmm_regs, fp->xmm_regs, CPU_NB_REGS * 16);
+ }
+
+ /* we must restore the default rounding state */
+ asm volatile ("fninit");
+ fpuc = 0x037f | (env->fpuc & (3 << 10));
+ asm volatile("fldcw %0" : : "m" (fpuc));
+}
+
+static int do_syscall(CPUState *env,
+ struct kqemu_cpu_state *kenv)
+{
+ int selector;
+
+ selector = (env->star >> 32) & 0xffff;
+#ifdef __x86_64__
+ if (env->hflags & HF_LMA_MASK) {
+ env->regs[R_ECX] = kenv->next_eip;
+ env->regs[11] = env->eflags;
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~env->fmask;
+ if (env->hflags & HF_CS64_MASK)
+ env->eip = env->lstar;
+ else
+ env->eip = env->cstar;
+ } else
+#endif
+ {
+ env->regs[R_ECX] = (uint32_t)kenv->next_eip;
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
+ env->eip = (uint32_t)env->star;
+ }
+ return 2;
+}
+
+#ifdef CONFIG_PROFILER
+
+#define PC_REC_SIZE 1
+#define PC_REC_HASH_BITS 16
+#define PC_REC_HASH_SIZE (1 << PC_REC_HASH_BITS)
+
+typedef struct PCRecord {
+ unsigned long pc;
+ int64_t count;
+ struct PCRecord *next;
+} PCRecord;
+
+static PCRecord *pc_rec_hash[PC_REC_HASH_SIZE];
+static int nb_pc_records;
+
+static void kqemu_record_pc(unsigned long pc)
+{
+ unsigned long h;
+ PCRecord **pr, *r;
+
+ h = pc / PC_REC_SIZE;
+ h = h ^ (h >> PC_REC_HASH_BITS);
+ h &= (PC_REC_HASH_SIZE - 1);
+ pr = &pc_rec_hash[h];
+ for(;;) {
+ r = *pr;
+ if (r == NULL)
+ break;
+ if (r->pc == pc) {
+ r->count++;
+ return;
+ }
+ pr = &r->next;
+ }
+ r = malloc(sizeof(PCRecord));
+ r->count = 1;
+ r->pc = pc;
+ r->next = NULL;
+ *pr = r;
+ nb_pc_records++;
+}
+
+static int pc_rec_cmp(const void *p1, const void *p2)
+{
+ PCRecord *r1 = *(PCRecord **)p1;
+ PCRecord *r2 = *(PCRecord **)p2;
+ if (r1->count < r2->count)
+ return 1;
+ else if (r1->count == r2->count)
+ return 0;
+ else
+ return -1;
+}
+
+static void kqemu_record_flush(void)
+{
+ PCRecord *r, *r_next;
+ int h;
+
+ for(h = 0; h < PC_REC_HASH_SIZE; h++) {
+ for(r = pc_rec_hash[h]; r != NULL; r = r_next) {
+ r_next = r->next;
+ free(r);
+ }
+ pc_rec_hash[h] = NULL;
+ }
+ nb_pc_records = 0;
+}
+
+void kqemu_record_dump(void)
+{
+ PCRecord **pr, *r;
+ int i, h;
+ FILE *f;
+ int64_t total, sum;
+
+ pr = malloc(sizeof(PCRecord *) * nb_pc_records);
+ i = 0;
+ total = 0;
+ for(h = 0; h < PC_REC_HASH_SIZE; h++) {
+ for(r = pc_rec_hash[h]; r != NULL; r = r->next) {
+ pr[i++] = r;
+ total += r->count;
+ }
+ }
+ qsort(pr, nb_pc_records, sizeof(PCRecord *), pc_rec_cmp);
+
+ f = fopen("/tmp/kqemu.stats", "w");
+ if (!f) {
+ perror("/tmp/kqemu.stats");
+ exit(1);
+ }
+ fprintf(f, "total: %" PRId64 "\n", total);
+ sum = 0;
+ for(i = 0; i < nb_pc_records; i++) {
+ r = pr[i];
+ sum += r->count;
+ fprintf(f, "%08lx: %" PRId64 " %0.2f%% %0.2f%%\n",
+ r->pc,
+ r->count,
+ (double)r->count / (double)total * 100.0,
+ (double)sum / (double)total * 100.0);
+ }
+ fclose(f);
+ free(pr);
+
+ kqemu_record_flush();
+}
+#endif
+
+int kqemu_cpu_exec(CPUState *env)
+{
+ struct kqemu_cpu_state kcpu_state, *kenv = &kcpu_state;
+ int ret, cpl, i;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+
+#ifdef _WIN32
+ DWORD temp;
+#endif
+
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu: cpu_exec: enter\n");
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+ memcpy(kenv->regs, env->regs, sizeof(kenv->regs));
+ kenv->eip = env->eip;
+ kenv->eflags = env->eflags;
+ memcpy(&kenv->segs, &env->segs, sizeof(env->segs));
+ memcpy(&kenv->ldt, &env->ldt, sizeof(env->ldt));
+ memcpy(&kenv->tr, &env->tr, sizeof(env->tr));
+ memcpy(&kenv->gdt, &env->gdt, sizeof(env->gdt));
+ memcpy(&kenv->idt, &env->idt, sizeof(env->idt));
+ kenv->cr0 = env->cr[0];
+ kenv->cr2 = env->cr[2];
+ kenv->cr3 = env->cr[3];
+ kenv->cr4 = env->cr[4];
+ kenv->a20_mask = env->a20_mask;
+#if KQEMU_VERSION >= 0x010100
+ kenv->efer = env->efer;
+#endif
+#if KQEMU_VERSION >= 0x010300
+ kenv->tsc_offset = 0;
+ kenv->star = env->star;
+ kenv->sysenter_cs = env->sysenter_cs;
+ kenv->sysenter_esp = env->sysenter_esp;
+ kenv->sysenter_eip = env->sysenter_eip;
+#ifdef __x86_64__
+ kenv->lstar = env->lstar;
+ kenv->cstar = env->cstar;
+ kenv->fmask = env->fmask;
+ kenv->kernelgsbase = env->kernelgsbase;
+#endif
+#endif
+ if (env->dr[7] & 0xff) {
+ kenv->dr7 = env->dr[7];
+ kenv->dr0 = env->dr[0];
+ kenv->dr1 = env->dr[1];
+ kenv->dr2 = env->dr[2];
+ kenv->dr3 = env->dr[3];
+ } else {
+ kenv->dr7 = 0;
+ }
+ kenv->dr6 = env->dr[6];
+ cpl = (env->hflags & HF_CPL_MASK);
+ kenv->cpl = cpl;
+ kenv->nb_pages_to_flush = nb_pages_to_flush;
+#if KQEMU_VERSION >= 0x010200
+ kenv->user_only = (env->kqemu_enabled == 1);
+ kenv->nb_ram_pages_to_update = nb_ram_pages_to_update;
+#endif
+ nb_ram_pages_to_update = 0;
+
+#if KQEMU_VERSION >= 0x010300
+ kenv->nb_modified_ram_pages = nb_modified_ram_pages;
+#endif
+ kqemu_reset_modified_ram_pages();
+
+ if (env->cpuid_features & CPUID_FXSR)
+ restore_native_fp_fxrstor(env);
+ else
+ restore_native_fp_frstor(env);
+
+#ifdef _WIN32
+ if (DeviceIoControl(kqemu_fd, KQEMU_EXEC,
+ kenv, sizeof(struct kqemu_cpu_state),
+ kenv, sizeof(struct kqemu_cpu_state),
+ &temp, NULL)) {
+ ret = kenv->retval;
+ } else {
+ ret = -1;
+ }
+#else
+#if KQEMU_VERSION >= 0x010100
+ ioctl(kqemu_fd, KQEMU_EXEC, kenv);
+ ret = kenv->retval;
+#else
+ ret = ioctl(kqemu_fd, KQEMU_EXEC, kenv);
+#endif
+#endif
+ if (env->cpuid_features & CPUID_FXSR)
+ save_native_fp_fxsave(env);
+ else
+ save_native_fp_fsave(env);
+
+ memcpy(env->regs, kenv->regs, sizeof(env->regs));
+ env->eip = kenv->eip;
+ env->eflags = kenv->eflags;
+ memcpy(env->segs, kenv->segs, sizeof(env->segs));
+ cpu_x86_set_cpl(env, kenv->cpl);
+ memcpy(&env->ldt, &kenv->ldt, sizeof(env->ldt));
+#if 0
+ /* no need to restore that */
+ memcpy(env->tr, kenv->tr, sizeof(env->tr));
+ memcpy(env->gdt, kenv->gdt, sizeof(env->gdt));
+ memcpy(env->idt, kenv->idt, sizeof(env->idt));
+ env->a20_mask = kenv->a20_mask;
+#endif
+ env->cr[0] = kenv->cr0;
+ env->cr[4] = kenv->cr4;
+ env->cr[3] = kenv->cr3;
+ env->cr[2] = kenv->cr2;
+ env->dr[6] = kenv->dr6;
+#if KQEMU_VERSION >= 0x010300
+#ifdef __x86_64__
+ env->kernelgsbase = kenv->kernelgsbase;
+#endif
+#endif
+
+ /* flush pages as indicated by kqemu */
+ if (kenv->nb_pages_to_flush >= KQEMU_FLUSH_ALL) {
+ tlb_flush(env, 1);
+ } else {
+ for(i = 0; i < kenv->nb_pages_to_flush; i++) {
+ tlb_flush_page(env, pages_to_flush[i]);
+ }
+ }
+ nb_pages_to_flush = 0;
+
+#ifdef CONFIG_PROFILER
+ kqemu_time += profile_getclock() - ti;
+ kqemu_exec_count++;
+#endif
+
+#if KQEMU_VERSION >= 0x010200
+ if (kenv->nb_ram_pages_to_update > 0) {
+ cpu_tlb_update_dirty(env);
+ }
+#endif
+
+#if KQEMU_VERSION >= 0x010300
+ if (kenv->nb_modified_ram_pages > 0) {
+ for(i = 0; i < kenv->nb_modified_ram_pages; i++) {
+ unsigned long addr;
+ addr = modified_ram_pages[i];
+ tb_invalidate_phys_page_range(addr, addr + TARGET_PAGE_SIZE, 0);
+ }
+ }
+#endif
+
+ /* restore the hidden flags */
+ {
+ unsigned int new_hflags;
+#ifdef TARGET_X86_64
+ if ((env->hflags & HF_LMA_MASK) &&
+ (env->segs[R_CS].flags & DESC_L_MASK)) {
+ /* long mode */
+ new_hflags = HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ } else
+#endif
+ {
+ /* legacy / compatibility case */
+ new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_CS32_SHIFT);
+ new_hflags |= (env->segs[R_SS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (!(env->cr[0] & CR0_PE_MASK) ||
+ (env->eflags & VM_MASK) ||
+ !(env->hflags & HF_CS32_MASK)) {
+ /* XXX: try to avoid this test. The problem comes from the
+ fact that is real mode or vm86 mode we only modify the
+ 'base' and 'selector' fields of the segment cache to go
+ faster. A solution may be to force addseg to one in
+ translate-i386.c. */
+ new_hflags |= HF_ADDSEG_MASK;
+ } else {
+ new_hflags |= ((env->segs[R_DS].base |
+ env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) <<
+ HF_ADDSEG_SHIFT;
+ }
+ }
+ env->hflags = (env->hflags &
+ ~(HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)) |
+ new_hflags;
+ }
+ /* update FPU flags */
+ env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) |
+ ((env->cr[0] << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK));
+ if (env->cr[4] & CR4_OSFXSR_MASK)
+ env->hflags |= HF_OSFXSR_MASK;
+ else
+ env->hflags &= ~HF_OSFXSR_MASK;
+
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu: kqemu_cpu_exec: ret=0x%x\n", ret);
+ }
+#endif
+ if (ret == KQEMU_RET_SYSCALL) {
+ /* syscall instruction */
+ return do_syscall(env, kenv);
+ } else
+ if ((ret & 0xff00) == KQEMU_RET_INT) {
+ env->exception_index = ret & 0xff;
+ env->error_code = 0;
+ env->exception_is_int = 1;
+ env->exception_next_eip = kenv->next_eip;
+#ifdef CONFIG_PROFILER
+ kqemu_ret_int_count++;
+#endif
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu: interrupt v=%02x:\n",
+ env->exception_index);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+ return 1;
+ } else if ((ret & 0xff00) == KQEMU_RET_EXCEPTION) {
+ env->exception_index = ret & 0xff;
+ env->error_code = kenv->error_code;
+ env->exception_is_int = 0;
+ env->exception_next_eip = 0;
+#ifdef CONFIG_PROFILER
+ kqemu_ret_excp_count++;
+#endif
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "kqemu: exception v=%02x e=%04x:\n",
+ env->exception_index, env->error_code);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+ return 1;
+ } else if (ret == KQEMU_RET_INTR) {
+#ifdef CONFIG_PROFILER
+ kqemu_ret_intr_count++;
+#endif
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+ return 0;
+ } else if (ret == KQEMU_RET_SOFTMMU) {
+#ifdef CONFIG_PROFILER
+ {
+ unsigned long pc = env->eip + env->segs[R_CS].base;
+ kqemu_record_pc(pc);
+ }
+#endif
+#ifdef DEBUG
+ if (loglevel & CPU_LOG_INT) {
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+ return 2;
+ } else {
+ cpu_dump_state(env, stderr, fprintf, 0);
+ fprintf(stderr, "Unsupported return value: 0x%x\n", ret);
+ exit(1);
+ }
+ return 0;
+}
+
+void kqemu_cpu_interrupt(CPUState *env)
+{
+#if defined(_WIN32) && KQEMU_VERSION >= 0x010101
+ /* cancelling the I/O request causes KQEMU to finish executing the
+ current block and successfully returning. */
+ CancelIo(kqemu_fd);
+#endif
+}
+
+#endif
diff --git a/kqemu.h b/kqemu.h
new file mode 100644
index 0000000..892e335
--- /dev/null
+++ b/kqemu.h
@@ -0,0 +1,132 @@
+/*
+ * KQEMU header
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef KQEMU_H
+#define KQEMU_H
+
+#define KQEMU_VERSION 0x010300
+
+struct kqemu_segment_cache {
+ uint32_t selector;
+ unsigned long base;
+ uint32_t limit;
+ uint32_t flags;
+};
+
+struct kqemu_cpu_state {
+#ifdef __x86_64__
+ unsigned long regs[16];
+#else
+ unsigned long regs[8];
+#endif
+ unsigned long eip;
+ unsigned long eflags;
+
+ uint32_t dummy0, dummy1, dumm2, dummy3, dummy4;
+
+ struct kqemu_segment_cache segs[6]; /* selector values */
+ struct kqemu_segment_cache ldt;
+ struct kqemu_segment_cache tr;
+ struct kqemu_segment_cache gdt; /* only base and limit are used */
+ struct kqemu_segment_cache idt; /* only base and limit are used */
+
+ unsigned long cr0;
+ unsigned long dummy5;
+ unsigned long cr2;
+ unsigned long cr3;
+ unsigned long cr4;
+ uint32_t a20_mask;
+
+ /* sysenter registers */
+ uint32_t sysenter_cs;
+ uint32_t sysenter_esp;
+ uint32_t sysenter_eip;
+ uint64_t efer __attribute__((aligned(8)));
+ uint64_t star;
+#ifdef __x86_64__
+ unsigned long lstar;
+ unsigned long cstar;
+ unsigned long fmask;
+ unsigned long kernelgsbase;
+#endif
+ uint64_t tsc_offset;
+
+ unsigned long dr0;
+ unsigned long dr1;
+ unsigned long dr2;
+ unsigned long dr3;
+ unsigned long dr6;
+ unsigned long dr7;
+
+ uint8_t cpl;
+ uint8_t user_only;
+
+ uint32_t error_code; /* error_code when exiting with an exception */
+ unsigned long next_eip; /* next eip value when exiting with an interrupt */
+ unsigned int nb_pages_to_flush; /* number of pages to flush,
+ KQEMU_FLUSH_ALL means full flush */
+#define KQEMU_MAX_PAGES_TO_FLUSH 512
+#define KQEMU_FLUSH_ALL (KQEMU_MAX_PAGES_TO_FLUSH + 1)
+
+ long retval;
+
+ /* number of ram_dirty entries to update */
+ unsigned int nb_ram_pages_to_update;
+#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512
+#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1)
+
+#define KQEMU_MAX_MODIFIED_RAM_PAGES 512
+ unsigned int nb_modified_ram_pages;
+};
+
+struct kqemu_init {
+ uint8_t *ram_base; /* must be page aligned */
+ unsigned long ram_size; /* must be multiple of 4 KB */
+ uint8_t *ram_dirty; /* must be page aligned */
+ uint32_t **phys_to_ram_map; /* must be page aligned */
+ unsigned long *pages_to_flush; /* must be page aligned */
+ unsigned long *ram_pages_to_update; /* must be page aligned */
+ unsigned long *modified_ram_pages; /* must be page aligned */
+};
+
+#define KQEMU_RET_ABORT (-1)
+#define KQEMU_RET_EXCEPTION 0x0000 /* 8 low order bit are the exception */
+#define KQEMU_RET_INT 0x0100 /* 8 low order bit are the interrupt */
+#define KQEMU_RET_SOFTMMU 0x0200 /* emulation needed (I/O or
+ unsupported INSN) */
+#define KQEMU_RET_INTR 0x0201 /* interrupted by a signal */
+#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */
+
+#ifdef _WIN32
+#define KQEMU_EXEC CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define KQEMU_INIT CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define KQEMU_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define KQEMU_MODIFY_RAM_PAGES CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#else
+#define KQEMU_EXEC _IOWR('q', 1, struct kqemu_cpu_state)
+#define KQEMU_INIT _IOW('q', 2, struct kqemu_init)
+#define KQEMU_GET_VERSION _IOR('q', 3, int)
+#define KQEMU_MODIFY_RAM_PAGES _IOW('q', 4, int)
+#endif
+
+#endif /* KQEMU_H */
diff --git a/linux-2.6.9-qemu-fast.patch b/linux-2.6.9-qemu-fast.patch
new file mode 100644
index 0000000..f8ecd0b
--- /dev/null
+++ b/linux-2.6.9-qemu-fast.patch
@@ -0,0 +1,70 @@
+--- linux-2.6.9/arch/i386/Kconfig 2004-10-18 23:53:22.000000000 +0200
++++ linux-2.6.9-qemu/arch/i386/Kconfig 2004-12-07 21:56:49.000000000 +0100
+@@ -337,6 +337,14 @@ config X86_GENERIC
+
+ endif
+
++config QEMU
++ bool "Kernel to run under QEMU"
++ depends on EXPERIMENTAL
++ help
++ Select this if you want to boot the kernel inside qemu-fast,
++ the non-mmu version of the x86 emulator. See
++ <http://fabrice.bellard.free.fr/qemu/>. Say N.
++
+ #
+ # Define implied options from the CPU selection here
+ #
+--- linux-2.6.9/include/asm-i386/fixmap.h 2004-10-18 23:53:08.000000000 +0200
++++ linux-2.6.9-qemu/include/asm-i386/fixmap.h 2004-12-07 23:16:11.000000000 +0100
+@@ -20,7 +20,11 @@
+ * Leave one empty page between vmalloc'ed areas and
+ * the start of the fixmap.
+ */
++#ifdef CONFIG_QEMU
++#define __FIXADDR_TOP 0xa7fff000
++#else
+ #define __FIXADDR_TOP 0xfffff000
++#endif
+
+ #ifndef __ASSEMBLY__
+ #include <linux/kernel.h>
+--- linux-2.6.9/include/asm-i386/page.h 2004-10-18 23:53:22.000000000 +0200
++++ linux-2.6.9-qemu/include/asm-i386/page.h 2004-12-07 21:56:49.000000000 +0100
+@@ -121,12 +121,19 @@ extern int sysctl_legacy_va_layout;
+ #endif /* __ASSEMBLY__ */
+
+ #ifdef __ASSEMBLY__
++#ifdef CONFIG_QEMU
++#define __PAGE_OFFSET (0x90000000)
++#else
+ #define __PAGE_OFFSET (0xC0000000)
++#endif /* QEMU */
++#else
++#ifdef CONFIG_QEMU
++#define __PAGE_OFFSET (0x90000000UL)
+ #else
+ #define __PAGE_OFFSET (0xC0000000UL)
++#endif /* QEMU */
+ #endif
+
+-
+ #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
+ #define VMALLOC_RESERVE ((unsigned long)__VMALLOC_RESERVE)
+ #define MAXMEM (-__PAGE_OFFSET-__VMALLOC_RESERVE)
+--- linux-2.6.9/include/asm-i386/param.h 2004-10-18 23:53:24.000000000 +0200
++++ linux-2.6.9-qemu/include/asm-i386/param.h 2004-12-07 21:56:49.000000000 +0100
+@@ -2,7 +2,12 @@
+ #define _ASMi386_PARAM_H
+
+ #ifdef __KERNEL__
+-# define HZ 1000 /* Internal kernel timer frequency */
++# include <linux/config.h>
++# ifdef CONFIG_QEMU
++# define HZ 100
++# else
++# define HZ 1000 /* Internal kernel timer frequency */
++# endif
+ # define USER_HZ 100 /* .. some user interfaces are in "ticks" */
+ # define CLOCKS_PER_SEC (USER_HZ) /* like times() */
+ #endif
diff --git a/linux-user/arm-semi.c b/linux-user/arm-semi.c
new file mode 100644
index 0000000..250d5b7
--- /dev/null
+++ b/linux-user/arm-semi.c
@@ -0,0 +1,203 @@
+/*
+ * Arm "Angel" semihosting syscalls
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "qemu.h"
+
+#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
+
+#define SYS_OPEN 0x01
+#define SYS_CLOSE 0x02
+#define SYS_WRITEC 0x03
+#define SYS_WRITE0 0x04
+#define SYS_WRITE 0x05
+#define SYS_READ 0x06
+#define SYS_READC 0x07
+#define SYS_ISTTY 0x09
+#define SYS_SEEK 0x0a
+#define SYS_FLEN 0x0c
+#define SYS_TMPNAM 0x0d
+#define SYS_REMOVE 0x0e
+#define SYS_RENAME 0x0f
+#define SYS_CLOCK 0x10
+#define SYS_TIME 0x11
+#define SYS_SYSTEM 0x12
+#define SYS_ERRNO 0x13
+#define SYS_GET_CMDLINE 0x15
+#define SYS_HEAPINFO 0x16
+#define SYS_EXIT 0x18
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+int open_modeflags[12] = {
+ O_RDONLY,
+ O_RDONLY | O_BINARY,
+ O_RDWR,
+ O_RDWR | O_BINARY,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ O_RDWR | O_CREAT | O_TRUNC,
+ O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+ O_WRONLY | O_CREAT | O_APPEND,
+ O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
+ O_RDWR | O_CREAT | O_APPEND,
+ O_RDWR | O_CREAT | O_APPEND | O_BINARY
+};
+
+static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
+{
+ if (code == (uint32_t)-1)
+ ts->swi_errno = errno;
+ return code;
+}
+
+#define ARG(n) tget32(args + n * 4)
+uint32_t do_arm_semihosting(CPUState *env)
+{
+ target_ulong args;
+ char * s;
+ int nr;
+ uint32_t ret;
+ TaskState *ts = env->opaque;
+
+ nr = env->regs[0];
+ args = env->regs[1];
+ switch (nr) {
+ case SYS_OPEN:
+ s = (char *)g2h(ARG(0));
+ if (ARG(1) >= 12)
+ return (uint32_t)-1;
+ if (strcmp(s, ":tt") == 0) {
+ if (ARG(1) < 4)
+ return STDIN_FILENO;
+ else
+ return STDOUT_FILENO;
+ }
+ return set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+ case SYS_CLOSE:
+ return set_swi_errno(ts, close(ARG(0)));
+ case SYS_WRITEC:
+ {
+ char c = tget8(args);
+ /* Write to debug console. stderr is near enough. */
+ return write(STDERR_FILENO, &c, 1);
+ }
+ case SYS_WRITE0:
+ s = lock_user_string(args);
+ ret = write(STDERR_FILENO, s, strlen(s));
+ unlock_user(s, args, 0);
+ return ret;
+ case SYS_WRITE:
+ ret = set_swi_errno(ts, write(ARG(0), g2h(ARG(1)), ARG(2)));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return ARG(2) - ret;
+ case SYS_READ:
+ ret = set_swi_errno(ts, read(ARG(0), g2h(ARG(1)), ARG(2)));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return ARG(2) - ret;
+ case SYS_READC:
+ /* XXX: Read from debug cosole. Not implemented. */
+ return 0;
+ case SYS_ISTTY:
+ return isatty(ARG(0));
+ case SYS_SEEK:
+ ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return 0;
+ case SYS_FLEN:
+ {
+ struct stat buf;
+ ret = set_swi_errno(ts, fstat(ARG(0), &buf));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return buf.st_size;
+ }
+ case SYS_TMPNAM:
+ /* XXX: Not implemented. */
+ return -1;
+ case SYS_REMOVE:
+ return set_swi_errno(ts, remove((char *)g2h(ARG(0))));
+ case SYS_RENAME:
+ return set_swi_errno(ts, rename((char *)g2h(ARG(0)),
+ (char *)g2h(ARG(2))));
+ case SYS_CLOCK:
+ return clock() / (CLOCKS_PER_SEC / 100);
+ case SYS_TIME:
+ return set_swi_errno(ts, time(NULL));
+ case SYS_SYSTEM:
+ return set_swi_errno(ts, system((char *)g2h(ARG(0))));
+ case SYS_ERRNO:
+ return ts->swi_errno;
+ case SYS_GET_CMDLINE:
+ /* XXX: Not implemented. */
+ s = (char *)g2h(ARG(0));
+ *s = 0;
+ return -1;
+ case SYS_HEAPINFO:
+ {
+ uint32_t *ptr;
+ uint32_t limit;
+
+ /* Some C llibraries assume the heap immediately follows .bss, so
+ allocate it using sbrk. */
+ if (!ts->heap_limit) {
+ long ret;
+
+ ts->heap_base = do_brk(0);
+ limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
+ /* Try a big heap, and reduce the size if that fails. */
+ for (;;) {
+ ret = do_brk(limit);
+ if (ret != -1)
+ break;
+ limit = (ts->heap_base >> 1) + (limit >> 1);
+ }
+ ts->heap_limit = limit;
+ }
+
+ page_unprotect_range (ARG(0), 32);
+ ptr = (uint32_t *)g2h(ARG(0));
+ ptr[0] = tswap32(ts->heap_base);
+ ptr[1] = tswap32(ts->heap_limit);
+ ptr[2] = tswap32(ts->stack_base);
+ ptr[3] = tswap32(0); /* Stack limit. */
+ return 0;
+ }
+ case SYS_EXIT:
+ exit(0);
+ default:
+ fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+}
+
diff --git a/linux-user/arm/syscall.h b/linux-user/arm/syscall.h
new file mode 100644
index 0000000..1d508f0
--- /dev/null
+++ b/linux-user/arm/syscall.h
@@ -0,0 +1,42 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ target_long uregs[18];
+};
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+#define ARM_SYSCALL_BASE 0x900000
+#define ARM_THUMB_SYSCALL 0
+
+#define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2)
+
+#define ARM_NR_semihosting 0x123456
+#define ARM_NR_thumb_semihosting 0xAB
+
+#if defined(TARGET_WORDS_BIGENDIAN)
+#define UNAME_MACHINE "armv5teb"
+#else
+#define UNAME_MACHINE "armv5tel"
+#endif
+
+uint32_t do_arm_semihosting(CPUState *);
diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h
new file mode 100644
index 0000000..195e459
--- /dev/null
+++ b/linux-user/arm/syscall_nr.h
@@ -0,0 +1,262 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall ( 0)
+#define TARGET_NR_exit ( 1)
+#define TARGET_NR_fork ( 2)
+#define TARGET_NR_read ( 3)
+#define TARGET_NR_write ( 4)
+#define TARGET_NR_open ( 5)
+#define TARGET_NR_close ( 6)
+#define TARGET_NR_waitpid ( 7) /* removed */
+#define TARGET_NR_creat ( 8)
+#define TARGET_NR_link ( 9)
+#define TARGET_NR_unlink ( 10)
+#define TARGET_NR_execve ( 11)
+#define TARGET_NR_chdir ( 12)
+#define TARGET_NR_time ( 13)
+#define TARGET_NR_mknod ( 14)
+#define TARGET_NR_chmod ( 15)
+#define TARGET_NR_lchown ( 16)
+#define TARGET_NR_break ( 17) /* removed */
+ /* 18 was sys_stat */
+#define TARGET_NR_lseek ( 19)
+#define TARGET_NR_getpid ( 20)
+#define TARGET_NR_mount ( 21)
+#define TARGET_NR_umount ( 22)
+#define TARGET_NR_setuid ( 23)
+#define TARGET_NR_getuid ( 24)
+#define TARGET_NR_stime ( 25)
+#define TARGET_NR_ptrace ( 26)
+#define TARGET_NR_alarm ( 27)
+
+#define TARGET_NR_pause ( 29)
+#define TARGET_NR_utime ( 30)
+#define TARGET_NR_stty ( 31) /* removed */
+#define TARGET_NR_gtty ( 32) /* removed */
+#define TARGET_NR_access ( 33)
+#define TARGET_NR_nice ( 34)
+#define TARGET_NR_ftime ( 35) /* removed */
+#define TARGET_NR_sync ( 36)
+#define TARGET_NR_kill ( 37)
+#define TARGET_NR_rename ( 38)
+#define TARGET_NR_mkdir ( 39)
+#define TARGET_NR_rmdir ( 40)
+#define TARGET_NR_dup ( 41)
+#define TARGET_NR_pipe ( 42)
+#define TARGET_NR_times ( 43)
+#define TARGET_NR_prof ( 44) /* removed */
+#define TARGET_NR_brk ( 45)
+#define TARGET_NR_setgid ( 46)
+#define TARGET_NR_getgid ( 47)
+#define TARGET_NR_signal ( 48) /* removed */
+#define TARGET_NR_geteuid ( 49)
+#define TARGET_NR_getegid ( 50)
+#define TARGET_NR_acct ( 51)
+#define TARGET_NR_umount2 ( 52)
+#define TARGET_NR_lock ( 53) /* removed */
+#define TARGET_NR_ioctl ( 54)
+#define TARGET_NR_fcntl ( 55)
+#define TARGET_NR_mpx ( 56) /* removed */
+#define TARGET_NR_setpgid ( 57)
+#define TARGET_NR_ulimit ( 58) /* removed */
+ /* 59 was sys_olduname */
+#define TARGET_NR_umask ( 60)
+#define TARGET_NR_chroot ( 61)
+#define TARGET_NR_ustat ( 62)
+#define TARGET_NR_dup2 ( 63)
+#define TARGET_NR_getppid ( 64)
+#define TARGET_NR_getpgrp ( 65)
+#define TARGET_NR_setsid ( 66)
+#define TARGET_NR_sigaction ( 67)
+#define TARGET_NR_sgetmask ( 68) /* removed */
+#define TARGET_NR_ssetmask ( 69) /* removed */
+#define TARGET_NR_setreuid ( 70)
+#define TARGET_NR_setregid ( 71)
+#define TARGET_NR_sigsuspend ( 72)
+#define TARGET_NR_sigpending ( 73)
+#define TARGET_NR_sethostname ( 74)
+#define TARGET_NR_setrlimit ( 75)
+#define TARGET_NR_getrlimit ( 76) /* Back compat 2GB limited rlimit */
+#define TARGET_NR_getrusage ( 77)
+#define TARGET_NR_gettimeofday ( 78)
+#define TARGET_NR_settimeofday ( 79)
+#define TARGET_NR_getgroups ( 80)
+#define TARGET_NR_setgroups ( 81)
+#define TARGET_NR_select ( 82)
+#define TARGET_NR_symlink ( 83)
+ /* 84 was sys_lstat */
+#define TARGET_NR_readlink ( 85)
+#define TARGET_NR_uselib ( 86)
+#define TARGET_NR_swapon ( 87)
+#define TARGET_NR_reboot ( 88)
+#define TARGET_NR_readdir ( 89)
+#define TARGET_NR_mmap ( 90)
+#define TARGET_NR_munmap ( 91)
+#define TARGET_NR_truncate ( 92)
+#define TARGET_NR_ftruncate ( 93)
+#define TARGET_NR_fchmod ( 94)
+#define TARGET_NR_fchown ( 95)
+#define TARGET_NR_getpriority ( 96)
+#define TARGET_NR_setpriority ( 97)
+#define TARGET_NR_profil ( 98) /* removed */
+#define TARGET_NR_statfs ( 99)
+#define TARGET_NR_fstatfs (100)
+#define TARGET_NR_ioperm (101)
+#define TARGET_NR_socketcall (102)
+#define TARGET_NR_syslog (103)
+#define TARGET_NR_setitimer (104)
+#define TARGET_NR_getitimer (105)
+#define TARGET_NR_stat (106)
+#define TARGET_NR_lstat (107)
+#define TARGET_NR_fstat (108)
+ /* 109 was sys_uname */
+ /* 110 was sys_iopl */
+#define TARGET_NR_vhangup (111)
+#define TARGET_NR_idle (112)
+#define TARGET_NR_syscall (113) /* syscall to call a syscall! */
+#define TARGET_NR_wait4 (114)
+#define TARGET_NR_swapoff (115)
+#define TARGET_NR_sysinfo (116)
+#define TARGET_NR_ipc (117)
+#define TARGET_NR_fsync (118)
+#define TARGET_NR_sigreturn (119)
+#define TARGET_NR_clone (120)
+#define TARGET_NR_setdomainname (121)
+#define TARGET_NR_uname (122)
+#define TARGET_NR_modify_ldt (123)
+#define TARGET_NR_adjtimex (124)
+#define TARGET_NR_mprotect (125)
+#define TARGET_NR_sigprocmask (126)
+#define TARGET_NR_create_module (127) /* removed */
+#define TARGET_NR_init_module (128)
+#define TARGET_NR_delete_module (129)
+#define TARGET_NR_get_kernel_syms (130) /* removed */
+#define TARGET_NR_quotactl (131)
+#define TARGET_NR_getpgid (132)
+#define TARGET_NR_fchdir (133)
+#define TARGET_NR_bdflush (134)
+#define TARGET_NR_sysfs (135)
+#define TARGET_NR_personality (136)
+#define TARGET_NR_afs_syscall (137) /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid (138)
+#define TARGET_NR_setfsgid (139)
+#define TARGET_NR__llseek (140)
+#define TARGET_NR_getdents (141)
+#define TARGET_NR__newselect (142)
+#define TARGET_NR_flock (143)
+#define TARGET_NR_msync (144)
+#define TARGET_NR_readv (145)
+#define TARGET_NR_writev (146)
+#define TARGET_NR_getsid (147)
+#define TARGET_NR_fdatasync (148)
+#define TARGET_NR__sysctl (149)
+#define TARGET_NR_mlock (150)
+#define TARGET_NR_munlock (151)
+#define TARGET_NR_mlockall (152)
+#define TARGET_NR_munlockall (153)
+#define TARGET_NR_sched_setparam (154)
+#define TARGET_NR_sched_getparam (155)
+#define TARGET_NR_sched_setscheduler (156)
+#define TARGET_NR_sched_getscheduler (157)
+#define TARGET_NR_sched_yield (158)
+#define TARGET_NR_sched_get_priority_max (159)
+#define TARGET_NR_sched_get_priority_min (160)
+#define TARGET_NR_sched_rr_get_interval (161)
+#define TARGET_NR_nanosleep (162)
+#define TARGET_NR_mremap (163)
+#define TARGET_NR_setresuid (164)
+#define TARGET_NR_getresuid (165)
+#define TARGET_NR_vm86 (166) /* removed */
+#define TARGET_NR_query_module (167) /* removed */
+#define TARGET_NR_poll (168)
+#define TARGET_NR_nfsservctl (169)
+#define TARGET_NR_setresgid (170)
+#define TARGET_NR_getresgid (171)
+#define TARGET_NR_prctl (172)
+#define TARGET_NR_rt_sigreturn (173)
+#define TARGET_NR_rt_sigaction (174)
+#define TARGET_NR_rt_sigprocmask (175)
+#define TARGET_NR_rt_sigpending (176)
+#define TARGET_NR_rt_sigtimedwait (177)
+#define TARGET_NR_rt_sigqueueinfo (178)
+#define TARGET_NR_rt_sigsuspend (179)
+#define TARGET_NR_pread (180)
+#define TARGET_NR_pwrite (181)
+#define TARGET_NR_chown (182)
+#define TARGET_NR_getcwd (183)
+#define TARGET_NR_capget (184)
+#define TARGET_NR_capset (185)
+#define TARGET_NR_sigaltstack (186)
+#define TARGET_NR_sendfile (187)
+ /* 188 reserved */
+ /* 189 reserved */
+#define TARGET_NR_vfork (190)
+#define TARGET_NR_ugetrlimit (191) /* SuS compliant getrlimit */
+#define TARGET_NR_mmap2 (192)
+#define TARGET_NR_truncate64 (193)
+#define TARGET_NR_ftruncate64 (194)
+#define TARGET_NR_stat64 (195)
+#define TARGET_NR_lstat64 (196)
+#define TARGET_NR_fstat64 (197)
+#define TARGET_NR_lchown32 (198)
+#define TARGET_NR_getuid32 (199)
+#define TARGET_NR_getgid32 (200)
+#define TARGET_NR_geteuid32 (201)
+#define TARGET_NR_getegid32 (202)
+#define TARGET_NR_setreuid32 (203)
+#define TARGET_NR_setregid32 (204)
+#define TARGET_NR_getgroups32 (205)
+#define TARGET_NR_setgroups32 (206)
+#define TARGET_NR_fchown32 (207)
+#define TARGET_NR_setresuid32 (208)
+#define TARGET_NR_getresuid32 (209)
+#define TARGET_NR_setresgid32 (210)
+#define TARGET_NR_getresgid32 (211)
+#define TARGET_NR_chown32 (212)
+#define TARGET_NR_setuid32 (213)
+#define TARGET_NR_setgid32 (214)
+#define TARGET_NR_setfsuid32 (215)
+#define TARGET_NR_setfsgid32 (216)
+#define TARGET_NR_getdents64 (217)
+#define TARGET_NR_pivot_root (218)
+#define TARGET_NR_mincore (219)
+#define TARGET_NR_madvise (220)
+#define TARGET_NR_fcntl64 (221)
+ /* 222 for tux */
+ /* 223 is unused */
+#define TARGET_NR_gettid (224)
+#define TARGET_NR_readahead (225)
+#define TARGET_NR_setxattr (226)
+#define TARGET_NR_lsetxattr (227)
+#define TARGET_NR_fsetxattr (228)
+#define TARGET_NR_getxattr (229)
+#define TARGET_NR_lgetxattr (230)
+#define TARGET_NR_fgetxattr (231)
+#define TARGET_NR_listxattr (232)
+#define TARGET_NR_llistxattr (233)
+#define TARGET_NR_flistxattr (234)
+#define TARGET_NR_removexattr (235)
+#define TARGET_NR_lremovexattr (236)
+#define TARGET_NR_fremovexattr (237)
+#define TARGET_NR_tkill (238)
+#define TARGET_NR_sendfile64 (239)
+#define TARGET_NR_futex (240)
+#define TARGET_NR_sched_setaffinity (241)
+#define TARGET_NR_sched_getaffinity (242)
+#define TARGET_NR_io_setup (243)
+#define TARGET_NR_io_destroy (244)
+#define TARGET_NR_io_getevents (245)
+#define TARGET_NR_io_submit (246)
+#define TARGET_NR_io_cancel (247)
+#define TARGET_NR_exit_group (248)
+#define TARGET_NR_lookup_dcookie (249)
+#define TARGET_NR_epoll_create (250)
+#define TARGET_NR_epoll_ctl (251)
+#define TARGET_NR_epoll_wait (252)
+#define TARGET_NR_remap_file_pages (253)
+ /* 254 for set_thread_area */
+ /* 255 for get_thread_area */
+ /* 256 for set_tid_address */
+#define TARGET_NR_utimes (269)
diff --git a/linux-user/arm/termbits.h b/linux-user/arm/termbits.h
new file mode 100644
index 0000000..36ead08
--- /dev/null
+++ b/linux-user/arm/termbits.h
@@ -0,0 +1,215 @@
+/* from asm/termbits.h */
+/* NOTE: exactly the same as i386 */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* ioctls */
+
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+#define TARGET_TCGETA 0x5405
+#define TARGET_TCSETA 0x5406
+#define TARGET_TCSETAW 0x5407
+#define TARGET_TCSETAF 0x5408
+#define TARGET_TCSBRK 0x5409
+#define TARGET_TCXONC 0x540A
+#define TARGET_TCFLSH 0x540B
+
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+#define TARGET_TIOCGPGRP 0x540F
+#define TARGET_TIOCSPGRP 0x5410
+#define TARGET_TIOCOUTQ 0x5411
+#define TARGET_TIOCSTI 0x5412
+#define TARGET_TIOCGWINSZ 0x5413
+#define TARGET_TIOCSWINSZ 0x5414
+#define TARGET_TIOCMGET 0x5415
+#define TARGET_TIOCMBIS 0x5416
+#define TARGET_TIOCMBIC 0x5417
+#define TARGET_TIOCMSET 0x5418
+#define TARGET_TIOCGSOFTCAR 0x5419
+#define TARGET_TIOCSSOFTCAR 0x541A
+#define TARGET_FIONREAD 0x541B
+#define TARGET_TIOCINQ TARGET_FIONREAD
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+#define TARGET_FIONBIO 0x5421
+#define TARGET_TIOCNOTTY 0x5422
+#define TARGET_TIOCSETD 0x5423
+#define TARGET_TIOCGETD 0x5424
+#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */
+#define TARGET_FIOCLEX 0x5451
+#define TARGET_FIOASYNC 0x5452
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* Used for packet mode */
+#define TARGET_TIOCPKT_DATA 0
+#define TARGET_TIOCPKT_FLUSHREAD 1
+#define TARGET_TIOCPKT_FLUSHWRITE 2
+#define TARGET_TIOCPKT_STOP 4
+#define TARGET_TIOCPKT_START 8
+#define TARGET_TIOCPKT_NOSTOP 16
+#define TARGET_TIOCPKT_DOSTOP 32
+
+#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
new file mode 100644
index 0000000..57b5ed2
--- /dev/null
+++ b/linux-user/elfload.c
@@ -0,0 +1,1240 @@
+/* This is the Linux kernel elf-loading code, ported into user space */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu.h"
+#include "disas.h"
+
+/* this flag is uneffective under linux too, should be deleted */
+#ifndef MAP_DENYWRITE
+#define MAP_DENYWRITE 0
+#endif
+
+/* should probably go in elf.h */
+#ifndef ELIBBAD
+#define ELIBBAD 80
+#endif
+
+#ifdef TARGET_I386
+
+#define ELF_PLATFORM get_elf_platform()
+
+static const char *get_elf_platform(void)
+{
+ static char elf_platform[] = "i386";
+ int family = (global_env->cpuid_version >> 8) & 0xff;
+ if (family > 6)
+ family = 6;
+ if (family >= 3)
+ elf_platform[1] = '0' + family;
+ return elf_platform;
+}
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ return global_env->cpuid_features;
+}
+
+#define ELF_START_MMAP 0x80000000
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) )
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_386
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->esp = infop->start_stack;
+ regs->eip = infop->entry;
+
+ /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
+ starts %edx contains a pointer to a function which might be
+ registered using `atexit'. This provides a mean for the
+ dynamic linker to call DT_FINI functions for shared libraries
+ that have been loaded before the code runs.
+
+ A value of 0 tells we have no such handler. */
+ regs->edx = 0;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_ARM
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_ARM )
+
+#define ELF_CLASS ELFCLASS32
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_ARM
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ target_long stack = infop->start_stack;
+ memset(regs, 0, sizeof(*regs));
+ regs->ARM_cpsr = 0x10;
+ if (infop->entry & 1)
+ regs->ARM_cpsr |= CPSR_T;
+ regs->ARM_pc = infop->entry & 0xfffffffe;
+ regs->ARM_sp = infop->start_stack;
+ regs->ARM_r2 = tgetl(stack + 8); /* envp */
+ regs->ARM_r1 = tgetl(stack + 4); /* envp */
+ /* XXX: it seems that r0 is zeroed after ! */
+ regs->ARM_r0 = 0;
+ /* For uClinux PIC binaries. */
+ regs->ARM_r10 = infop->start_data;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+enum
+{
+ ARM_HWCAP_ARM_SWP = 1 << 0,
+ ARM_HWCAP_ARM_HALF = 1 << 1,
+ ARM_HWCAP_ARM_THUMB = 1 << 2,
+ ARM_HWCAP_ARM_26BIT = 1 << 3,
+ ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
+ ARM_HWCAP_ARM_FPA = 1 << 5,
+ ARM_HWCAP_ARM_VFP = 1 << 6,
+ ARM_HWCAP_ARM_EDSP = 1 << 7,
+};
+
+#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
+ | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
+ | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP)
+
+#endif
+
+#ifdef TARGET_SPARC
+#ifdef TARGET_SPARC64
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SPARCV9 )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_SPARCV9
+
+#define STACK_BIAS 2047
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->tstate = 0;
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+ regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS;
+}
+
+#else
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SPARC )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_SPARC
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->psr = 0;
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+}
+
+#endif
+#endif
+
+#ifdef TARGET_PPC
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_PPC )
+
+#define ELF_CLASS ELFCLASS32
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_PPC
+
+/*
+ * We need to put in some extra aux table entries to tell glibc what
+ * the cache block size is, so it can use the dcbz instruction safely.
+ */
+#define AT_DCACHEBSIZE 19
+#define AT_ICACHEBSIZE 20
+#define AT_UCACHEBSIZE 21
+/* A special ignored type value for PPC, for glibc compatibility. */
+#define AT_IGNOREPPC 22
+/*
+ * The requirements here are:
+ * - keep the final alignment of sp (sp & 0xf)
+ * - make sure the 32-bit value at the first 16 byte aligned position of
+ * AUXV is greater than 16 for glibc compatibility.
+ * AT_IGNOREPPC is used for that.
+ * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC,
+ * even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
+ */
+#define DLINFO_ARCH_ITEMS 5
+#define ARCH_DLINFO \
+do { \
+ NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
+ /* \
+ * Now handle glibc compatibility. \
+ */ \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ } while (0)
+
+static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
+{
+ target_ulong pos = infop->start_stack;
+ target_ulong tmp;
+
+ _regs->msr = 1 << MSR_PR; /* Set user mode */
+ _regs->gpr[1] = infop->start_stack;
+ _regs->nip = infop->entry;
+ /* Note that isn't exactly what regular kernel does
+ * but this is what the ABI wants and is needed to allow
+ * execution of PPC BSD programs.
+ */
+ _regs->gpr[3] = tgetl(pos);
+ pos += sizeof(target_ulong);
+ _regs->gpr[4] = pos;
+ for (tmp = 1; tmp != 0; pos += sizeof(target_ulong))
+ tmp = ldl(pos);
+ _regs->gpr[5] = pos;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_MIPS
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_MIPS )
+
+#define ELF_CLASS ELFCLASS32
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_MIPS
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->cp0_status = CP0St_UM;
+ regs->cp0_epc = infop->entry;
+ regs->regs[29] = infop->start_stack;
+}
+
+#endif /* TARGET_MIPS */
+
+#ifdef TARGET_SH4
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SH )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_SH
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ /* Check other registers XXXXX */
+ regs->pc = infop->entry;
+ regs->regs[15] = infop->start_stack - 16 * 4;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifndef ELF_PLATFORM
+#define ELF_PLATFORM (NULL)
+#endif
+
+#ifndef ELF_HWCAP
+#define ELF_HWCAP 0
+#endif
+
+#include "elf.h"
+
+struct exec
+{
+ unsigned int a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned int a_text; /* length of text, in bytes */
+ unsigned int a_data; /* length of data, in bytes */
+ unsigned int a_bss; /* length of uninitialized data area, in bytes */
+ unsigned int a_syms; /* length of symbol table data in file, in bytes */
+ unsigned int a_entry; /* start address */
+ unsigned int a_trsize; /* length of relocation info for text, in bytes */
+ unsigned int a_drsize; /* length of relocation info for data, in bytes */
+};
+
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+
+/* max code+data+bss space allocated to elf interpreter */
+#define INTERP_MAP_SIZE (32 * 1024 * 1024)
+
+/* max code+data+bss+brk space allocated to ET_DYN executables */
+#define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
+
+/* from personality.h */
+
+/* Flags for bug emulation. These occupy the top three bytes. */
+#define STICKY_TIMEOUTS 0x4000000
+#define WHOLE_SECONDS 0x2000000
+
+/* Personality types. These go in the low byte. Avoid using the top bit,
+ * it will conflict with error returns.
+ */
+#define PER_MASK (0x00ff)
+#define PER_LINUX (0x0000)
+#define PER_SVR4 (0x0001 | STICKY_TIMEOUTS)
+#define PER_SVR3 (0x0002 | STICKY_TIMEOUTS)
+#define PER_SCOSVR3 (0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS)
+#define PER_WYSEV386 (0x0004 | STICKY_TIMEOUTS)
+#define PER_ISCR4 (0x0005 | STICKY_TIMEOUTS)
+#define PER_BSD (0x0006)
+#define PER_XENIX (0x0007 | STICKY_TIMEOUTS)
+
+/* Necessary parameters */
+#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
+#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
+#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
+
+#define INTERPRETER_NONE 0
+#define INTERPRETER_AOUT 1
+#define INTERPRETER_ELF 2
+
+#define DLINFO_ITEMS 12
+
+static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
+{
+ memcpy(to, from, n);
+}
+
+extern unsigned long x86_stack_size;
+
+static int load_aout_interp(void * exptr, int interp_fd);
+
+#ifdef BSWAP_NEEDED
+static void bswap_ehdr(struct elfhdr *ehdr)
+{
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswaptls(&ehdr->e_entry); /* Entry point virtual address */
+ bswaptls(&ehdr->e_phoff); /* Program header table file offset */
+ bswaptls(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void bswap_phdr(struct elf_phdr *phdr)
+{
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswaptls(&phdr->p_offset); /* Segment file offset */
+ bswaptls(&phdr->p_vaddr); /* Segment virtual address */
+ bswaptls(&phdr->p_paddr); /* Segment physical address */
+ bswaptls(&phdr->p_filesz); /* Segment size in file */
+ bswaptls(&phdr->p_memsz); /* Segment size in memory */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswaptls(&phdr->p_align); /* Segment alignment */
+}
+
+static void bswap_shdr(struct elf_shdr *shdr)
+{
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswaptls(&shdr->sh_flags);
+ bswaptls(&shdr->sh_addr);
+ bswaptls(&shdr->sh_offset);
+ bswaptls(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswaptls(&shdr->sh_addralign);
+ bswaptls(&shdr->sh_entsize);
+}
+
+static void bswap_sym(Elf32_Sym *sym)
+{
+ bswap32s(&sym->st_name);
+ bswap32s(&sym->st_value);
+ bswap32s(&sym->st_size);
+ bswap16s(&sym->st_shndx);
+}
+#endif
+
+/*
+ * 'copy_elf_strings()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ *
+ */
+static unsigned long copy_elf_strings(int argc,char ** argv, void **page,
+ unsigned long p)
+{
+ char *tmp, *tmp1, *pag = NULL;
+ int len, offset = 0;
+
+ if (!p) {
+ return 0; /* bullet-proofing */
+ }
+ while (argc-- > 0) {
+ tmp = argv[argc];
+ if (!tmp) {
+ fprintf(stderr, "VFS: argc is wrong");
+ exit(-1);
+ }
+ tmp1 = tmp;
+ while (*tmp++);
+ len = tmp - tmp1;
+ if (p < len) { /* this shouldn't happen - 128kB */
+ return 0;
+ }
+ while (len) {
+ --p; --tmp; --len;
+ if (--offset < 0) {
+ offset = p % TARGET_PAGE_SIZE;
+ pag = (char *)page[p/TARGET_PAGE_SIZE];
+ if (!pag) {
+ pag = (char *)malloc(TARGET_PAGE_SIZE);
+ page[p/TARGET_PAGE_SIZE] = pag;
+ if (!pag)
+ return 0;
+ }
+ }
+ if (len == 0 || offset == 0) {
+ *(pag + offset) = *tmp;
+ }
+ else {
+ int bytes_to_copy = (len > offset) ? offset : len;
+ tmp -= bytes_to_copy;
+ p -= bytes_to_copy;
+ offset -= bytes_to_copy;
+ len -= bytes_to_copy;
+ memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
+ }
+ }
+ }
+ return p;
+}
+
+unsigned long setup_arg_pages(target_ulong p, struct linux_binprm * bprm,
+ struct image_info * info)
+{
+ target_ulong stack_base, size, error;
+ int i;
+
+ /* Create enough stack to hold everything. If we don't use
+ * it for args, we'll use it for something else...
+ */
+ size = x86_stack_size;
+ if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE)
+ size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ error = target_mmap(0,
+ size + qemu_host_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (error == -1) {
+ perror("stk mmap");
+ exit(-1);
+ }
+ /* we reserve one extra page at the top of the stack as guard */
+ target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
+
+ stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ p += stack_base;
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ if (bprm->page[i]) {
+ info->rss++;
+
+ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
+ free(bprm->page[i]);
+ }
+ stack_base += TARGET_PAGE_SIZE;
+ }
+ return p;
+}
+
+static void set_brk(unsigned long start, unsigned long end)
+{
+ /* page-align the start and end addresses... */
+ start = HOST_PAGE_ALIGN(start);
+ end = HOST_PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ if(target_mmap(start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) {
+ perror("cannot mmap brk");
+ exit(-1);
+ }
+}
+
+
+/* We need to explicitly zero any fractional pages after the data
+ section (i.e. bss). This would contain the junk from the file that
+ should not be in memory. */
+static void padzero(unsigned long elf_bss)
+{
+ unsigned long nbyte;
+
+ /* XXX: this is really a hack : if the real host page size is
+ smaller than the target page size, some pages after the end
+ of the file may not be mapped. A better fix would be to
+ patch target_mmap(), but it is more complicated as the file
+ size must be known */
+ if (qemu_real_host_page_size < qemu_host_page_size) {
+ unsigned long end_addr, end_addr1;
+ end_addr1 = (elf_bss + qemu_real_host_page_size - 1) &
+ ~(qemu_real_host_page_size - 1);
+ end_addr = HOST_PAGE_ALIGN(elf_bss);
+ if (end_addr1 < end_addr) {
+ mmap((void *)end_addr1, end_addr - end_addr1,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ }
+ }
+
+ nbyte = elf_bss & (qemu_host_page_size-1);
+ if (nbyte) {
+ nbyte = qemu_host_page_size - nbyte;
+ do {
+ tput8(elf_bss, 0);
+ elf_bss++;
+ } while (--nbyte);
+ }
+}
+
+
+static unsigned long create_elf_tables(target_ulong p, int argc, int envc,
+ struct elfhdr * exec,
+ unsigned long load_addr,
+ unsigned long load_bias,
+ unsigned long interp_load_addr, int ibcs,
+ struct image_info *info)
+{
+ target_ulong sp;
+ int size;
+ target_ulong u_platform;
+ const char *k_platform;
+ const int n = sizeof(target_ulong);
+
+ sp = p;
+ u_platform = 0;
+ k_platform = ELF_PLATFORM;
+ if (k_platform) {
+ size_t len = strlen(k_platform) + 1;
+ sp -= (len + n - 1) & ~(n - 1);
+ u_platform = sp;
+ memcpy_to_target(sp, k_platform, len);
+ }
+ /*
+ * Force 16 byte _final_ alignment here for generality.
+ */
+ sp = sp &~ (target_ulong)15;
+ size = (DLINFO_ITEMS + 1) * 2;
+ if (k_platform)
+ size += 2;
+#ifdef DLINFO_ARCH_ITEMS
+ size += DLINFO_ARCH_ITEMS * 2;
+#endif
+ size += envc + argc + 2;
+ size += (!ibcs ? 3 : 1); /* argc itself */
+ size *= n;
+ if (size & 15)
+ sp -= 16 - (size & 15);
+
+#define NEW_AUX_ENT(id, val) do { \
+ sp -= n; tputl(sp, val); \
+ sp -= n; tputl(sp, id); \
+ } while(0)
+ NEW_AUX_ENT (AT_NULL, 0);
+
+ /* There must be exactly DLINFO_ITEMS entries here. */
+ NEW_AUX_ENT(AT_PHDR, (target_ulong)(load_addr + exec->e_phoff));
+ NEW_AUX_ENT(AT_PHENT, (target_ulong)(sizeof (struct elf_phdr)));
+ NEW_AUX_ENT(AT_PHNUM, (target_ulong)(exec->e_phnum));
+ NEW_AUX_ENT(AT_PAGESZ, (target_ulong)(TARGET_PAGE_SIZE));
+ NEW_AUX_ENT(AT_BASE, (target_ulong)(interp_load_addr));
+ NEW_AUX_ENT(AT_FLAGS, (target_ulong)0);
+ NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry);
+ NEW_AUX_ENT(AT_UID, (target_ulong) getuid());
+ NEW_AUX_ENT(AT_EUID, (target_ulong) geteuid());
+ NEW_AUX_ENT(AT_GID, (target_ulong) getgid());
+ NEW_AUX_ENT(AT_EGID, (target_ulong) getegid());
+ NEW_AUX_ENT(AT_HWCAP, (target_ulong) ELF_HWCAP);
+ if (k_platform)
+ NEW_AUX_ENT(AT_PLATFORM, u_platform);
+#ifdef ARCH_DLINFO
+ /*
+ * ARCH_DLINFO must come last so platform specific code can enforce
+ * special alignment requirements on the AUXV if necessary (eg. PPC).
+ */
+ ARCH_DLINFO;
+#endif
+#undef NEW_AUX_ENT
+
+ sp = loader_build_argptr(envc, argc, sp, p, !ibcs);
+ return sp;
+}
+
+
+static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
+ int interpreter_fd,
+ unsigned long *interp_load_addr)
+{
+ struct elf_phdr *elf_phdata = NULL;
+ struct elf_phdr *eppnt;
+ unsigned long load_addr = 0;
+ int load_addr_set = 0;
+ int retval;
+ unsigned long last_bss, elf_bss;
+ unsigned long error;
+ int i;
+
+ elf_bss = 0;
+ last_bss = 0;
+ error = 0;
+
+#ifdef BSWAP_NEEDED
+ bswap_ehdr(interp_elf_ex);
+#endif
+ /* First of all, some simple consistency checks */
+ if ((interp_elf_ex->e_type != ET_EXEC &&
+ interp_elf_ex->e_type != ET_DYN) ||
+ !elf_check_arch(interp_elf_ex->e_machine)) {
+ return ~0UL;
+ }
+
+
+ /* Now read in all of the header information */
+
+ if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
+ return ~0UL;
+
+ elf_phdata = (struct elf_phdr *)
+ malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+
+ if (!elf_phdata)
+ return ~0UL;
+
+ /*
+ * If the size of this structure has changed, then punt, since
+ * we will be doing the wrong thing.
+ */
+ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) {
+ free(elf_phdata);
+ return ~0UL;
+ }
+
+ retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(interpreter_fd,
+ (char *) elf_phdata,
+ sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+ }
+ if (retval < 0) {
+ perror("load_elf_interp");
+ exit(-1);
+ free (elf_phdata);
+ return retval;
+ }
+#ifdef BSWAP_NEEDED
+ eppnt = elf_phdata;
+ for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
+ bswap_phdr(eppnt);
+ }
+#endif
+
+ if (interp_elf_ex->e_type == ET_DYN) {
+ /* in order to avoid harcoding the interpreter load
+ address in qemu, we allocate a big enough memory zone */
+ error = target_mmap(0, INTERP_MAP_SIZE,
+ PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+ load_addr = error;
+ load_addr_set = 1;
+ }
+
+ eppnt = elf_phdata;
+ for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
+ if (eppnt->p_type == PT_LOAD) {
+ int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
+ int elf_prot = 0;
+ unsigned long vaddr = 0;
+ unsigned long k;
+
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
+ if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
+ elf_type |= MAP_FIXED;
+ vaddr = eppnt->p_vaddr;
+ }
+ error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
+ eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
+ elf_prot,
+ elf_type,
+ interpreter_fd,
+ eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
+
+ if (error == -1) {
+ /* Real error */
+ close(interpreter_fd);
+ free(elf_phdata);
+ return ~0UL;
+ }
+
+ if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
+ load_addr = error;
+ load_addr_set = 1;
+ }
+
+ /*
+ * Find the end of the file mapping for this phdr, and keep
+ * track of the largest address we see for this.
+ */
+ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
+ if (k > elf_bss) elf_bss = k;
+
+ /*
+ * Do the same thing for the memory mapping - between
+ * elf_bss and last_bss is the bss section.
+ */
+ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
+ if (k > last_bss) last_bss = k;
+ }
+
+ /* Now use mmap to map the library into memory. */
+
+ close(interpreter_fd);
+
+ /*
+ * Now fill out the bss section. First pad the last page up
+ * to the page boundary, and then perform a mmap to make sure
+ * that there are zeromapped pages up to and including the last
+ * bss page.
+ */
+ padzero(elf_bss);
+ elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */
+
+ /* Map the last of the bss segment */
+ if (last_bss > elf_bss) {
+ target_mmap(elf_bss, last_bss-elf_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ }
+ free(elf_phdata);
+
+ *interp_load_addr = load_addr;
+ return ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+}
+
+/* Best attempt to load symbols from this ELF object. */
+static void load_symbols(struct elfhdr *hdr, int fd)
+{
+ unsigned int i;
+ struct elf_shdr sechdr, symtab, strtab;
+ char *strings;
+ struct syminfo *s;
+
+ lseek(fd, hdr->e_shoff, SEEK_SET);
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
+ return;
+#ifdef BSWAP_NEEDED
+ bswap_shdr(&sechdr);
+#endif
+ if (sechdr.sh_type == SHT_SYMTAB) {
+ symtab = sechdr;
+ lseek(fd, hdr->e_shoff
+ + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
+ if (read(fd, &strtab, sizeof(strtab))
+ != sizeof(strtab))
+ return;
+#ifdef BSWAP_NEEDED
+ bswap_shdr(&strtab);
+#endif
+ goto found;
+ }
+ }
+ return; /* Shouldn't happen... */
+
+ found:
+ /* Now know where the strtab and symtab are. Snarf them. */
+ s = malloc(sizeof(*s));
+ s->disas_symtab = malloc(symtab.sh_size);
+ s->disas_strtab = strings = malloc(strtab.sh_size);
+ if (!s->disas_symtab || !s->disas_strtab)
+ return;
+
+ lseek(fd, symtab.sh_offset, SEEK_SET);
+ if (read(fd, s->disas_symtab, symtab.sh_size) != symtab.sh_size)
+ return;
+
+#ifdef BSWAP_NEEDED
+ for (i = 0; i < symtab.sh_size / sizeof(struct elf_sym); i++)
+ bswap_sym(s->disas_symtab + sizeof(struct elf_sym)*i);
+#endif
+
+ lseek(fd, strtab.sh_offset, SEEK_SET);
+ if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
+ return;
+ s->disas_num_syms = symtab.sh_size / sizeof(struct elf_sym);
+ s->next = syminfos;
+ syminfos = s;
+}
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info)
+{
+ struct elfhdr elf_ex;
+ struct elfhdr interp_elf_ex;
+ struct exec interp_ex;
+ int interpreter_fd = -1; /* avoid warning */
+ unsigned long load_addr, load_bias;
+ int load_addr_set = 0;
+ unsigned int interpreter_type = INTERPRETER_NONE;
+ unsigned char ibcs2_interpreter;
+ int i;
+ unsigned long mapped_addr;
+ struct elf_phdr * elf_ppnt;
+ struct elf_phdr *elf_phdata;
+ unsigned long elf_bss, k, elf_brk;
+ int retval;
+ char * elf_interpreter;
+ unsigned long elf_entry, interp_load_addr = 0;
+ int status;
+ unsigned long start_code, end_code, end_data;
+ unsigned long elf_stack;
+ char passed_fileno[6];
+
+ ibcs2_interpreter = 0;
+ status = 0;
+ load_addr = 0;
+ load_bias = 0;
+ elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
+#ifdef BSWAP_NEEDED
+ bswap_ehdr(&elf_ex);
+#endif
+
+ /* First of all, some simple consistency checks */
+ if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
+ (! elf_check_arch(elf_ex.e_machine))) {
+ return -ENOEXEC;
+ }
+
+ bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
+ bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
+ bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
+ if (!bprm->p) {
+ retval = -E2BIG;
+ }
+
+ /* Now read in all of the header information */
+ elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);
+ if (elf_phdata == NULL) {
+ return -ENOMEM;
+ }
+
+ retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET);
+ if(retval > 0) {
+ retval = read(bprm->fd, (char *) elf_phdata,
+ elf_ex.e_phentsize * elf_ex.e_phnum);
+ }
+
+ if (retval < 0) {
+ perror("load_elf_binary");
+ exit(-1);
+ free (elf_phdata);
+ return -errno;
+ }
+
+#ifdef BSWAP_NEEDED
+ elf_ppnt = elf_phdata;
+ for (i=0; i<elf_ex.e_phnum; i++, elf_ppnt++) {
+ bswap_phdr(elf_ppnt);
+ }
+#endif
+ elf_ppnt = elf_phdata;
+
+ elf_bss = 0;
+ elf_brk = 0;
+
+
+ elf_stack = ~0UL;
+ elf_interpreter = NULL;
+ start_code = ~0UL;
+ end_code = 0;
+ end_data = 0;
+
+ for(i=0;i < elf_ex.e_phnum; i++) {
+ if (elf_ppnt->p_type == PT_INTERP) {
+ if ( elf_interpreter != NULL )
+ {
+ free (elf_phdata);
+ free(elf_interpreter);
+ close(bprm->fd);
+ return -EINVAL;
+ }
+
+ /* This is the program interpreter used for
+ * shared libraries - for now assume that this
+ * is an a.out format binary
+ */
+
+ elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
+
+ if (elf_interpreter == NULL) {
+ free (elf_phdata);
+ close(bprm->fd);
+ return -ENOMEM;
+ }
+
+ retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz);
+ }
+ if(retval < 0) {
+ perror("load_elf_binary2");
+ exit(-1);
+ }
+
+ /* If the program interpreter is one of these two,
+ then assume an iBCS2 image. Otherwise assume
+ a native linux image. */
+
+ /* JRP - Need to add X86 lib dir stuff here... */
+
+ if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
+ strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {
+ ibcs2_interpreter = 1;
+ }
+
+#if 0
+ printf("Using ELF interpreter %s\n", elf_interpreter);
+#endif
+ if (retval >= 0) {
+ retval = open(path(elf_interpreter), O_RDONLY);
+ if(retval >= 0) {
+ interpreter_fd = retval;
+ }
+ else {
+ perror(elf_interpreter);
+ exit(-1);
+ /* retval = -errno; */
+ }
+ }
+
+ if (retval >= 0) {
+ retval = lseek(interpreter_fd, 0, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(interpreter_fd,bprm->buf,128);
+ }
+ }
+ if (retval >= 0) {
+ interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */
+ interp_elf_ex=*((struct elfhdr *) bprm->buf); /* elf exec-header */
+ }
+ if (retval < 0) {
+ perror("load_elf_binary3");
+ exit(-1);
+ free (elf_phdata);
+ free(elf_interpreter);
+ close(bprm->fd);
+ return retval;
+ }
+ }
+ elf_ppnt++;
+ }
+
+ /* Some simple consistency checks for the interpreter */
+ if (elf_interpreter){
+ interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
+
+ /* Now figure out which format our binary is */
+ if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) &&
+ (N_MAGIC(interp_ex) != QMAGIC)) {
+ interpreter_type = INTERPRETER_ELF;
+ }
+
+ if (interp_elf_ex.e_ident[0] != 0x7f ||
+ strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
+ interpreter_type &= ~INTERPRETER_ELF;
+ }
+
+ if (!interpreter_type) {
+ free(elf_interpreter);
+ free(elf_phdata);
+ close(bprm->fd);
+ return -ELIBBAD;
+ }
+ }
+
+ /* OK, we are done with that, now set up the arg stuff,
+ and then start this sucker up */
+
+ {
+ char * passed_p;
+
+ if (interpreter_type == INTERPRETER_AOUT) {
+ snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd);
+ passed_p = passed_fileno;
+
+ if (elf_interpreter) {
+ bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);
+ bprm->argc++;
+ }
+ }
+ if (!bprm->p) {
+ if (elf_interpreter) {
+ free(elf_interpreter);
+ }
+ free (elf_phdata);
+ close(bprm->fd);
+ return -E2BIG;
+ }
+ }
+
+ /* OK, This is the point of no return */
+ info->end_data = 0;
+ info->end_code = 0;
+ info->start_mmap = (unsigned long)ELF_START_MMAP;
+ info->mmap = 0;
+ elf_entry = (unsigned long) elf_ex.e_entry;
+
+ /* Do this so that we can load the interpreter, if need be. We will
+ change some of these later */
+ info->rss = 0;
+ bprm->p = setup_arg_pages(bprm->p, bprm, info);
+ info->start_stack = bprm->p;
+
+ /* Now we do a little grungy work by mmaping the ELF image into
+ * the correct location in memory. At this point, we assume that
+ * the image should be loaded at fixed address, not at a variable
+ * address.
+ */
+
+ for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ int elf_prot = 0;
+ int elf_flags = 0;
+ unsigned long error;
+
+ if (elf_ppnt->p_type != PT_LOAD)
+ continue;
+
+ if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
+ if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
+ if (elf_ex.e_type == ET_EXEC || load_addr_set) {
+ elf_flags |= MAP_FIXED;
+ } else if (elf_ex.e_type == ET_DYN) {
+ /* Try and get dynamic programs out of the way of the default mmap
+ base, as well as whatever program they might try to exec. This
+ is because the brk will follow the loader, and is not movable. */
+ /* NOTE: for qemu, we do a big mmap to get enough space
+ without harcoding any address */
+ error = target_mmap(0, ET_DYN_MAP_SIZE,
+ PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+ load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
+ }
+
+ error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
+ (elf_ppnt->p_filesz +
+ TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
+ elf_prot,
+ (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
+ bprm->fd,
+ (elf_ppnt->p_offset -
+ TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+
+#ifdef LOW_ELF_STACK
+ if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
+ elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);
+#endif
+
+ if (!load_addr_set) {
+ load_addr_set = 1;
+ load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
+ if (elf_ex.e_type == ET_DYN) {
+ load_bias += error -
+ TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
+ load_addr += load_bias;
+ }
+ }
+ k = elf_ppnt->p_vaddr;
+ if (k < start_code)
+ start_code = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
+ if (k > elf_bss)
+ elf_bss = k;
+ if ((elf_ppnt->p_flags & PF_X) && end_code < k)
+ end_code = k;
+ if (end_data < k)
+ end_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
+ if (k > elf_brk) elf_brk = k;
+ }
+
+ elf_entry += load_bias;
+ elf_bss += load_bias;
+ elf_brk += load_bias;
+ start_code += load_bias;
+ end_code += load_bias;
+ // start_data += load_bias;
+ end_data += load_bias;
+
+ if (elf_interpreter) {
+ if (interpreter_type & 1) {
+ elf_entry = load_aout_interp(&interp_ex, interpreter_fd);
+ }
+ else if (interpreter_type & 2) {
+ elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
+ &interp_load_addr);
+ }
+
+ close(interpreter_fd);
+ free(elf_interpreter);
+
+ if (elf_entry == ~0UL) {
+ printf("Unable to load interpreter\n");
+ free(elf_phdata);
+ exit(-1);
+ return 0;
+ }
+ }
+
+ free(elf_phdata);
+
+ if (loglevel)
+ load_symbols(&elf_ex, bprm->fd);
+
+ if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
+ info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
+
+#ifdef LOW_ELF_STACK
+ info->start_stack = bprm->p = elf_stack - 4;
+#endif
+ bprm->p = create_elf_tables(bprm->p,
+ bprm->argc,
+ bprm->envc,
+ &elf_ex,
+ load_addr, load_bias,
+ interp_load_addr,
+ (interpreter_type == INTERPRETER_AOUT ? 0 : 1),
+ info);
+ info->start_brk = info->brk = elf_brk;
+ info->end_code = end_code;
+ info->start_code = start_code;
+ info->start_data = end_code;
+ info->end_data = end_data;
+ info->start_stack = bprm->p;
+
+ /* Calling set_brk effectively mmaps the pages that we need for the bss and break
+ sections */
+ set_brk(elf_bss, elf_brk);
+
+ padzero(elf_bss);
+
+#if 0
+ printf("(start_brk) %x\n" , info->start_brk);
+ printf("(end_code) %x\n" , info->end_code);
+ printf("(start_code) %x\n" , info->start_code);
+ printf("(end_data) %x\n" , info->end_data);
+ printf("(start_stack) %x\n" , info->start_stack);
+ printf("(brk) %x\n" , info->brk);
+#endif
+
+ if ( info->personality == PER_SVR4 )
+ {
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior.
+ Since we do not have the power to recompile these, we
+ emulate the SVr4 behavior. Sigh. */
+ mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, -1, 0);
+ }
+
+ info->entry = elf_entry;
+
+ return 0;
+}
+
+static int load_aout_interp(void * exptr, int interp_fd)
+{
+ printf("a.out interpreter not yet supported\n");
+ return(0);
+}
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ init_thread(regs, infop);
+}
diff --git a/linux-user/flat.h b/linux-user/flat.h
new file mode 100644
index 0000000..b057b14
--- /dev/null
+++ b/linux-user/flat.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com>
+ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
+ * The Silver Hammer Group, Ltd.
+ *
+ * This file provides the definitions and structures needed to
+ * support uClinux flat-format executables.
+ */
+
+#define FLAT_VERSION 0x00000004L
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#define MAX_SHARED_LIBS (4)
+#else
+#define MAX_SHARED_LIBS (1)
+#endif
+
+/*
+ * To make everything easier to port and manage cross platform
+ * development, all fields are in network byte order.
+ */
+
+struct flat_hdr {
+ char magic[4];
+ unsigned long rev; /* version (as above) */
+ unsigned long entry; /* Offset of first executable instruction
+ with text segment from beginning of file */
+ unsigned long data_start; /* Offset of data segment from beginning of
+ file */
+ unsigned long data_end; /* Offset of end of data segment
+ from beginning of file */
+ unsigned long bss_end; /* Offset of end of bss segment from beginning
+ of file */
+
+ /* (It is assumed that data_end through bss_end forms the bss segment.) */
+
+ unsigned long stack_size; /* Size of stack, in bytes */
+ unsigned long reloc_start; /* Offset of relocation records from
+ beginning of file */
+ unsigned long reloc_count; /* Number of relocation records */
+ unsigned long flags;
+ unsigned long build_date; /* When the program/library was built */
+ unsigned long filler[5]; /* Reservered, set to zero */
+};
+
+#define FLAT_FLAG_RAM 0x0001 /* load program entirely into RAM */
+#define FLAT_FLAG_GOTPIC 0x0002 /* program is PIC with GOT */
+#define FLAT_FLAG_GZIP 0x0004 /* all but the header is compressed */
+#define FLAT_FLAG_GZDATA 0x0008 /* only data/relocs are compressed (for XIP) */
+#define FLAT_FLAG_KTRACE 0x0010 /* output useful kernel trace for debugging */
+
+
+/*
+ * While it would be nice to keep this header clean, users of older
+ * tools still need this support in the kernel. So this section is
+ * purely for compatibility with old tool chains.
+ *
+ * DO NOT make changes or enhancements to the old format please, just work
+ * with the format above, except to fix bugs with old format support.
+ */
+
+#define OLD_FLAT_VERSION 0x00000002L
+#define OLD_FLAT_RELOC_TYPE_TEXT 0
+#define OLD_FLAT_RELOC_TYPE_DATA 1
+#define OLD_FLAT_RELOC_TYPE_BSS 2
+
+# define OLD_FLAT_FLAG_RAM 0x1 /* load program entirely into RAM */
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
new file mode 100644
index 0000000..bf55be2
--- /dev/null
+++ b/linux-user/flatload.c
@@ -0,0 +1,793 @@
+/****************************************************************************/
+/*
+ * QEMU bFLT binary loader. Based on linux/fs/binfmt_flat.c
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Copyright (C) 2006 CodeSourcery.
+ * Copyright (C) 2000-2003 David McCullough <davidm@snapgear.com>
+ * Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com>
+ * Copyright (C) 2002 SnapGear, by Paul Dale <pauli@snapgear.com>
+ * Copyright (C) 2000, 2001 Lineo, by David McCullough <davidm@lineo.com>
+ * based heavily on:
+ *
+ * linux/fs/binfmt_aout.c:
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ * linux/fs/binfmt_flat.c for 2.0 kernel
+ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
+ * JAN/99 -- coded full program relocation (gerg@snapgear.com)
+ */
+
+/* ??? ZFLAT and shared library support is currently disabled. */
+
+/****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "qemu.h"
+#include "flat.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DBG_FLT(a...) printf(a)
+#else
+#define DBG_FLT(a...)
+#endif
+
+#define flat_reloc_valid(reloc, size) ((reloc) <= (size))
+#define flat_old_ram_flag(flag) (flag)
+#ifdef TARGET_WORDS_BIGENDIAN
+#define flat_get_relocate_addr(relval) (relval)
+#else
+#define flat_get_relocate_addr(relval) bswap32(relval)
+#endif
+
+#define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */
+#define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */
+
+struct lib_info {
+ target_ulong start_code; /* Start of text segment */
+ target_ulong start_data; /* Start of data segment */
+ target_ulong end_data; /* Start of bss section */
+ target_ulong start_brk; /* End of data segment */
+ target_ulong text_len; /* Length of text segment */
+ target_ulong entry; /* Start address for this module */
+ target_ulong build_date; /* When this one was compiled */
+ short loaded; /* Has this library been loaded? */
+};
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+static int load_flat_shared_library(int id, struct lib_info *p);
+#endif
+
+struct linux_binprm;
+
+#define ntohl(x) be32_to_cpu(x)
+
+/****************************************************************************/
+/*
+ * create_flat_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+
+/* Push a block of strings onto the guest stack. */
+static target_ulong copy_strings(target_ulong p, int n, char **s)
+{
+ int len;
+
+ while (n-- > 0) {
+ len = strlen(s[n]) + 1;
+ p -= len;
+ memcpy_to_target(p, s[n], len);
+ }
+
+ return p;
+}
+
+int target_pread(int fd, target_ulong ptr, target_ulong len,
+ target_ulong offset)
+{
+ void *buf;
+ int ret;
+
+ buf = lock_user(ptr, len, 0);
+ ret = pread(fd, buf, len, offset);
+ unlock_user(buf, ptr, len);
+ return ret;
+}
+/****************************************************************************/
+
+#ifdef CONFIG_BINFMT_ZFLAT
+
+#include <linux/zlib.h>
+
+#define LBUFSIZE 4000
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
+#define RESERVED 0xC0 /* bit 6,7: reserved */
+
+static int decompress_exec(
+ struct linux_binprm *bprm,
+ unsigned long offset,
+ char *dst,
+ long len,
+ int fd)
+{
+ unsigned char *buf;
+ z_stream strm;
+ loff_t fpos;
+ int ret, retval;
+
+ DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len);
+
+ memset(&strm, 0, sizeof(strm));
+ strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
+ if (strm.workspace == NULL) {
+ DBG_FLT("binfmt_flat: no memory for decompress workspace\n");
+ return -ENOMEM;
+ }
+ buf = kmalloc(LBUFSIZE, GFP_KERNEL);
+ if (buf == NULL) {
+ DBG_FLT("binfmt_flat: no memory for read buffer\n");
+ retval = -ENOMEM;
+ goto out_free;
+ }
+
+ /* Read in first chunk of data and parse gzip header. */
+ fpos = offset;
+ ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);
+
+ strm.next_in = buf;
+ strm.avail_in = ret;
+ strm.total_in = 0;
+
+ retval = -ENOEXEC;
+
+ /* Check minimum size -- gzip header */
+ if (ret < 10) {
+ DBG_FLT("binfmt_flat: file too small?\n");
+ goto out_free_buf;
+ }
+
+ /* Check gzip magic number */
+ if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) {
+ DBG_FLT("binfmt_flat: unknown compression magic?\n");
+ goto out_free_buf;
+ }
+
+ /* Check gzip method */
+ if (buf[2] != 8) {
+ DBG_FLT("binfmt_flat: unknown compression method?\n");
+ goto out_free_buf;
+ }
+ /* Check gzip flags */
+ if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) ||
+ (buf[3] & RESERVED)) {
+ DBG_FLT("binfmt_flat: unknown flags?\n");
+ goto out_free_buf;
+ }
+
+ ret = 10;
+ if (buf[3] & EXTRA_FIELD) {
+ ret += 2 + buf[10] + (buf[11] << 8);
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n");
+ goto out_free_buf;
+ }
+ }
+ if (buf[3] & ORIG_NAME) {
+ for (; ret < LBUFSIZE && (buf[ret] != 0); ret++)
+ ;
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n");
+ goto out_free_buf;
+ }
+ }
+ if (buf[3] & COMMENT) {
+ for (; ret < LBUFSIZE && (buf[ret] != 0); ret++)
+ ;
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n");
+ goto out_free_buf;
+ }
+ }
+
+ strm.next_in += ret;
+ strm.avail_in -= ret;
+
+ strm.next_out = dst;
+ strm.avail_out = len;
+ strm.total_out = 0;
+
+ if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
+ DBG_FLT("binfmt_flat: zlib init failed?\n");
+ goto out_free_buf;
+ }
+
+ while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) {
+ ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);
+ if (ret <= 0)
+ break;
+ if (ret >= (unsigned long) -4096)
+ break;
+ len -= ret;
+
+ strm.next_in = buf;
+ strm.avail_in = ret;
+ strm.total_in = 0;
+ }
+
+ if (ret < 0) {
+ DBG_FLT("binfmt_flat: decompression failed (%d), %s\n",
+ ret, strm.msg);
+ goto out_zlib;
+ }
+
+ retval = 0;
+out_zlib:
+ zlib_inflateEnd(&strm);
+out_free_buf:
+ kfree(buf);
+out_free:
+ kfree(strm.workspace);
+out:
+ return retval;
+}
+
+#endif /* CONFIG_BINFMT_ZFLAT */
+
+/****************************************************************************/
+
+static target_ulong
+calc_reloc(target_ulong r, struct lib_info *p, int curid, int internalp)
+{
+ target_ulong addr;
+ int id;
+ target_ulong start_brk;
+ target_ulong start_data;
+ target_ulong text_len;
+ target_ulong start_code;
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#error needs checking
+ if (r == 0)
+ id = curid; /* Relocs of 0 are always self referring */
+ else {
+ id = (r >> 24) & 0xff; /* Find ID for this reloc */
+ r &= 0x00ffffff; /* Trim ID off here */
+ }
+ if (id >= MAX_SHARED_LIBS) {
+ fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n",
+ (unsigned) r, id);
+ goto failed;
+ }
+ if (curid != id) {
+ if (internalp) {
+ fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not "
+ "in same module (%d != %d)\n",
+ (unsigned) r, curid, id);
+ goto failed;
+ } else if ( ! p[id].loaded &&
+ load_flat_shared_library(id, p) > (unsigned long) -4096) {
+ fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id);
+ goto failed;
+ }
+ /* Check versioning information (i.e. time stamps) */
+ if (p[id].build_date && p[curid].build_date
+ && p[curid].build_date < p[id].build_date) {
+ fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n",
+ id, curid);
+ goto failed;
+ }
+ }
+#else
+ id = 0;
+#endif
+
+ start_brk = p[id].start_brk;
+ start_data = p[id].start_data;
+ start_code = p[id].start_code;
+ text_len = p[id].text_len;
+
+ if (!flat_reloc_valid(r, start_brk - start_data + text_len)) {
+ fprintf(stderr, "BINFMT_FLAT: reloc outside program 0x%x "
+ "(0 - 0x%x/0x%x)\n",
+ (int) r,(int)(start_brk-start_code),(int)text_len);
+ goto failed;
+ }
+
+ if (r < text_len) /* In text segment */
+ addr = r + start_code;
+ else /* In data segment */
+ addr = r - text_len + start_data;
+
+ /* Range checked already above so doing the range tests is redundant...*/
+ return(addr);
+
+failed:
+ abort();
+ return RELOC_FAILED;
+}
+
+/****************************************************************************/
+
+/* ??? This does not handle endianness correctly. */
+void old_reloc(struct lib_info *libinfo, uint32_t rl)
+{
+#ifdef DEBUG
+ char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" };
+#endif
+ uint32_t *ptr;
+ uint32_t offset;
+ int reloc_type;
+
+ offset = rl & 0x3fffffff;
+ reloc_type = rl >> 30;
+ /* ??? How to handle this? */
+#if defined(CONFIG_COLDFIRE)
+ ptr = (uint32_t *) (libinfo->start_code + offset);
+#else
+ ptr = (uint32_t *) (libinfo->start_data + offset);
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Relocation of variable at DATASEG+%x "
+ "(address %p, currently %x) into segment %s\n",
+ offset, ptr, (int)*ptr, segment[reloc_type]);
+#endif
+
+ switch (reloc_type) {
+ case OLD_FLAT_RELOC_TYPE_TEXT:
+ *ptr += libinfo->start_code;
+ break;
+ case OLD_FLAT_RELOC_TYPE_DATA:
+ *ptr += libinfo->start_data;
+ break;
+ case OLD_FLAT_RELOC_TYPE_BSS:
+ *ptr += libinfo->end_data;
+ break;
+ default:
+ fprintf(stderr, "BINFMT_FLAT: Unknown relocation type=%x\n",
+ reloc_type);
+ break;
+ }
+ DBG_FLT("Relocation became %x\n", (int)*ptr);
+}
+
+/****************************************************************************/
+
+static int load_flat_file(struct linux_binprm * bprm,
+ struct lib_info *libinfo, int id, target_ulong *extra_stack)
+{
+ struct flat_hdr * hdr;
+ target_ulong textpos = 0, datapos = 0, result;
+ target_ulong realdatastart = 0;
+ target_ulong text_len, data_len, bss_len, stack_len, flags;
+ target_ulong memp = 0; /* for finding the brk area */
+ target_ulong extra;
+ target_ulong reloc = 0, rp;
+ int i, rev, relocs = 0;
+ target_ulong fpos;
+ target_ulong start_code, end_code;
+
+ hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */
+
+ text_len = ntohl(hdr->data_start);
+ data_len = ntohl(hdr->data_end) - ntohl(hdr->data_start);
+ bss_len = ntohl(hdr->bss_end) - ntohl(hdr->data_end);
+ stack_len = ntohl(hdr->stack_size);
+ if (extra_stack) {
+ stack_len += *extra_stack;
+ *extra_stack = stack_len;
+ }
+ relocs = ntohl(hdr->reloc_count);
+ flags = ntohl(hdr->flags);
+ rev = ntohl(hdr->rev);
+
+ DBG_FLT("BINFMT_FLAT: Loading file: %s\n", bprm->filename);
+
+ if (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION) {
+ fprintf(stderr, "BINFMT_FLAT: bad magic/rev (0x%x, need 0x%x)\n",
+ rev, (int) FLAT_VERSION);
+ return -ENOEXEC;
+ }
+
+ /* Don't allow old format executables to use shared libraries */
+ if (rev == OLD_FLAT_VERSION && id != 0) {
+ fprintf(stderr, "BINFMT_FLAT: shared libraries are not available\n");
+ return -ENOEXEC;
+ }
+
+ /*
+ * fix up the flags for the older format, there were all kinds
+ * of endian hacks, this only works for the simple cases
+ */
+ if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags))
+ flags = FLAT_FLAG_RAM;
+
+#ifndef CONFIG_BINFMT_ZFLAT
+ if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) {
+ fprintf(stderr, "Support for ZFLAT executables is not enabled\n");
+ return -ENOEXEC;
+ }
+#endif
+
+ /*
+ * calculate the extra space we need to map in
+ */
+ extra = relocs * sizeof(target_ulong);
+ if (extra < bss_len + stack_len)
+ extra = bss_len + stack_len;
+
+ /*
+ * there are a couple of cases here, the separate code/data
+ * case, and then the fully copied to RAM case which lumps
+ * it all together.
+ */
+ if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) {
+ /*
+ * this should give us a ROM ptr, but if it doesn't we don't
+ * really care
+ */
+ DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n");
+
+ textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC,
+ MAP_PRIVATE, bprm->fd, 0);
+ if (textpos == -1) {
+ fprintf(stderr, "Unable to mmap process text\n");
+ return -1;
+ }
+
+ realdatastart = target_mmap(0, data_len + extra +
+ MAX_SHARED_LIBS * sizeof(target_ulong),
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (realdatastart == -1) {
+ fprintf(stderr, "Unable to allocate RAM for process data\n");
+ return realdatastart;
+ }
+ datapos = realdatastart + MAX_SHARED_LIBS * sizeof(target_ulong);
+
+ DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n",
+ (int)(data_len + bss_len + stack_len), (int)datapos);
+
+ fpos = ntohl(hdr->data_start);
+#ifdef CONFIG_BINFMT_ZFLAT
+ if (flags & FLAT_FLAG_GZDATA) {
+ result = decompress_exec(bprm, fpos, (char *) datapos,
+ data_len + (relocs * sizeof(target_ulong)))
+ } else
+#endif
+ {
+ result = target_pread(bprm->fd, datapos,
+ data_len + (relocs * sizeof(target_ulong)),
+ fpos);
+ }
+ if (result < 0) {
+ fprintf(stderr, "Unable to read data+bss\n");
+ return result;
+ }
+
+ reloc = datapos + (ntohl(hdr->reloc_start) - text_len);
+ memp = realdatastart;
+
+ } else {
+
+ textpos = target_mmap(0, text_len + data_len + extra +
+ MAX_SHARED_LIBS * sizeof(target_ulong),
+ PROT_READ | PROT_EXEC | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (textpos == -1 ) {
+ fprintf(stderr, "Unable to allocate RAM for process text/data\n");
+ return -1;
+ }
+
+ realdatastart = textpos + ntohl(hdr->data_start);
+ datapos = realdatastart + MAX_SHARED_LIBS * sizeof(target_ulong);
+ reloc = (textpos + ntohl(hdr->reloc_start) +
+ MAX_SHARED_LIBS * sizeof(target_ulong));
+ memp = textpos;
+
+#ifdef CONFIG_BINFMT_ZFLAT
+#error code needs checking
+ /*
+ * load it all in and treat it like a RAM load from now on
+ */
+ if (flags & FLAT_FLAG_GZIP) {
+ result = decompress_exec(bprm, sizeof (struct flat_hdr),
+ (((char *) textpos) + sizeof (struct flat_hdr)),
+ (text_len + data_len + (relocs * sizeof(unsigned long))
+ - sizeof (struct flat_hdr)),
+ 0);
+ memmove((void *) datapos, (void *) realdatastart,
+ data_len + (relocs * sizeof(unsigned long)));
+ } else if (flags & FLAT_FLAG_GZDATA) {
+ fpos = 0;
+ result = bprm->file->f_op->read(bprm->file,
+ (char *) textpos, text_len, &fpos);
+ if (result < (unsigned long) -4096)
+ result = decompress_exec(bprm, text_len, (char *) datapos,
+ data_len + (relocs * sizeof(unsigned long)), 0);
+ }
+ else
+#endif
+ {
+ result = target_pread(bprm->fd, textpos,
+ text_len, 0);
+ if (result >= 0) {
+ result = target_pread(bprm->fd, datapos,
+ data_len + (relocs * sizeof(target_ulong)),
+ ntohl(hdr->data_start));
+ }
+ }
+ if (result < 0) {
+ fprintf(stderr, "Unable to read code+data+bss\n");
+ return result;
+ }
+ }
+
+ DBG_FLT("Mapping is 0x%x, Entry point is 0x%x, data_start is 0x%x\n",
+ (int)textpos, 0x00ffffff&ntohl(hdr->entry),
+ ntohl(hdr->data_start));
+
+ /* The main program needs a little extra setup in the task structure */
+ start_code = textpos + sizeof (struct flat_hdr);
+ end_code = textpos + text_len;
+
+ DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n",
+ id ? "Lib" : "Load", bprm->filename,
+ (int) start_code, (int) end_code,
+ (int) datapos,
+ (int) (datapos + data_len),
+ (int) (datapos + data_len),
+ (int) (((datapos + data_len + bss_len) + 3) & ~3));
+
+ text_len -= sizeof(struct flat_hdr); /* the real code len */
+
+ /* Store the current module values into the global library structure */
+ libinfo[id].start_code = start_code;
+ libinfo[id].start_data = datapos;
+ libinfo[id].end_data = datapos + data_len;
+ libinfo[id].start_brk = datapos + data_len + bss_len;
+ libinfo[id].text_len = text_len;
+ libinfo[id].loaded = 1;
+ libinfo[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
+ libinfo[id].build_date = ntohl(hdr->build_date);
+
+ /*
+ * We just load the allocations into some temporary memory to
+ * help simplify all this mumbo jumbo
+ *
+ * We've got two different sections of relocation entries.
+ * The first is the GOT which resides at the begining of the data segment
+ * and is terminated with a -1. This one can be relocated in place.
+ * The second is the extra relocation entries tacked after the image's
+ * data segment. These require a little more processing as the entry is
+ * really an offset into the image which contains an offset into the
+ * image.
+ */
+ if (flags & FLAT_FLAG_GOTPIC) {
+ rp = datapos;
+ while (1) {
+ target_ulong addr;
+ addr = tgetl(rp);
+ if (addr == -1)
+ break;
+ if (addr) {
+ addr = calc_reloc(addr, libinfo, id, 0);
+ if (addr == RELOC_FAILED)
+ return -ENOEXEC;
+ tputl(rp, addr);
+ }
+ rp += sizeof(target_ulong);
+ }
+ }
+
+ /*
+ * Now run through the relocation entries.
+ * We've got to be careful here as C++ produces relocatable zero
+ * entries in the constructor and destructor tables which are then
+ * tested for being not zero (which will always occur unless we're
+ * based from address zero). This causes an endless loop as __start
+ * is at zero. The solution used is to not relocate zero addresses.
+ * This has the negative side effect of not allowing a global data
+ * reference to be statically initialised to _stext (I've moved
+ * __start to address 4 so that is okay).
+ */
+ if (rev > OLD_FLAT_VERSION) {
+ for (i = 0; i < relocs; i++) {
+ target_ulong addr, relval;
+
+ /* Get the address of the pointer to be
+ relocated (of course, the address has to be
+ relocated first). */
+ relval = tgetl(reloc + i * sizeof (target_ulong));
+ addr = flat_get_relocate_addr(relval);
+ rp = calc_reloc(addr, libinfo, id, 1);
+ if (rp == RELOC_FAILED)
+ return -ENOEXEC;
+
+ /* Get the pointer's value. */
+ addr = tgetl(rp);
+ if (addr != 0) {
+ /*
+ * Do the relocation. PIC relocs in the data section are
+ * already in target order
+ */
+
+#ifndef TARGET_WORDS_BIGENDIAN
+ if ((flags & FLAT_FLAG_GOTPIC) == 0)
+ addr = bswap32(addr);
+#endif
+ addr = calc_reloc(addr, libinfo, id, 0);
+ if (addr == RELOC_FAILED)
+ return -ENOEXEC;
+
+ /* Write back the relocated pointer. */
+ tputl(rp, addr);
+ }
+ }
+ } else {
+ for (i = 0; i < relocs; i++) {
+ target_ulong relval;
+ relval = tgetl(reloc + i * sizeof (target_ulong));
+ old_reloc(&libinfo[0], relval);
+ }
+ }
+
+ /* zero the BSS. */
+ memset((void*)(datapos + data_len), 0, bss_len);
+
+ return 0;
+}
+
+
+/****************************************************************************/
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+
+/*
+ * Load a shared library into memory. The library gets its own data
+ * segment (including bss) but not argv/argc/environ.
+ */
+
+static int load_flat_shared_library(int id, struct lib_info *libs)
+{
+ struct linux_binprm bprm;
+ int res;
+ char buf[16];
+
+ /* Create the file name */
+ sprintf(buf, "/lib/lib%d.so", id);
+
+ /* Open the file up */
+ bprm.filename = buf;
+ bprm.file = open_exec(bprm.filename);
+ res = PTR_ERR(bprm.file);
+ if (IS_ERR(bprm.file))
+ return res;
+
+ res = prepare_binprm(&bprm);
+
+ if (res <= (unsigned long)-4096)
+ res = load_flat_file(&bprm, libs, id, NULL);
+ if (bprm.file) {
+ allow_write_access(bprm.file);
+ fput(bprm.file);
+ bprm.file = NULL;
+ }
+ return(res);
+}
+
+#endif /* CONFIG_BINFMT_SHARED_FLAT */
+
+int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info)
+{
+ struct lib_info libinfo[MAX_SHARED_LIBS];
+ target_ulong p = bprm->p;
+ target_ulong stack_len;
+ target_ulong start_addr;
+ target_ulong sp;
+ int res;
+ int i, j;
+
+ memset(libinfo, 0, sizeof(libinfo));
+ /*
+ * We have to add the size of our arguments to our stack size
+ * otherwise it's too easy for users to create stack overflows
+ * by passing in a huge argument list. And yes, we have to be
+ * pedantic and include space for the argv/envp array as it may have
+ * a lot of entries.
+ */
+#define TOP_OF_ARGS (TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *))
+ stack_len = TOP_OF_ARGS - bprm->p; /* the strings */
+ stack_len += (bprm->argc + 1) * 4; /* the argv array */
+ stack_len += (bprm->envc + 1) * 4; /* the envp array */
+
+
+ res = load_flat_file(bprm, libinfo, 0, &stack_len);
+ if (res > (unsigned long)-4096)
+ return res;
+
+ /* Update data segment pointers for all libraries */
+ for (i=0; i<MAX_SHARED_LIBS; i++) {
+ if (libinfo[i].loaded) {
+ target_ulong p;
+ p = libinfo[i].start_data;
+ for (j=0; j<MAX_SHARED_LIBS; j++) {
+ p -= 4;
+ tput32(p, libinfo[j].loaded
+ ? libinfo[j].start_data
+ : UNLOADED_LIB);
+ }
+ }
+ }
+
+ p = ((libinfo[0].start_brk + stack_len + 3) & ~3) - 4;
+ DBG_FLT("p=%x\n", (int)p);
+
+ /* Copy argv/envp. */
+ p = copy_strings(p, bprm->argc, bprm->argv);
+ p = copy_strings(p, bprm->envc, bprm->envp);
+ /* Align stack. */
+ sp = p & ~(target_ulong)(sizeof(target_ulong) - 1);
+ sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, 1);
+
+ /* Fake some return addresses to ensure the call chain will
+ * initialise library in order for us. We are required to call
+ * lib 1 first, then 2, ... and finally the main program (id 0).
+ */
+ start_addr = libinfo[0].entry;
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#error here
+ for (i = MAX_SHARED_LIBS-1; i>0; i--) {
+ if (libinfo[i].loaded) {
+ /* Push previos first to call address */
+ --sp; put_user(start_addr, sp);
+ start_addr = libinfo[i].entry;
+ }
+ }
+#endif
+
+ /* Stash our initial stack pointer into the mm structure */
+ info->start_code = libinfo[0].start_code;
+ info->end_code = libinfo[0].start_code = libinfo[0].text_len;
+ info->start_data = libinfo[0].start_data;
+ info->end_data = libinfo[0].end_data;
+ info->start_brk = libinfo[0].start_brk;
+ info->start_stack = sp;
+ info->entry = start_addr;
+ info->code_offset = info->start_code;
+ info->data_offset = info->start_data - libinfo[0].text_len;
+
+ DBG_FLT("start_thread(entry=0x%x, start_stack=0x%x)\n",
+ (int)info->entry, (int)info->start_stack);
+
+ return 0;
+}
diff --git a/linux-user/i386/syscall.h b/linux-user/i386/syscall.h
new file mode 100644
index 0000000..cc0942b
--- /dev/null
+++ b/linux-user/i386/syscall.h
@@ -0,0 +1,221 @@
+/* default linux values for the selectors */
+#define __USER_CS (0x23)
+#define __USER_DS (0x2B)
+
+struct target_pt_regs {
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ int xds;
+ int xes;
+ long orig_eax;
+ long eip;
+ int xcs;
+ long eflags;
+ long esp;
+ int xss;
+};
+
+/* ioctls */
+
+#define TARGET_LDT_ENTRIES 8192
+#define TARGET_LDT_ENTRY_SIZE 8
+
+#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
+#define TARGET_GDT_ENTRY_TLS_MIN 6
+#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
+
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ target_ulong base_addr;
+ unsigned int limit;
+ unsigned int flags;
+};
+
+/* vm86 defines */
+
+#define TARGET_BIOSSEG 0x0f000
+
+#define TARGET_CPU_086 0
+#define TARGET_CPU_186 1
+#define TARGET_CPU_286 2
+#define TARGET_CPU_386 3
+#define TARGET_CPU_486 4
+#define TARGET_CPU_586 5
+
+#define TARGET_VM86_SIGNAL 0 /* return due to signal */
+#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
+#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
+#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
+
+/*
+ * Additional return values when invoking new vm86()
+ */
+#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
+#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
+
+/*
+ * function codes when invoking new vm86()
+ */
+#define TARGET_VM86_PLUS_INSTALL_CHECK 0
+#define TARGET_VM86_ENTER 1
+#define TARGET_VM86_ENTER_NO_BYPASS 2
+#define TARGET_VM86_REQUEST_IRQ 3
+#define TARGET_VM86_FREE_IRQ 4
+#define TARGET_VM86_GET_IRQ_BITS 5
+#define TARGET_VM86_GET_AND_RESET_IRQ 6
+
+/*
+ * This is the stack-layout seen by the user space program when we have
+ * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
+ * is 'kernel_vm86_regs' (see below).
+ */
+
+struct target_vm86_regs {
+/*
+ * normal regs, with special meaning for the segment descriptors..
+ */
+ target_long ebx;
+ target_long ecx;
+ target_long edx;
+ target_long esi;
+ target_long edi;
+ target_long ebp;
+ target_long eax;
+ target_long __null_ds;
+ target_long __null_es;
+ target_long __null_fs;
+ target_long __null_gs;
+ target_long orig_eax;
+ target_long eip;
+ unsigned short cs, __csh;
+ target_long eflags;
+ target_long esp;
+ unsigned short ss, __ssh;
+/*
+ * these are specific to v86 mode:
+ */
+ unsigned short es, __esh;
+ unsigned short ds, __dsh;
+ unsigned short fs, __fsh;
+ unsigned short gs, __gsh;
+};
+
+struct target_revectored_struct {
+ target_ulong __map[8]; /* 256 bits */
+};
+
+struct target_vm86_struct {
+ struct target_vm86_regs regs;
+ target_ulong flags;
+ target_ulong screen_bitmap;
+ target_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+};
+
+/*
+ * flags masks
+ */
+#define TARGET_VM86_SCREEN_BITMAP 0x0001
+
+struct target_vm86plus_info_struct {
+ target_ulong flags;
+#define TARGET_force_return_for_pic (1 << 0)
+#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
+#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
+#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
+ unsigned char vm86dbg_intxxtab[32]; /* for debugger */
+};
+
+struct target_vm86plus_struct {
+ struct target_vm86_regs regs;
+ target_ulong flags;
+ target_ulong screen_bitmap;
+ target_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+ struct target_vm86plus_info_struct vm86plus;
+};
+
+/* ipcs */
+
+#define TARGET_SEMOP 1
+#define TARGET_SEMGET 2
+#define TARGET_SEMCTL 3
+#define TARGET_MSGSND 11
+#define TARGET_MSGRCV 12
+#define TARGET_MSGGET 13
+#define TARGET_MSGCTL 14
+#define TARGET_SHMAT 21
+#define TARGET_SHMDT 22
+#define TARGET_SHMGET 23
+#define TARGET_SHMCTL 24
+
+struct target_msgbuf {
+ int mtype;
+ char mtext[1];
+};
+
+struct target_ipc_kludge {
+ unsigned int msgp; /* Really (struct msgbuf *) */
+ int msgtyp;
+};
+
+struct target_ipc_perm {
+ int key;
+ unsigned short uid;
+ unsigned short gid;
+ unsigned short cuid;
+ unsigned short cgid;
+ unsigned short mode;
+ unsigned short seq;
+};
+
+struct target_msqid_ds {
+ struct target_ipc_perm msg_perm;
+ unsigned int msg_first; /* really struct target_msg* */
+ unsigned int msg_last; /* really struct target_msg* */
+ unsigned int msg_stime; /* really target_time_t */
+ unsigned int msg_rtime; /* really target_time_t */
+ unsigned int msg_ctime; /* really target_time_t */
+ unsigned int wwait; /* really struct wait_queue* */
+ unsigned int rwait; /* really struct wait_queue* */
+ unsigned short msg_cbytes;
+ unsigned short msg_qnum;
+ unsigned short msg_qbytes;
+ unsigned short msg_lspid;
+ unsigned short msg_lrpid;
+};
+
+struct target_shmid_ds {
+ struct target_ipc_perm shm_perm;
+ int shm_segsz;
+ unsigned int shm_atime; /* really target_time_t */
+ unsigned int shm_dtime; /* really target_time_t */
+ unsigned int shm_ctime; /* really target_time_t */
+ unsigned short shm_cpid;
+ unsigned short shm_lpid;
+ short shm_nattch;
+ unsigned short shm_npages;
+ unsigned long *shm_pages;
+ void *attaches; /* really struct shm_desc * */
+};
+
+#define TARGET_IPC_RMID 0
+#define TARGET_IPC_SET 1
+#define TARGET_IPC_STAT 2
+
+union target_semun {
+ int val;
+ unsigned int buf; /* really struct semid_ds * */
+ unsigned int array; /* really unsigned short * */
+ unsigned int __buf; /* really struct seminfo * */
+ unsigned int __pad; /* really void* */
+};
+
+#define UNAME_MACHINE "i686"
diff --git a/linux-user/i386/syscall_nr.h b/linux-user/i386/syscall_nr.h
new file mode 100644
index 0000000..9fa6be9
--- /dev/null
+++ b/linux-user/i386/syscall_nr.h
@@ -0,0 +1,274 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall 0
+#define TARGET_NR_exit 1
+#define TARGET_NR_fork 2
+#define TARGET_NR_read 3
+#define TARGET_NR_write 4
+#define TARGET_NR_open 5
+#define TARGET_NR_close 6
+#define TARGET_NR_waitpid 7
+#define TARGET_NR_creat 8
+#define TARGET_NR_link 9
+#define TARGET_NR_unlink 10
+#define TARGET_NR_execve 11
+#define TARGET_NR_chdir 12
+#define TARGET_NR_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#define TARGET_NR_sync 36
+#define TARGET_NR_kill 37
+#define TARGET_NR_rename 38
+#define TARGET_NR_mkdir 39
+#define TARGET_NR_rmdir 40
+#define TARGET_NR_dup 41
+#define TARGET_NR_pipe 42
+#define TARGET_NR_times 43
+#define TARGET_NR_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#define TARGET_NR_umask 60
+#define TARGET_NR_chroot 61
+#define TARGET_NR_ustat 62
+#define TARGET_NR_dup2 63
+#define TARGET_NR_getppid 64
+#define TARGET_NR_getpgrp 65
+#define TARGET_NR_setsid 66
+#define TARGET_NR_sigaction 67
+#define TARGET_NR_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#define TARGET_NR_readlink 85
+#define TARGET_NR_uselib 86
+#define TARGET_NR_swapon 87
+#define TARGET_NR_reboot 88
+#define TARGET_NR_readdir 89
+#define TARGET_NR_mmap 90
+#define TARGET_NR_munmap 91
+#define TARGET_NR_truncate 92
+#define TARGET_NR_ftruncate 93
+#define TARGET_NR_fchmod 94
+#define TARGET_NR_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#define TARGET_NR_socketcall 102
+#define TARGET_NR_syslog 103
+#define TARGET_NR_setitimer 104
+#define TARGET_NR_getitimer 105
+#define TARGET_NR_stat 106
+#define TARGET_NR_lstat 107
+#define TARGET_NR_fstat 108
+#define TARGET_NR_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86old 113
+#define TARGET_NR_wait4 114
+#define TARGET_NR_swapoff 115
+#define TARGET_NR_sysinfo 116
+#define TARGET_NR_ipc 117
+#define TARGET_NR_fsync 118
+#define TARGET_NR_sigreturn 119
+#define TARGET_NR_clone 120
+#define TARGET_NR_setdomainname 121
+#define TARGET_NR_uname 122
+#define TARGET_NR_modify_ldt 123
+#define TARGET_NR_adjtimex 124
+#define TARGET_NR_mprotect 125
+#define TARGET_NR_sigprocmask 126
+#define TARGET_NR_create_module 127
+#define TARGET_NR_init_module 128
+#define TARGET_NR_delete_module 129
+#define TARGET_NR_get_kernel_syms 130
+#define TARGET_NR_quotactl 131
+#define TARGET_NR_getpgid 132
+#define TARGET_NR_fchdir 133
+#define TARGET_NR_bdflush 134
+#define TARGET_NR_sysfs 135
+#define TARGET_NR_personality 136
+#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#define TARGET_NR_flock 143
+#define TARGET_NR_msync 144
+#define TARGET_NR_readv 145
+#define TARGET_NR_writev 146
+#define TARGET_NR_getsid 147
+#define TARGET_NR_fdatasync 148
+#define TARGET_NR__sysctl 149
+#define TARGET_NR_mlock 150
+#define TARGET_NR_munlock 151
+#define TARGET_NR_mlockall 152
+#define TARGET_NR_munlockall 153
+#define TARGET_NR_sched_setparam 154
+#define TARGET_NR_sched_getparam 155
+#define TARGET_NR_sched_setscheduler 156
+#define TARGET_NR_sched_getscheduler 157
+#define TARGET_NR_sched_yield 158
+#define TARGET_NR_sched_get_priority_max 159
+#define TARGET_NR_sched_get_priority_min 160
+#define TARGET_NR_sched_rr_get_interval 161
+#define TARGET_NR_nanosleep 162
+#define TARGET_NR_mremap 163
+#define TARGET_NR_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_vm86 166
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#define TARGET_NR_prctl 172
+#define TARGET_NR_rt_sigreturn 173
+#define TARGET_NR_rt_sigaction 174
+#define TARGET_NR_rt_sigprocmask 175
+#define TARGET_NR_rt_sigpending 176
+#define TARGET_NR_rt_sigtimedwait 177
+#define TARGET_NR_rt_sigqueueinfo 178
+#define TARGET_NR_rt_sigsuspend 179
+#define TARGET_NR_pread 180
+#define TARGET_NR_pwrite 181
+#define TARGET_NR_chown 182
+#define TARGET_NR_getcwd 183
+#define TARGET_NR_capget 184
+#define TARGET_NR_capset 185
+#define TARGET_NR_sigaltstack 186
+#define TARGET_NR_sendfile 187
+#define TARGET_NR_getpmsg 188 /* some people actually want streams */
+#define TARGET_NR_putpmsg 189 /* some people actually want streams */
+#define TARGET_NR_vfork 190
+#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define TARGET_NR_mmap2 192
+#define TARGET_NR_truncate64 193
+#define TARGET_NR_ftruncate64 194
+#define TARGET_NR_stat64 195
+#define TARGET_NR_lstat64 196
+#define TARGET_NR_fstat64 197
+#define TARGET_NR_lchown32 198
+#define TARGET_NR_getuid32 199
+#define TARGET_NR_getgid32 200
+#define TARGET_NR_geteuid32 201
+#define TARGET_NR_getegid32 202
+#define TARGET_NR_setreuid32 203
+#define TARGET_NR_setregid32 204
+#define TARGET_NR_getgroups32 205
+#define TARGET_NR_setgroups32 206
+#define TARGET_NR_fchown32 207
+#define TARGET_NR_setresuid32 208
+#define TARGET_NR_getresuid32 209
+#define TARGET_NR_setresgid32 210
+#define TARGET_NR_getresgid32 211
+#define TARGET_NR_chown32 212
+#define TARGET_NR_setuid32 213
+#define TARGET_NR_setgid32 214
+#define TARGET_NR_setfsuid32 215
+#define TARGET_NR_setfsgid32 216
+#define TARGET_NR_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_madvise1 219 /* delete when C lib stub is removed */
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_fcntl64 221
+/* 223 is unused */
+#define TARGET_NR_gettid 224
+#define TARGET_NR_readahead 225
+#define TARGET_NR_setxattr 226
+#define TARGET_NR_lsetxattr 227
+#define TARGET_NR_fsetxattr 228
+#define TARGET_NR_getxattr 229
+#define TARGET_NR_lgetxattr 230
+#define TARGET_NR_fgetxattr 231
+#define TARGET_NR_listxattr 232
+#define TARGET_NR_llistxattr 233
+#define TARGET_NR_flistxattr 234
+#define TARGET_NR_removexattr 235
+#define TARGET_NR_lremovexattr 236
+#define TARGET_NR_fremovexattr 237
+#define TARGET_NR_tkill 238
+#define TARGET_NR_sendfile64 239
+#define TARGET_NR_futex 240
+#define TARGET_NR_sched_setaffinity 241
+#define TARGET_NR_sched_getaffinity 242
+#define TARGET_NR_set_thread_area 243
+#define TARGET_NR_get_thread_area 244
+#define TARGET_NR_io_setup 245
+#define TARGET_NR_io_destroy 246
+#define TARGET_NR_io_getevents 247
+#define TARGET_NR_io_submit 248
+#define TARGET_NR_io_cancel 249
+#define TARGET_NR_fadvise64 250
+
+#define TARGET_NR_exit_group 252
+#define TARGET_NR_lookup_dcookie 253
+#define TARGET_NR_epoll_create 254
+#define TARGET_NR_epoll_ctl 255
+#define TARGET_NR_epoll_wait 256
+#define TARGET_NR_remap_file_pages 257
+#define TARGET_NR_set_tid_address 258
+#define TARGET_NR_timer_create 259
+#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1)
+#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3)
+#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4)
+#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5)
+#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6)
+#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8)
+
+#define TARGET_NR_utimes 271
diff --git a/linux-user/i386/termbits.h b/linux-user/i386/termbits.h
new file mode 100644
index 0000000..adff802
--- /dev/null
+++ b/linux-user/i386/termbits.h
@@ -0,0 +1,214 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* ioctls */
+
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+#define TARGET_TCGETA 0x5405
+#define TARGET_TCSETA 0x5406
+#define TARGET_TCSETAW 0x5407
+#define TARGET_TCSETAF 0x5408
+#define TARGET_TCSBRK 0x5409
+#define TARGET_TCXONC 0x540A
+#define TARGET_TCFLSH 0x540B
+
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+#define TARGET_TIOCGPGRP 0x540F
+#define TARGET_TIOCSPGRP 0x5410
+#define TARGET_TIOCOUTQ 0x5411
+#define TARGET_TIOCSTI 0x5412
+#define TARGET_TIOCGWINSZ 0x5413
+#define TARGET_TIOCSWINSZ 0x5414
+#define TARGET_TIOCMGET 0x5415
+#define TARGET_TIOCMBIS 0x5416
+#define TARGET_TIOCMBIC 0x5417
+#define TARGET_TIOCMSET 0x5418
+#define TARGET_TIOCGSOFTCAR 0x5419
+#define TARGET_TIOCSSOFTCAR 0x541A
+#define TARGET_FIONREAD 0x541B
+#define TARGET_TIOCINQ TARGET_FIONREAD
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+#define TARGET_FIONBIO 0x5421
+#define TARGET_TIOCNOTTY 0x5422
+#define TARGET_TIOCSETD 0x5423
+#define TARGET_TIOCGETD 0x5424
+#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */
+#define TARGET_FIOCLEX 0x5451
+#define TARGET_FIOASYNC 0x5452
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* Used for packet mode */
+#define TARGET_TIOCPKT_DATA 0
+#define TARGET_TIOCPKT_FLUSHREAD 1
+#define TARGET_TIOCPKT_FLUSHWRITE 2
+#define TARGET_TIOCPKT_STOP 4
+#define TARGET_TIOCPKT_START 8
+#define TARGET_TIOCPKT_NOSTOP 16
+#define TARGET_TIOCPKT_DOSTOP 32
+
+#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
new file mode 100644
index 0000000..dae3efa
--- /dev/null
+++ b/linux-user/ioctls.h
@@ -0,0 +1,302 @@
+ /* emulated ioctl list */
+
+ IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize)))
+ IOCTL(TIOCSWINSZ, IOC_W, MK_PTR(MK_STRUCT(STRUCT_winsize)))
+ IOCTL(FIONREAD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TCGETA, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TCSETA, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSETAW, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSETAF, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSBRK, 0, TYPE_INT)
+ IOCTL(TCSBRKP, 0, TYPE_INT)
+ IOCTL(TCXONC, 0, TYPE_INT)
+ IOCTL(TCFLSH, 0, TYPE_INT)
+ IOCTL(TIOCEXCL, 0, TYPE_NULL)
+ IOCTL(TIOCNXCL, 0, TYPE_NULL)
+ IOCTL(TIOCSCTTY, 0, TYPE_INT)
+ IOCTL(TIOCGPGRP, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSPGRP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMBIS, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMBIC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMSET, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGSOFTCAR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSSOFTCAR, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCLINUX, IOC_R | IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCCONS, 0, TYPE_NULL)
+ IOCTL(TIOCGSERIAL, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSSERIAL, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCPKT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(FIONBIO, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCNOTTY, 0, TYPE_NULL)
+ IOCTL(TIOCGETD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSETD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGPTN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSPTLCK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(FIOCLEX, 0, TYPE_NULL)
+ IOCTL(FIONCLEX, 0, TYPE_NULL)
+ IOCTL(FIOASYNC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGLCKTRMIOS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCSLCKTRMIOS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCSERCONFIG, 0, TYPE_NULL)
+ IOCTL(TIOCSERGETLSR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSERGETMULTI, IOC_R, MK_PTR(MK_STRUCT(STRUCT_serial_multiport_struct)))
+ IOCTL(TIOCSERSETMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_serial_multiport_struct)))
+ IOCTL(TIOCMIWAIT, 0, TYPE_INT)
+ IOCTL(TIOCGICOUNT, IOC_R, MK_PTR(MK_STRUCT(STRUCT_serial_icounter_struct)))
+
+ IOCTL(KIOCSOUND, 0, TYPE_INT)
+ IOCTL(KDMKTONE, 0, TYPE_INT)
+ IOCTL(KDGKBTYPE, IOC_R, MK_PTR(TYPE_CHAR))
+ IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry)))
+ IOCTL(KDGKBSENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbsentry)))
+
+ IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(BLKRRPART, 0, TYPE_NULL)
+ IOCTL(BLKGETSIZE, IOC_R, MK_PTR(TYPE_ULONG))
+#ifdef BLKGETSIZE64
+ IOCTL(BLKGETSIZE64, IOC_R, MK_PTR(TYPE_ULONGLONG))
+#endif
+ IOCTL(BLKFLSBUF, 0, TYPE_NULL)
+ IOCTL(BLKRASET, 0, TYPE_INT)
+ IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG))
+#ifdef FIBMAP
+ IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG))
+#endif
+#ifdef FIGETBSZ
+ IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG))
+#endif
+
+ IOCTL(SIOCATMARK, 0, TYPE_NULL)
+ IOCTL(SIOCADDRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
+ IOCTL(SIOCDELRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
+ IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+ IOCTL(SIOCSIFFLAGS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+ IOCTL(SIOCGIFADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFBRDADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFBRDADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFDSTADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFDSTADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFNETMASK, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFNETMASK, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFHWADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFHWADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFTXQLEN, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFTXQLEN, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFMETRIC, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCSIFMETRIC, IOC_W, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCGIFMTU, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCSIFMTU, IOC_W, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCGIFMAP, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifmap_ifreq)))
+ IOCTL(SIOCSIFMAP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_ifmap_ifreq)))
+ IOCTL(SIOCGIFSLAVE, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_char_ifreq)))
+ IOCTL(SIOCSIFSLAVE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_char_ifreq)))
+ IOCTL(SIOCGIFMEM, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ptr_ifreq)))
+ IOCTL(SIOCSIFMEM, IOC_W, MK_PTR(MK_STRUCT(STRUCT_ptr_ifreq)))
+ IOCTL(SIOCADDMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCDELMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFLINK, 0, TYPE_NULL)
+ IOCTL(SIOCGIFCONF, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifconf)))
+ IOCTL(SIOCGIFENCAP, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SIOCSIFENCAP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SIOCDARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCSARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCGARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCDRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCSRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCGRARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+
+ IOCTL(CDROMPAUSE, 0, TYPE_NULL)
+ IOCTL(CDROMSTART, 0, TYPE_NULL)
+ IOCTL(CDROMSTOP, 0, TYPE_NULL)
+ IOCTL(CDROMRESUME, 0, TYPE_NULL)
+ IOCTL(CDROMEJECT, 0, TYPE_NULL)
+ IOCTL(CDROMEJECT_SW, 0, TYPE_INT)
+ IOCTL(CDROMCLOSETRAY, 0, TYPE_NULL)
+ IOCTL(CDROMRESET, 0, TYPE_NULL)
+ IOCTL(CDROMPLAYMSF, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMPLAYTRKIND, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADTOCHDR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADTOCENTRY, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMVOLCTRL, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMSUBCHNL, IOC_RW, MK_PTR(TYPE_INT))
+ /* XXX: incorrect (need specific handling) */
+ IOCTL(CDROMREADAUDIO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_cdrom_read_audio)))
+ IOCTL(CDROMREADCOOKED, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADRAW, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADMODE1, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADMODE2, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADALL, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMMULTISESSION, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROM_GET_UPC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMVOLREAD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMSEEK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMPLAYBLK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROM_MEDIA_CHANGED, 0, TYPE_NULL)
+ IOCTL(CDROM_SET_OPTIONS, 0, TYPE_INT)
+ IOCTL(CDROM_CLEAR_OPTIONS, 0, TYPE_INT)
+ IOCTL(CDROM_SELECT_SPEED, 0, TYPE_INT)
+ IOCTL(CDROM_SELECT_DISC, 0, TYPE_INT)
+ IOCTL(CDROM_DRIVE_STATUS, 0, TYPE_NULL)
+ IOCTL(CDROM_DISC_STATUS, 0, TYPE_NULL)
+ IOCTL(CDROMAUDIOBUFSIZ, 0, TYPE_INT)
+
+#if 0
+ IOCTL(SNDCTL_COPR_HALT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_LOAD, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RCODE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RCVMSG, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RDATA, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_COPR_RUN, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_SENDMSG, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_WCODE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_WDATA, IOC_W, MK_PTR(TYPE_INT))
+#endif
+ IOCTL(SNDCTL_DSP_CHANNELS, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETBLKSIZE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETCAPS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETFMTS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETIPTR, IOC_R, MK_PTR(MK_STRUCT(STRUCT_count_info)))
+ IOCTL(SNDCTL_DSP_GETOPTR, IOC_R, MK_PTR(MK_STRUCT(STRUCT_count_info)))
+ IOCTL(SNDCTL_DSP_GETISPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
+ IOCTL(SNDCTL_DSP_GETOSPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
+ IOCTL(SNDCTL_DSP_GETTRIGGER, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_MAPINBUF, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_MAPOUTBUF, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_NONBLOCK, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_POST, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETDUPLEX, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETFMT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SETFRAGMENT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SETSYNCRO, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETTRIGGER, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SPEED, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_STEREO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SUBDIVIDE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SYNC, 0, TYPE_NULL)
+#if 0
+ IOCTL(SNDCTL_FM_4OP_ENABLE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_FM_LOAD_INSTR, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_INFO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_MPUCMD, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_MPUMODE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_PRETIME, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_CTRLRATE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_GETINCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_GETOUTCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_NRMIDIS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_NRSYNTHS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_OUTOFBAND, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_PANIC, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_PERCMODE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_RESETSAMPLES, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_SYNC, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_TESTMIDI, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_THRESHOLD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SYNTH_INFO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SYNTH_MEMAVL, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_CONTINUE, 0, TYPE_NULL)
+ IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT))
+#if 0
+ /* we invalidate these defines because they have a same number as
+ termios ioctls */
+ IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL)
+ IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL)
+#endif
+ IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT))
+
+ IOCTL(SOUND_PCM_WRITE_FILTER, IOC_W | IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_RATE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_CHANNELS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_BITS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_FILTER, IOC_R, MK_PTR(TYPE_INT))
+#endif
+ IOCTL(SOUND_MIXER_INFO, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_ACCESS, 0, TYPE_PTRVOID)
+ IOCTL(SOUND_MIXER_PRIVATE1, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE2, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE3, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE4, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE5, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_VOLUME, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_BASS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_TREBLE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_SYNTH, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_PCM, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_SPEAKER, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_MIC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_CD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_IMIX, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_ALTPCM, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECLEV, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_IGAIN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_OGAIN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE1, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE2, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE3, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_MUTE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_ENHANCE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LOUD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECSRC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_DEVMASK, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECMASK, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_STEREODEVS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_CAPS, IOC_R, MK_PTR(TYPE_INT))
+
+ IOCTL(SOUND_MIXER_WRITE_VOLUME, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_BASS, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_TREBLE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_SYNTH, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_PCM, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_SPEAKER, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_MIC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_CD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_IMIX, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_ALTPCM, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_RECLEV, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_IGAIN, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_OGAIN, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE1, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE2, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE3, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_MUTE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_ENHANCE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LOUD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_RECSRC, IOC_W, MK_PTR(TYPE_INT))
+
+ IOCTL(HDIO_GETGEO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_hd_geometry)))
+ IOCTL(HDIO_GET_UNMASKINTR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_MULTCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_IDENTITY, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_KEEPSETTINGS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_NOWERR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_DMA, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_32BIT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_DRIVE_CMD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_SET_UNMASKINTR, 0, TYPE_INT)
+ IOCTL(HDIO_SET_MULTCOUNT, 0, TYPE_INT)
+ IOCTL(HDIO_SET_KEEPSETTINGS, 0, TYPE_INT)
+ IOCTL(HDIO_SET_NOWERR, 0, TYPE_INT)
+ IOCTL(HDIO_SET_DMA, 0, TYPE_INT)
+ IOCTL(HDIO_SET_32BIT, 0, TYPE_INT)
+ IOCTL(HDIO_SET_PIO_MODE, 0, TYPE_INT)
+
+ IOCTL(VFAT_IOCTL_READDIR_BOTH, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
+ IOCTL(VFAT_IOCTL_READDIR_SHORT, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c
new file mode 100644
index 0000000..ef5409b
--- /dev/null
+++ b/linux-user/linuxload.c
@@ -0,0 +1,195 @@
+/* Code for loading Linux executables. Mostly linux kenrel code. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "qemu.h"
+
+#define NGROUPS 32
+
+/* ??? This should really be somewhere else. */
+void memcpy_to_target(target_ulong dest, const void *src,
+ unsigned long len)
+{
+ void *host_ptr;
+
+ host_ptr = lock_user(dest, len, 0);
+ memcpy(host_ptr, src, len);
+ unlock_user(host_ptr, dest, 1);
+}
+
+static int in_group_p(gid_t g)
+{
+ /* return TRUE if we're in the specified group, FALSE otherwise */
+ int ngroup;
+ int i;
+ gid_t grouplist[NGROUPS];
+
+ ngroup = getgroups(NGROUPS, grouplist);
+ for(i = 0; i < ngroup; i++) {
+ if(grouplist[i] == g) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int count(char ** vec)
+{
+ int i;
+
+ for(i = 0; *vec; i++) {
+ vec++;
+ }
+
+ return(i);
+}
+
+static int prepare_binprm(struct linux_binprm *bprm)
+{
+ struct stat st;
+ int mode;
+ int retval, id_change;
+
+ if(fstat(bprm->fd, &st) < 0) {
+ return(-errno);
+ }
+
+ mode = st.st_mode;
+ if(!S_ISREG(mode)) { /* Must be regular file */
+ return(-EACCES);
+ }
+ if(!(mode & 0111)) { /* Must have at least one execute bit set */
+ return(-EACCES);
+ }
+
+ bprm->e_uid = geteuid();
+ bprm->e_gid = getegid();
+ id_change = 0;
+
+ /* Set-uid? */
+ if(mode & S_ISUID) {
+ bprm->e_uid = st.st_uid;
+ if(bprm->e_uid != geteuid()) {
+ id_change = 1;
+ }
+ }
+
+ /* Set-gid? */
+ /*
+ * If setgid is set but no group execute bit then this
+ * is a candidate for mandatory locking, not a setgid
+ * executable.
+ */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ bprm->e_gid = st.st_gid;
+ if (!in_group_p(bprm->e_gid)) {
+ id_change = 1;
+ }
+ }
+
+ memset(bprm->buf, 0, sizeof(bprm->buf));
+ retval = lseek(bprm->fd, 0L, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(bprm->fd, bprm->buf, 128);
+ }
+ if(retval < 0) {
+ perror("prepare_binprm");
+ exit(-1);
+ /* return(-errno); */
+ }
+ else {
+ return(retval);
+ }
+}
+
+/* Construct the envp and argv tables on the target stack. */
+target_ulong loader_build_argptr(int envc, int argc, target_ulong sp,
+ target_ulong stringp, int push_ptr)
+{
+ int n = sizeof(target_ulong);
+ target_ulong envp;
+ target_ulong argv;
+
+ sp -= (envc + 1) * n;
+ envp = sp;
+ sp -= (argc + 1) * n;
+ argv = sp;
+ if (push_ptr) {
+ sp -= n; tputl(sp, envp);
+ sp -= n; tputl(sp, argv);
+ }
+ sp -= n; tputl(sp, argc);
+
+ while (argc-- > 0) {
+ tputl(argv, stringp); argv += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ tputl(argv, 0);
+ while (envc-- > 0) {
+ tputl(envp, stringp); envp += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ tputl(envp, 0);
+
+ return sp;
+}
+
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop)
+{
+ struct linux_binprm bprm;
+ int retval;
+ int i;
+
+ bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
+ bprm.page[i] = 0;
+ retval = open(filename, O_RDONLY);
+ if (retval < 0)
+ return retval;
+ bprm.fd = retval;
+ bprm.filename = (char *)filename;
+ bprm.argc = count(argv);
+ bprm.argv = argv;
+ bprm.envc = count(envp);
+ bprm.envp = envp;
+
+ retval = prepare_binprm(&bprm);
+
+ if(retval>=0) {
+ if (bprm.buf[0] == 0x7f
+ && bprm.buf[1] == 'E'
+ && bprm.buf[2] == 'L'
+ && bprm.buf[3] == 'F') {
+ retval = load_elf_binary(&bprm,regs,infop);
+#if defined(TARGET_HAS_BFLT)
+ } else if (bprm.buf[0] == 'b'
+ && bprm.buf[1] == 'F'
+ && bprm.buf[2] == 'L'
+ && bprm.buf[3] == 'T') {
+ retval = load_flt_binary(&bprm,regs,infop);
+#endif
+ } else {
+ fprintf(stderr, "Unknown binary format\n");
+ return -1;
+ }
+ }
+
+ if(retval>=0) {
+ /* success. Initialize important registers */
+ do_init_thread(regs, infop);
+ return retval;
+ }
+
+ /* Something went wrong, return the inode and free the argument pages*/
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) {
+ free(bprm.page[i]);
+ }
+ return(retval);
+}
diff --git a/linux-user/main.c b/linux-user/main.c
new file mode 100644
index 0000000..d169311
--- /dev/null
+++ b/linux-user/main.c
@@ -0,0 +1,1716 @@
+/*
+ * qemu user main
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu.h"
+
+#define DEBUG_LOGFILE "/tmp/qemu.log"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#endif
+
+static const char *interp_prefix = CONFIG_QEMU_PREFIX;
+const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
+
+#if defined(__i386__) && !defined(CONFIG_STATIC)
+/* Force usage of an ELF interpreter even if it is an ELF shared
+ object ! */
+const char interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
+#endif
+
+/* for recent libc, we add these dummy symbols which are not declared
+ when generating a linked object (bug in ld ?) */
+#if (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) && !defined(CONFIG_STATIC)
+long __preinit_array_start[0];
+long __preinit_array_end[0];
+long __init_array_start[0];
+long __init_array_end[0];
+long __fini_array_start[0];
+long __fini_array_end[0];
+#endif
+
+/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+ we allocate a bigger stack. Need a better solution, for example
+ by remapping the process stack directly at the right place */
+unsigned long x86_stack_size = 512 * 1024;
+
+void gemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void cpu_outb(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
+}
+
+void cpu_outw(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
+}
+
+void cpu_outl(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
+}
+
+int cpu_inb(CPUState *env, int addr)
+{
+ fprintf(stderr, "inb: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_inw(CPUState *env, int addr)
+{
+ fprintf(stderr, "inw: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_inl(CPUState *env, int addr)
+{
+ fprintf(stderr, "inl: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+
+/* timers for rdtsc */
+
+#if 0
+
+static uint64_t emu_time;
+
+int64_t cpu_get_real_ticks(void)
+{
+ return emu_time++;
+}
+
+#endif
+
+#ifdef TARGET_I386
+/***********************************************************/
+/* CPUX86 core interface */
+
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpu_get_real_ticks();
+}
+
+static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
+ int flags)
+{
+ unsigned int e1, e2;
+ uint32_t *p;
+ e1 = (addr << 16) | (limit & 0xffff);
+ e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
+ e2 |= flags;
+ p = ptr;
+ p[0] = tswapl(e1);
+ p[1] = tswapl(e2);
+}
+
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ unsigned long addr, unsigned int sel)
+{
+ unsigned int e1, e2;
+ uint32_t *p;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ p = ptr;
+ p[0] = tswapl(e1);
+ p[1] = tswapl(e2);
+}
+
+uint64_t gdt_table[6];
+uint64_t idt_table[256];
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+
+void cpu_loop(CPUX86State *env)
+{
+ int trapnr;
+ target_ulong pc;
+ target_siginfo_t info;
+
+ for(;;) {
+ trapnr = cpu_x86_exec(env);
+ switch(trapnr) {
+ case 0x80:
+ /* linux syscall */
+ env->regs[R_EAX] = do_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP]);
+ break;
+ case EXCP0B_NOSEG:
+ case EXCP0C_STACK:
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP0D_GPF:
+ if (env->eflags & VM_MASK) {
+ handle_vm86_fault(env);
+ } else {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+ case EXCP0E_PAGE:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ if (!(env->error_code & 1))
+ info.si_code = TARGET_SEGV_MAPERR;
+ else
+ info.si_code = TARGET_SEGV_ACCERR;
+ info._sifields._sigfault._addr = env->cr[2];
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP00_DIVZ:
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else {
+ /* division by zero */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_INTDIV;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+ case EXCP01_SSTP:
+ case EXCP03_INT3:
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else {
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ if (trapnr == EXCP01_SSTP) {
+ info.si_code = TARGET_TRAP_BRKPT;
+ info._sifields._sigfault._addr = env->eip;
+ } else {
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ }
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+ case EXCP04_INTO:
+ case EXCP05_BOUND:
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+ case EXCP06_ILLOP:
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ pc = env->segs[R_CS].base + env->eip;
+ fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
+ (long)pc, trapnr);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_ARM
+
+/* XXX: find a better solution */
+extern void tb_invalidate_page_range(target_ulong start, target_ulong end);
+
+static void arm_cache_flush(target_ulong start, target_ulong last)
+{
+ target_ulong addr, last1;
+
+ if (last < start)
+ return;
+ addr = start;
+ for(;;) {
+ last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1;
+ if (last1 > last)
+ last1 = last;
+ tb_invalidate_page_range(addr, last1 + 1);
+ if (last1 == last)
+ break;
+ addr = last1 + 1;
+ }
+}
+
+void cpu_loop(CPUARMState *env)
+{
+ int trapnr;
+ unsigned int n, insn;
+ target_siginfo_t info;
+ uint32_t addr;
+
+ for(;;) {
+ trapnr = cpu_arm_exec(env);
+ switch(trapnr) {
+ case EXCP_UDEF:
+ {
+ TaskState *ts = env->opaque;
+ uint32_t opcode;
+
+ /* we handle the FPU emulation here, as Linux */
+ /* we get the opcode */
+ opcode = tget32(env->regs[15]);
+
+ if (EmulateAll(opcode, &ts->fpa, env) == 0) {
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->regs[15];
+ queue_signal(info.si_signo, &info);
+ } else {
+ /* increment PC */
+ env->regs[15] += 4;
+ }
+ }
+ break;
+ case EXCP_SWI:
+ case EXCP_BKPT:
+ {
+ env->eabi = 1;
+ /* system call */
+ if (trapnr == EXCP_BKPT) {
+ if (env->thumb) {
+ insn = tget16(env->regs[15]);
+ n = insn & 0xff;
+ env->regs[15] += 2;
+ } else {
+ insn = tget32(env->regs[15]);
+ n = (insn & 0xf) | ((insn >> 4) & 0xff0);
+ env->regs[15] += 4;
+ }
+ } else {
+ if (env->thumb) {
+ insn = tget16(env->regs[15] - 2);
+ n = insn & 0xff;
+ } else {
+ insn = tget32(env->regs[15] - 4);
+ n = insn & 0xffffff;
+ }
+ }
+
+ if (n == ARM_NR_cacheflush) {
+ arm_cache_flush(env->regs[0], env->regs[1]);
+ } else if (n == ARM_NR_semihosting
+ || n == ARM_NR_thumb_semihosting) {
+ env->regs[0] = do_arm_semihosting (env);
+ } else if (n == 0 || n >= ARM_SYSCALL_BASE
+ || (env->thumb && n == ARM_THUMB_SYSCALL)) {
+ /* linux syscall */
+ if (env->thumb || n == 0) {
+ n = env->regs[7];
+ } else {
+ n -= ARM_SYSCALL_BASE;
+ env->eabi = 0;
+ }
+ env->regs[0] = do_syscall(env,
+ n,
+ env->regs[0],
+ env->regs[1],
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5]);
+ } else {
+ goto error;
+ }
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_PREFETCH_ABORT:
+ addr = env->cp15.c6_data;
+ goto do_segv;
+ case EXCP_DATA_ABORT:
+ addr = env->cp15.c6_insn;
+ goto do_segv;
+ do_segv:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = addr;
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ error:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+
+#endif
+
+#ifdef TARGET_SPARC
+
+//#define DEBUG_WIN
+
+/* WARNING: dealing with register windows _is_ complicated. More info
+ can be found at http://www.sics.se/~psm/sparcstack.html */
+static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
+{
+ index = (index + cwp * 16) & (16 * NWINDOWS - 1);
+ /* wrap handling : if cwp is on the last window, then we use the
+ registers 'after' the end */
+ if (index < 8 && env->cwp == (NWINDOWS - 1))
+ index += (16 * NWINDOWS);
+ return index;
+}
+
+/* save the register window 'cwp1' */
+static inline void save_window_offset(CPUSPARCState *env, int cwp1)
+{
+ unsigned int i;
+ target_ulong sp_ptr;
+
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#if defined(DEBUG_WIN)
+ printf("win_overflow: sp_ptr=0x%x save_cwp=%d\n",
+ (int)sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ tputl(sp_ptr, env->regbase[get_reg_index(env, cwp1, 8 + i)]);
+ sp_ptr += sizeof(target_ulong);
+ }
+}
+
+static void save_window(CPUSPARCState *env)
+{
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+ new_wim = ((env->wim >> 1) | (env->wim << (NWINDOWS - 1))) &
+ ((1LL << NWINDOWS) - 1);
+ save_window_offset(env, (env->cwp - 2) & (NWINDOWS - 1));
+ env->wim = new_wim;
+#else
+ save_window_offset(env, (env->cwp - 2) & (NWINDOWS - 1));
+ env->cansave++;
+ env->canrestore--;
+#endif
+}
+
+static void restore_window(CPUSPARCState *env)
+{
+ unsigned int new_wim, i, cwp1;
+ target_ulong sp_ptr;
+
+ new_wim = ((env->wim << 1) | (env->wim >> (NWINDOWS - 1))) &
+ ((1LL << NWINDOWS) - 1);
+
+ /* restore the invalid window */
+ cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#if defined(DEBUG_WIN)
+ printf("win_underflow: sp_ptr=0x%x load_cwp=%d\n",
+ (int)sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ env->regbase[get_reg_index(env, cwp1, 8 + i)] = tgetl(sp_ptr);
+ sp_ptr += sizeof(target_ulong);
+ }
+ env->wim = new_wim;
+#ifdef TARGET_SPARC64
+ env->canrestore++;
+ if (env->cleanwin < NWINDOWS - 1)
+ env->cleanwin++;
+ env->cansave--;
+#endif
+}
+
+static void flush_windows(CPUSPARCState *env)
+{
+ int offset, cwp1;
+
+ offset = 1;
+ for(;;) {
+ /* if restore would invoke restore_window(), then we can stop */
+ cwp1 = (env->cwp + offset) & (NWINDOWS - 1);
+ if (env->wim & (1 << cwp1))
+ break;
+ save_window_offset(env, cwp1);
+ offset++;
+ }
+ /* set wim so that restore will reload the registers */
+ cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
+ env->wim = 1 << cwp1;
+#if defined(DEBUG_WIN)
+ printf("flush_windows: nb=%d\n", offset - 1);
+#endif
+}
+
+void cpu_loop (CPUSPARCState *env)
+{
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_sparc_exec (env);
+
+ switch (trapnr) {
+#ifndef TARGET_SPARC64
+ case 0x88:
+ case 0x90:
+#else
+ case 0x16d:
+#endif
+ ret = do_syscall (env, env->gregs[1],
+ env->regwptr[0], env->regwptr[1],
+ env->regwptr[2], env->regwptr[3],
+ env->regwptr[4], env->regwptr[5]);
+ if ((unsigned int)ret >= (unsigned int)(-515)) {
+ env->psr |= PSR_CARRY;
+ ret = -ret;
+ } else {
+ env->psr &= ~PSR_CARRY;
+ }
+ env->regwptr[0] = ret;
+ /* next instruction */
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ break;
+ case 0x83: /* flush windows */
+ flush_windows(env);
+ /* next instruction */
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ break;
+#ifndef TARGET_SPARC64
+ case TT_WIN_OVF: /* window overflow */
+ save_window(env);
+ break;
+ case TT_WIN_UNF: /* window underflow */
+ restore_window(env);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->mmuregs[4];
+ queue_signal(info.si_signo, &info);
+ }
+ break;
+#else
+ case TT_SPILL: /* window overflow */
+ save_window(env);
+ break;
+ case TT_FILL: /* window underflow */
+ restore_window(env);
+ break;
+ // XXX
+#endif
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+
+#endif
+
+#ifdef TARGET_PPC
+
+static inline uint64_t cpu_ppc_get_tb (CPUState *env)
+{
+ /* TO FIX */
+ return 0;
+}
+
+uint32_t cpu_ppc_load_tbl (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
+}
+
+uint32_t cpu_ppc_load_tbu (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) >> 32;
+}
+
+static void cpu_ppc_store_tb (CPUState *env, uint64_t value)
+{
+ /* TO FIX */
+}
+
+void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
+{
+ cpu_ppc_store_tb(env, ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
+}
+
+void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
+{
+ cpu_ppc_store_tb(env, ((uint64_t)cpu_ppc_load_tbl(env) << 32) | value);
+}
+
+uint32_t cpu_ppc_load_decr (CPUState *env)
+{
+ /* TO FIX */
+ return -1;
+}
+
+void cpu_ppc_store_decr (CPUState *env, uint32_t value)
+{
+ /* TO FIX */
+}
+
+void cpu_loop(CPUPPCState *env)
+{
+ target_siginfo_t info;
+ int trapnr;
+ uint32_t ret;
+
+ for(;;) {
+ trapnr = cpu_ppc_exec(env);
+ if (trapnr != EXCP_SYSCALL_USER && trapnr != EXCP_BRANCH &&
+ trapnr != EXCP_TRACE) {
+ if (loglevel > 0) {
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+ }
+ switch(trapnr) {
+ case EXCP_NONE:
+ break;
+ case EXCP_SYSCALL_USER:
+ /* system call */
+ /* WARNING:
+ * PPC ABI uses overflow flag in cr0 to signal an error
+ * in syscalls.
+ */
+#if 0
+ printf("syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", env->gpr[0],
+ env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6]);
+#endif
+ env->crf[0] &= ~0x1;
+ ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->gpr[7],
+ env->gpr[8]);
+ if (ret > (uint32_t)(-515)) {
+ env->crf[0] |= 0x1;
+ ret = -ret;
+ }
+ env->gpr[3] = ret;
+#if 0
+ printf("syscall returned 0x%08x (%d)\n", ret, ret);
+#endif
+ break;
+ case EXCP_RESET:
+ /* Should not happen ! */
+ fprintf(stderr, "RESET asked... Stop emulation\n");
+ if (loglevel)
+ fprintf(logfile, "RESET asked... Stop emulation\n");
+ abort();
+ case EXCP_MACHINE_CHECK:
+ fprintf(stderr, "Machine check exeption... Stop emulation\n");
+ if (loglevel)
+ fprintf(logfile, "RESET asked... Stop emulation\n");
+ info.si_signo = TARGET_SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_BUS_OBJERR;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(info.si_signo, &info);
+ case EXCP_DSI:
+ fprintf(stderr, "Invalid data memory access: 0x%08x\n",
+ env->spr[SPR_DAR]);
+ if (loglevel) {
+ fprintf(logfile, "Invalid data memory access: 0x%08x\n",
+ env->spr[SPR_DAR]);
+ }
+ switch (env->error_code & 0xFF000000) {
+ case 0x40000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ case 0x04000000:
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLADR;
+ break;
+ case 0x08000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_ACCERR;
+ break;
+ default:
+ /* Let's send a regular segfault... */
+ fprintf(stderr, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ if (loglevel) {
+ fprintf(logfile, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ }
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ }
+ info._sifields._sigfault._addr = env->nip;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_ISI:
+ fprintf(stderr, "Invalid instruction fetch\n");
+ if (loglevel)
+ fprintf(logfile, "Invalid instruction fetch\n");
+ switch (env->error_code & 0xFF000000) {
+ case 0x40000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ case 0x10000000:
+ case 0x08000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_ACCERR;
+ break;
+ default:
+ /* Let's send a regular segfault... */
+ fprintf(stderr, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ if (loglevel) {
+ fprintf(logfile, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ }
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ }
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_EXTERNAL:
+ /* Should not happen ! */
+ fprintf(stderr, "External interruption... Stop emulation\n");
+ if (loglevel)
+ fprintf(logfile, "External interruption... Stop emulation\n");
+ abort();
+ case EXCP_ALIGN:
+ fprintf(stderr, "Invalid unaligned memory access\n");
+ if (loglevel)
+ fprintf(logfile, "Invalid unaligned memory access\n");
+ info.si_signo = TARGET_SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_BUS_ADRALN;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_PROGRAM:
+ switch (env->error_code & ~0xF) {
+ case EXCP_FP:
+ fprintf(stderr, "Program exception\n");
+ if (loglevel)
+ fprintf(logfile, "Program exception\n");
+ /* Set FX */
+ env->fpscr[7] |= 0x8;
+ /* Finally, update FEX */
+ if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
+ ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
+ env->fpscr[7] |= 0x4;
+ info.si_signo = TARGET_SIGFPE;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case EXCP_FP_OX:
+ info.si_code = TARGET_FPE_FLTOVF;
+ break;
+ case EXCP_FP_UX:
+ info.si_code = TARGET_FPE_FLTUND;
+ break;
+ case EXCP_FP_ZX:
+ case EXCP_FP_VXZDZ:
+ info.si_code = TARGET_FPE_FLTDIV;
+ break;
+ case EXCP_FP_XX:
+ info.si_code = TARGET_FPE_FLTRES;
+ break;
+ case EXCP_FP_VXSOFT:
+ info.si_code = TARGET_FPE_FLTINV;
+ break;
+ case EXCP_FP_VXNAN:
+ case EXCP_FP_VXISI:
+ case EXCP_FP_VXIDI:
+ case EXCP_FP_VXIMZ:
+ case EXCP_FP_VXVC:
+ case EXCP_FP_VXSQRT:
+ case EXCP_FP_VXCVI:
+ info.si_code = TARGET_FPE_FLTSUB;
+ break;
+ default:
+ fprintf(stderr, "Unknown floating point exception "
+ "(%02x)\n", env->error_code);
+ if (loglevel) {
+ fprintf(logfile, "Unknown floating point exception "
+ "(%02x)\n", env->error_code & 0xF);
+ }
+ }
+ break;
+ case EXCP_INVAL:
+ fprintf(stderr, "Invalid instruction\n");
+ if (loglevel)
+ fprintf(logfile, "Invalid instruction\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case EXCP_INVAL_INVAL:
+ info.si_code = TARGET_ILL_ILLOPC;
+ break;
+ case EXCP_INVAL_LSWX:
+ info.si_code = TARGET_ILL_ILLOPN;
+ break;
+ case EXCP_INVAL_SPR:
+ info.si_code = TARGET_ILL_PRVREG;
+ break;
+ case EXCP_INVAL_FP:
+ info.si_code = TARGET_ILL_COPROC;
+ break;
+ default:
+ fprintf(stderr, "Unknown invalid operation (%02x)\n",
+ env->error_code & 0xF);
+ if (loglevel) {
+ fprintf(logfile, "Unknown invalid operation (%02x)\n",
+ env->error_code & 0xF);
+ }
+ info.si_code = TARGET_ILL_ILLADR;
+ break;
+ }
+ break;
+ case EXCP_PRIV:
+ fprintf(stderr, "Privilege violation\n");
+ if (loglevel)
+ fprintf(logfile, "Privilege violation\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case EXCP_PRIV_OPC:
+ info.si_code = TARGET_ILL_PRVOPC;
+ break;
+ case EXCP_PRIV_REG:
+ info.si_code = TARGET_ILL_PRVREG;
+ break;
+ default:
+ fprintf(stderr, "Unknown privilege violation (%02x)\n",
+ env->error_code & 0xF);
+ info.si_code = TARGET_ILL_PRVOPC;
+ break;
+ }
+ break;
+ case EXCP_TRAP:
+ fprintf(stderr, "Tried to call a TRAP\n");
+ if (loglevel)
+ fprintf(logfile, "Tried to call a TRAP\n");
+ abort();
+ default:
+ /* Should not happen ! */
+ fprintf(stderr, "Unknown program exception (%02x)\n",
+ env->error_code);
+ if (loglevel) {
+ fprintf(logfile, "Unknwon program exception (%02x)\n",
+ env->error_code);
+ }
+ abort();
+ }
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_NO_FP:
+ fprintf(stderr, "No floating point allowed\n");
+ if (loglevel)
+ fprintf(logfile, "No floating point allowed\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_COPROC;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_DECR:
+ /* Should not happen ! */
+ fprintf(stderr, "Decrementer exception\n");
+ if (loglevel)
+ fprintf(logfile, "Decrementer exception\n");
+ abort();
+ case EXCP_TRACE:
+ /* Do nothing: we use this to trace execution */
+ break;
+ case EXCP_FP_ASSIST:
+ /* Should not happen ! */
+ fprintf(stderr, "Floating point assist exception\n");
+ if (loglevel)
+ fprintf(logfile, "Floating point assist exception\n");
+ abort();
+ case EXCP_MTMSR:
+ /* We reloaded the msr, just go on */
+ if (msr_pr == 0) {
+ fprintf(stderr, "Tried to go into supervisor mode !\n");
+ if (loglevel)
+ fprintf(logfile, "Tried to go into supervisor mode !\n");
+ abort();
+ }
+ break;
+ case EXCP_BRANCH:
+ /* We stopped because of a jump... */
+ break;
+ case EXCP_INTERRUPT:
+ /* Don't know why this should ever happen... */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ if (loglevel) {
+ fprintf(logfile, "qemu: unhandled CPU exception 0x%02x - "
+ "0x%02x - aborting\n", trapnr, env->error_code);
+ }
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_MIPS
+
+#define MIPS_SYS(name, args) args,
+
+static const uint8_t mips_syscall_args[] = {
+ MIPS_SYS(sys_syscall , 0) /* 4000 */
+ MIPS_SYS(sys_exit , 1)
+ MIPS_SYS(sys_fork , 0)
+ MIPS_SYS(sys_read , 3)
+ MIPS_SYS(sys_write , 3)
+ MIPS_SYS(sys_open , 3) /* 4005 */
+ MIPS_SYS(sys_close , 1)
+ MIPS_SYS(sys_waitpid , 3)
+ MIPS_SYS(sys_creat , 2)
+ MIPS_SYS(sys_link , 2)
+ MIPS_SYS(sys_unlink , 1) /* 4010 */
+ MIPS_SYS(sys_execve , 0)
+ MIPS_SYS(sys_chdir , 1)
+ MIPS_SYS(sys_time , 1)
+ MIPS_SYS(sys_mknod , 3)
+ MIPS_SYS(sys_chmod , 2) /* 4015 */
+ MIPS_SYS(sys_lchown , 3)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */
+ MIPS_SYS(sys_lseek , 3)
+ MIPS_SYS(sys_getpid , 0) /* 4020 */
+ MIPS_SYS(sys_mount , 5)
+ MIPS_SYS(sys_oldumount , 1)
+ MIPS_SYS(sys_setuid , 1)
+ MIPS_SYS(sys_getuid , 0)
+ MIPS_SYS(sys_stime , 1) /* 4025 */
+ MIPS_SYS(sys_ptrace , 4)
+ MIPS_SYS(sys_alarm , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */
+ MIPS_SYS(sys_pause , 0)
+ MIPS_SYS(sys_utime , 2) /* 4030 */
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_access , 2)
+ MIPS_SYS(sys_nice , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4035 */
+ MIPS_SYS(sys_sync , 0)
+ MIPS_SYS(sys_kill , 2)
+ MIPS_SYS(sys_rename , 2)
+ MIPS_SYS(sys_mkdir , 2)
+ MIPS_SYS(sys_rmdir , 1) /* 4040 */
+ MIPS_SYS(sys_dup , 1)
+ MIPS_SYS(sys_pipe , 0)
+ MIPS_SYS(sys_times , 1)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_brk , 1) /* 4045 */
+ MIPS_SYS(sys_setgid , 1)
+ MIPS_SYS(sys_getgid , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */
+ MIPS_SYS(sys_geteuid , 0)
+ MIPS_SYS(sys_getegid , 0) /* 4050 */
+ MIPS_SYS(sys_acct , 0)
+ MIPS_SYS(sys_umount , 2)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ioctl , 3)
+ MIPS_SYS(sys_fcntl , 3) /* 4055 */
+ MIPS_SYS(sys_ni_syscall , 2)
+ MIPS_SYS(sys_setpgid , 2)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_olduname , 1)
+ MIPS_SYS(sys_umask , 1) /* 4060 */
+ MIPS_SYS(sys_chroot , 1)
+ MIPS_SYS(sys_ustat , 2)
+ MIPS_SYS(sys_dup2 , 2)
+ MIPS_SYS(sys_getppid , 0)
+ MIPS_SYS(sys_getpgrp , 0) /* 4065 */
+ MIPS_SYS(sys_setsid , 0)
+ MIPS_SYS(sys_sigaction , 3)
+ MIPS_SYS(sys_sgetmask , 0)
+ MIPS_SYS(sys_ssetmask , 1)
+ MIPS_SYS(sys_setreuid , 2) /* 4070 */
+ MIPS_SYS(sys_setregid , 2)
+ MIPS_SYS(sys_sigsuspend , 0)
+ MIPS_SYS(sys_sigpending , 1)
+ MIPS_SYS(sys_sethostname , 2)
+ MIPS_SYS(sys_setrlimit , 2) /* 4075 */
+ MIPS_SYS(sys_getrlimit , 2)
+ MIPS_SYS(sys_getrusage , 2)
+ MIPS_SYS(sys_gettimeofday, 2)
+ MIPS_SYS(sys_settimeofday, 2)
+ MIPS_SYS(sys_getgroups , 2) /* 4080 */
+ MIPS_SYS(sys_setgroups , 2)
+ MIPS_SYS(sys_ni_syscall , 0) /* old_select */
+ MIPS_SYS(sys_symlink , 2)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */
+ MIPS_SYS(sys_readlink , 3) /* 4085 */
+ MIPS_SYS(sys_uselib , 1)
+ MIPS_SYS(sys_swapon , 2)
+ MIPS_SYS(sys_reboot , 3)
+ MIPS_SYS(old_readdir , 3)
+ MIPS_SYS(old_mmap , 6) /* 4090 */
+ MIPS_SYS(sys_munmap , 2)
+ MIPS_SYS(sys_truncate , 2)
+ MIPS_SYS(sys_ftruncate , 2)
+ MIPS_SYS(sys_fchmod , 2)
+ MIPS_SYS(sys_fchown , 3) /* 4095 */
+ MIPS_SYS(sys_getpriority , 2)
+ MIPS_SYS(sys_setpriority , 3)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_statfs , 2)
+ MIPS_SYS(sys_fstatfs , 2) /* 4100 */
+ MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */
+ MIPS_SYS(sys_socketcall , 2)
+ MIPS_SYS(sys_syslog , 3)
+ MIPS_SYS(sys_setitimer , 3)
+ MIPS_SYS(sys_getitimer , 2) /* 4105 */
+ MIPS_SYS(sys_newstat , 2)
+ MIPS_SYS(sys_newlstat , 2)
+ MIPS_SYS(sys_newfstat , 2)
+ MIPS_SYS(sys_uname , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */
+ MIPS_SYS(sys_vhangup , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */
+ MIPS_SYS(sys_wait4 , 4)
+ MIPS_SYS(sys_swapoff , 1) /* 4115 */
+ MIPS_SYS(sys_sysinfo , 1)
+ MIPS_SYS(sys_ipc , 6)
+ MIPS_SYS(sys_fsync , 1)
+ MIPS_SYS(sys_sigreturn , 0)
+ MIPS_SYS(sys_clone , 0) /* 4120 */
+ MIPS_SYS(sys_setdomainname, 2)
+ MIPS_SYS(sys_newuname , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */
+ MIPS_SYS(sys_adjtimex , 1)
+ MIPS_SYS(sys_mprotect , 3) /* 4125 */
+ MIPS_SYS(sys_sigprocmask , 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* was create_module */
+ MIPS_SYS(sys_init_module , 5)
+ MIPS_SYS(sys_delete_module, 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */
+ MIPS_SYS(sys_quotactl , 0)
+ MIPS_SYS(sys_getpgid , 1)
+ MIPS_SYS(sys_fchdir , 1)
+ MIPS_SYS(sys_bdflush , 2)
+ MIPS_SYS(sys_sysfs , 3) /* 4135 */
+ MIPS_SYS(sys_personality , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */
+ MIPS_SYS(sys_setfsuid , 1)
+ MIPS_SYS(sys_setfsgid , 1)
+ MIPS_SYS(sys_llseek , 5) /* 4140 */
+ MIPS_SYS(sys_getdents , 3)
+ MIPS_SYS(sys_select , 5)
+ MIPS_SYS(sys_flock , 2)
+ MIPS_SYS(sys_msync , 3)
+ MIPS_SYS(sys_readv , 3) /* 4145 */
+ MIPS_SYS(sys_writev , 3)
+ MIPS_SYS(sys_cacheflush , 3)
+ MIPS_SYS(sys_cachectl , 3)
+ MIPS_SYS(sys_sysmips , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4150 */
+ MIPS_SYS(sys_getsid , 1)
+ MIPS_SYS(sys_fdatasync , 0)
+ MIPS_SYS(sys_sysctl , 1)
+ MIPS_SYS(sys_mlock , 2)
+ MIPS_SYS(sys_munlock , 2) /* 4155 */
+ MIPS_SYS(sys_mlockall , 1)
+ MIPS_SYS(sys_munlockall , 0)
+ MIPS_SYS(sys_sched_setparam, 2)
+ MIPS_SYS(sys_sched_getparam, 2)
+ MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */
+ MIPS_SYS(sys_sched_getscheduler, 1)
+ MIPS_SYS(sys_sched_yield , 0)
+ MIPS_SYS(sys_sched_get_priority_max, 1)
+ MIPS_SYS(sys_sched_get_priority_min, 1)
+ MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */
+ MIPS_SYS(sys_nanosleep, 2)
+ MIPS_SYS(sys_mremap , 4)
+ MIPS_SYS(sys_accept , 3)
+ MIPS_SYS(sys_bind , 3)
+ MIPS_SYS(sys_connect , 3) /* 4170 */
+ MIPS_SYS(sys_getpeername , 3)
+ MIPS_SYS(sys_getsockname , 3)
+ MIPS_SYS(sys_getsockopt , 5)
+ MIPS_SYS(sys_listen , 2)
+ MIPS_SYS(sys_recv , 4) /* 4175 */
+ MIPS_SYS(sys_recvfrom , 6)
+ MIPS_SYS(sys_recvmsg , 3)
+ MIPS_SYS(sys_send , 4)
+ MIPS_SYS(sys_sendmsg , 3)
+ MIPS_SYS(sys_sendto , 6) /* 4180 */
+ MIPS_SYS(sys_setsockopt , 5)
+ MIPS_SYS(sys_shutdown , 2)
+ MIPS_SYS(sys_socket , 3)
+ MIPS_SYS(sys_socketpair , 4)
+ MIPS_SYS(sys_setresuid , 3) /* 4185 */
+ MIPS_SYS(sys_getresuid , 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */
+ MIPS_SYS(sys_poll , 3)
+ MIPS_SYS(sys_nfsservctl , 3)
+ MIPS_SYS(sys_setresgid , 3) /* 4190 */
+ MIPS_SYS(sys_getresgid , 3)
+ MIPS_SYS(sys_prctl , 5)
+ MIPS_SYS(sys_rt_sigreturn, 0)
+ MIPS_SYS(sys_rt_sigaction, 4)
+ MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */
+ MIPS_SYS(sys_rt_sigpending, 2)
+ MIPS_SYS(sys_rt_sigtimedwait, 4)
+ MIPS_SYS(sys_rt_sigqueueinfo, 3)
+ MIPS_SYS(sys_rt_sigsuspend, 0)
+ MIPS_SYS(sys_pread64 , 6) /* 4200 */
+ MIPS_SYS(sys_pwrite64 , 6)
+ MIPS_SYS(sys_chown , 3)
+ MIPS_SYS(sys_getcwd , 2)
+ MIPS_SYS(sys_capget , 2)
+ MIPS_SYS(sys_capset , 2) /* 4205 */
+ MIPS_SYS(sys_sigaltstack , 0)
+ MIPS_SYS(sys_sendfile , 4)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_mmap2 , 6) /* 4210 */
+ MIPS_SYS(sys_truncate64 , 4)
+ MIPS_SYS(sys_ftruncate64 , 4)
+ MIPS_SYS(sys_stat64 , 2)
+ MIPS_SYS(sys_lstat64 , 2)
+ MIPS_SYS(sys_fstat64 , 2) /* 4215 */
+ MIPS_SYS(sys_pivot_root , 2)
+ MIPS_SYS(sys_mincore , 3)
+ MIPS_SYS(sys_madvise , 3)
+ MIPS_SYS(sys_getdents64 , 3)
+ MIPS_SYS(sys_fcntl64 , 3) /* 4220 */
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_gettid , 0)
+ MIPS_SYS(sys_readahead , 5)
+ MIPS_SYS(sys_setxattr , 5)
+ MIPS_SYS(sys_lsetxattr , 5) /* 4225 */
+ MIPS_SYS(sys_fsetxattr , 5)
+ MIPS_SYS(sys_getxattr , 4)
+ MIPS_SYS(sys_lgetxattr , 4)
+ MIPS_SYS(sys_fgetxattr , 4)
+ MIPS_SYS(sys_listxattr , 3) /* 4230 */
+ MIPS_SYS(sys_llistxattr , 3)
+ MIPS_SYS(sys_flistxattr , 3)
+ MIPS_SYS(sys_removexattr , 2)
+ MIPS_SYS(sys_lremovexattr, 2)
+ MIPS_SYS(sys_fremovexattr, 2) /* 4235 */
+ MIPS_SYS(sys_tkill , 2)
+ MIPS_SYS(sys_sendfile64 , 5)
+ MIPS_SYS(sys_futex , 2)
+ MIPS_SYS(sys_sched_setaffinity, 3)
+ MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */
+ MIPS_SYS(sys_io_setup , 2)
+ MIPS_SYS(sys_io_destroy , 1)
+ MIPS_SYS(sys_io_getevents, 5)
+ MIPS_SYS(sys_io_submit , 3)
+ MIPS_SYS(sys_io_cancel , 3) /* 4245 */
+ MIPS_SYS(sys_exit_group , 1)
+ MIPS_SYS(sys_lookup_dcookie, 3)
+ MIPS_SYS(sys_epoll_create, 1)
+ MIPS_SYS(sys_epoll_ctl , 4)
+ MIPS_SYS(sys_epoll_wait , 3) /* 4250 */
+ MIPS_SYS(sys_remap_file_pages, 5)
+ MIPS_SYS(sys_set_tid_address, 1)
+ MIPS_SYS(sys_restart_syscall, 0)
+ MIPS_SYS(sys_fadvise64_64, 7)
+ MIPS_SYS(sys_statfs64 , 3) /* 4255 */
+ MIPS_SYS(sys_fstatfs64 , 2)
+ MIPS_SYS(sys_timer_create, 3)
+ MIPS_SYS(sys_timer_settime, 4)
+ MIPS_SYS(sys_timer_gettime, 2)
+ MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */
+ MIPS_SYS(sys_timer_delete, 1)
+ MIPS_SYS(sys_clock_settime, 2)
+ MIPS_SYS(sys_clock_gettime, 2)
+ MIPS_SYS(sys_clock_getres, 2)
+ MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */
+ MIPS_SYS(sys_tgkill , 3)
+ MIPS_SYS(sys_utimes , 2)
+ MIPS_SYS(sys_mbind , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */
+ MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */
+ MIPS_SYS(sys_mq_open , 4)
+ MIPS_SYS(sys_mq_unlink , 1)
+ MIPS_SYS(sys_mq_timedsend, 5)
+ MIPS_SYS(sys_mq_timedreceive, 5)
+ MIPS_SYS(sys_mq_notify , 2) /* 4275 */
+ MIPS_SYS(sys_mq_getsetattr, 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */
+ MIPS_SYS(sys_waitid , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */
+ MIPS_SYS(sys_add_key , 5)
+ MIPS_SYS(sys_request_key , 4)
+ MIPS_SYS(sys_keyctl , 5)
+};
+
+#undef MIPS_SYS
+
+void cpu_loop(CPUMIPSState *env)
+{
+ target_siginfo_t info;
+ int trapnr, ret, nb_args;
+ unsigned int syscall_num;
+ target_ulong arg5, arg6, sp_reg;
+
+ for(;;) {
+ trapnr = cpu_mips_exec(env);
+ switch(trapnr) {
+ case EXCP_SYSCALL:
+ {
+ syscall_num = env->gpr[2] - 4000;
+ env->PC += 4;
+ if (syscall_num >= sizeof(mips_syscall_args)) {
+ ret = -ENOSYS;
+ } else {
+ nb_args = mips_syscall_args[syscall_num];
+ if (nb_args >= 5) {
+ sp_reg = env->gpr[29];
+ /* these arguments are taken from the stack */
+ arg5 = tgetl(sp_reg + 16);
+ if (nb_args >= 6) {
+ arg6 = tgetl(sp_reg + 20);
+ } else {
+ arg6 = 0;
+ }
+ } else {
+ arg5 = 0;
+ arg6 = 0;
+ }
+ ret = do_syscall(env,
+ env->gpr[2],
+ env->gpr[4],
+ env->gpr[5],
+ env->gpr[6],
+ env->gpr[7],
+ arg5,
+ arg6);
+ }
+ if ((unsigned int)ret >= (unsigned int)(-1133)) {
+ env->gpr[7] = 1; /* error flag */
+ ret = -ret;
+ env->gpr[0] = ret;
+ env->gpr[2] = ret;
+ } else {
+ env->gpr[7] = 0; /* error flag */
+ env->gpr[2] = ret;
+ }
+ }
+ break;
+ case EXCP_CpU:
+ case EXCP_RI:
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = 0;
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ default:
+ // error:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_SH4
+void cpu_loop (CPUState *env)
+{
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_sh4_exec (env);
+
+ switch (trapnr) {
+ case 0x160:
+ ret = do_syscall(env,
+ env->gregs[3],
+ env->gregs[4],
+ env->gregs[5],
+ env->gregs[6],
+ env->gregs[7],
+ env->gregs[0],
+ 0);
+ env->gregs[0] = ret;
+ env->pc += 2;
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif
+
+void usage(void)
+{
+ printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n"
+ "usage: qemu-" TARGET_ARCH " [-h] [-g] [-d opts] [-L path] [-s size] program [arguments...]\n"
+ "Linux CPU emulator (compiled for %s emulation)\n"
+ "\n"
+ "-h print this help\n"
+ "-g port wait gdb connection to port\n"
+ "-L path set the elf interpreter prefix (default=%s)\n"
+ "-s size set the stack size in bytes (default=%ld)\n"
+ "\n"
+ "debug options:\n"
+#ifdef USE_CODE_COPY
+ "-no-code-copy disable code copy acceleration\n"
+#endif
+ "-d options activate log (logfile=%s)\n"
+ "-p pagesize set the host page size to 'pagesize'\n",
+ TARGET_ARCH,
+ interp_prefix,
+ x86_stack_size,
+ DEBUG_LOGFILE);
+ _exit(1);
+}
+
+/* XXX: currently only used for async signals (see signal.c) */
+CPUState *global_env;
+
+/* used to free thread contexts */
+TaskState *first_task_state;
+
+int main(int argc, char **argv)
+{
+ const char *filename;
+ struct target_pt_regs regs1, *regs = &regs1;
+ struct image_info info1, *info = &info1;
+ TaskState ts1, *ts = &ts1;
+ CPUState *env;
+ int optind;
+ const char *r;
+ int gdbstub_port = 0;
+
+ if (argc <= 1)
+ usage();
+
+ /* init debug */
+ cpu_set_log_filename(DEBUG_LOGFILE);
+
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ r = argv[optind];
+ if (r[0] != '-')
+ break;
+ optind++;
+ r++;
+ if (!strcmp(r, "-")) {
+ break;
+ } else if (!strcmp(r, "d")) {
+ int mask;
+ CPULogItem *item;
+
+ if (optind >= argc)
+ break;
+
+ r = argv[optind++];
+ mask = cpu_str_to_log_mask(r);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ } else if (!strcmp(r, "s")) {
+ r = argv[optind++];
+ x86_stack_size = strtol(r, (char **)&r, 0);
+ if (x86_stack_size <= 0)
+ usage();
+ if (*r == 'M')
+ x86_stack_size *= 1024 * 1024;
+ else if (*r == 'k' || *r == 'K')
+ x86_stack_size *= 1024;
+ } else if (!strcmp(r, "L")) {
+ interp_prefix = argv[optind++];
+ } else if (!strcmp(r, "p")) {
+ qemu_host_page_size = atoi(argv[optind++]);
+ if (qemu_host_page_size == 0 ||
+ (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
+ fprintf(stderr, "page size must be a power of two\n");
+ exit(1);
+ }
+ } else if (!strcmp(r, "g")) {
+ gdbstub_port = atoi(argv[optind++]);
+ } else if (!strcmp(r, "r")) {
+ qemu_uname_release = argv[optind++];
+ } else
+#ifdef USE_CODE_COPY
+ if (!strcmp(r, "no-code-copy")) {
+ code_copy_enabled = 0;
+ } else
+#endif
+ {
+ usage();
+ }
+ }
+ if (optind >= argc)
+ usage();
+ filename = argv[optind];
+
+ /* Zero out regs */
+ memset(regs, 0, sizeof(struct target_pt_regs));
+
+ /* Zero out image_info */
+ memset(info, 0, sizeof(struct image_info));
+
+ /* Scan interp_prefix dir for replacement files. */
+ init_paths(interp_prefix);
+
+ /* NOTE: we need to init the CPU at this stage to get
+ qemu_host_page_size */
+ env = cpu_init();
+ global_env = env;
+
+ if (loader_exec(filename, argv+optind, environ, regs, info) != 0) {
+ printf("Error loading %s\n", filename);
+ _exit(1);
+ }
+
+ if (loglevel) {
+ page_dump(logfile);
+
+ fprintf(logfile, "start_brk 0x%08lx\n" , info->start_brk);
+ fprintf(logfile, "end_code 0x%08lx\n" , info->end_code);
+ fprintf(logfile, "start_code 0x%08lx\n" , info->start_code);
+ fprintf(logfile, "start_data 0x%08lx\n" , info->start_data);
+ fprintf(logfile, "end_data 0x%08lx\n" , info->end_data);
+ fprintf(logfile, "start_stack 0x%08lx\n" , info->start_stack);
+ fprintf(logfile, "brk 0x%08lx\n" , info->brk);
+ fprintf(logfile, "entry 0x%08lx\n" , info->entry);
+ }
+
+ target_set_brk(info->brk);
+ syscall_init();
+ signal_init();
+
+ /* build Task State */
+ memset(ts, 0, sizeof(TaskState));
+ env->opaque = ts;
+ ts->used = 1;
+ ts->info = info;
+ env->user_mode_only = 1;
+
+#if defined(TARGET_I386)
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ env->hflags |= HF_PE_MASK;
+ if (env->cpuid_features & CPUID_SSE) {
+ env->cr[4] |= CR4_OSFXSR_MASK;
+ env->hflags |= HF_OSFXSR_MASK;
+ }
+
+ /* flags setup : we activate the IRQs by default as in user mode */
+ env->eflags |= IF_MASK;
+
+ /* linux register setup */
+ env->regs[R_EAX] = regs->eax;
+ env->regs[R_EBX] = regs->ebx;
+ env->regs[R_ECX] = regs->ecx;
+ env->regs[R_EDX] = regs->edx;
+ env->regs[R_ESI] = regs->esi;
+ env->regs[R_EDI] = regs->edi;
+ env->regs[R_EBP] = regs->ebp;
+ env->regs[R_ESP] = regs->esp;
+ env->eip = regs->eip;
+
+ /* linux interrupt setup */
+ env->idt.base = h2g(idt_table);
+ env->idt.limit = sizeof(idt_table) - 1;
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 3);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+ set_idt(0x80, 3);
+
+ /* linux segment setup */
+ env->gdt.base = h2g(gdt_table);
+ env->gdt.limit = sizeof(gdt_table) - 1;
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+ write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_FS, __USER_DS);
+ cpu_x86_load_seg(env, R_GS, __USER_DS);
+
+#elif defined(TARGET_ARM)
+ {
+ int i;
+ cpu_arm_set_model(env, ARM_CPUID_ARM1026);
+ cpsr_write(env, regs->uregs[16], 0xffffffff);
+ for(i = 0; i < 16; i++) {
+ env->regs[i] = regs->uregs[i];
+ }
+ ts->stack_base = info->start_stack;
+ ts->heap_base = info->brk;
+ /* This will be filled in on the first SYS_HEAPINFO call. */
+ ts->heap_limit = 0;
+ }
+#elif defined(TARGET_SPARC)
+ {
+ int i;
+ env->pc = regs->pc;
+ env->npc = regs->npc;
+ env->y = regs->y;
+ for(i = 0; i < 8; i++)
+ env->gregs[i] = regs->u_regs[i];
+ for(i = 0; i < 8; i++)
+ env->regwptr[i] = regs->u_regs[i + 8];
+ }
+#elif defined(TARGET_PPC)
+ {
+ ppc_def_t *def;
+ int i;
+
+ /* Choose and initialise CPU */
+ /* XXX: CPU model (or PVR) should be provided on command line */
+ // ppc_find_by_name("750gx", &def);
+ // ppc_find_by_name("750fx", &def);
+ // ppc_find_by_name("750p", &def);
+ ppc_find_by_name("750", &def);
+ // ppc_find_by_name("G3", &def);
+ // ppc_find_by_name("604r", &def);
+ // ppc_find_by_name("604e", &def);
+ // ppc_find_by_name("604", &def);
+ if (def == NULL) {
+ cpu_abort(env,
+ "Unable to find PowerPC CPU definition\n");
+ }
+ cpu_ppc_register(env, def);
+
+ for (i = 0; i < 32; i++) {
+ if (i != 12 && i != 6 && i != 13)
+ env->msr[i] = (regs->msr >> i) & 1;
+ }
+ env->nip = regs->nip;
+ for(i = 0; i < 32; i++) {
+ env->gpr[i] = regs->gpr[i];
+ }
+ }
+#elif defined(TARGET_MIPS)
+ {
+ int i;
+
+ for(i = 0; i < 32; i++) {
+ env->gpr[i] = regs->regs[i];
+ }
+ env->PC = regs->cp0_epc;
+#ifdef MIPS_USES_FPU
+ env->CP0_Status |= (1 << CP0St_CU1);
+#endif
+ }
+#elif defined(TARGET_SH4)
+ {
+ int i;
+
+ for(i = 0; i < 16; i++) {
+ env->gregs[i] = regs->regs[i];
+ }
+ env->pc = regs->pc;
+ }
+#else
+#error unsupported target CPU
+#endif
+
+ if (gdbstub_port) {
+ gdbserver_start (gdbstub_port);
+ gdb_handlesig(env, 0);
+ }
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
diff --git a/linux-user/mips/syscall.h b/linux-user/mips/syscall.h
new file mode 100644
index 0000000..4b3c7d6
--- /dev/null
+++ b/linux-user/mips/syscall.h
@@ -0,0 +1,23 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+#if 1
+ /* Pad bytes for argument save space on the stack. */
+ target_ulong pad0[6];
+#endif
+
+ /* Saved main processor registers. */
+ target_ulong regs[32];
+
+ /* Saved special registers. */
+ target_ulong cp0_status;
+ target_ulong lo;
+ target_ulong hi;
+ target_ulong cp0_badvaddr;
+ target_ulong cp0_cause;
+ target_ulong cp0_epc;
+};
+
+#define UNAME_MACHINE "mips"
diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h
new file mode 100644
index 0000000..3593e65
--- /dev/null
+++ b/linux-user/mips/syscall_nr.h
@@ -0,0 +1,288 @@
+/*
+ * Linux o32 style syscalls are in the range from 4000 to 4999.
+ */
+#define TARGET_NR_Linux 4000
+#define TARGET_NR_syscall (TARGET_NR_Linux + 0)
+#define TARGET_NR_exit (TARGET_NR_Linux + 1)
+#define TARGET_NR_fork (TARGET_NR_Linux + 2)
+#define TARGET_NR_read (TARGET_NR_Linux + 3)
+#define TARGET_NR_write (TARGET_NR_Linux + 4)
+#define TARGET_NR_open (TARGET_NR_Linux + 5)
+#define TARGET_NR_close (TARGET_NR_Linux + 6)
+#define TARGET_NR_waitpid (TARGET_NR_Linux + 7)
+#define TARGET_NR_creat (TARGET_NR_Linux + 8)
+#define TARGET_NR_link (TARGET_NR_Linux + 9)
+#define TARGET_NR_unlink (TARGET_NR_Linux + 10)
+#define TARGET_NR_execve (TARGET_NR_Linux + 11)
+#define TARGET_NR_chdir (TARGET_NR_Linux + 12)
+#define TARGET_NR_time (TARGET_NR_Linux + 13)
+#define TARGET_NR_mknod (TARGET_NR_Linux + 14)
+#define TARGET_NR_chmod (TARGET_NR_Linux + 15)
+#define TARGET_NR_lchown32 (TARGET_NR_Linux + 16)
+#define TARGET_NR_break (TARGET_NR_Linux + 17)
+#define TARGET_NR_unused18 (TARGET_NR_Linux + 18)
+#define TARGET_NR_lseek (TARGET_NR_Linux + 19)
+#define TARGET_NR_getpid (TARGET_NR_Linux + 20)
+#define TARGET_NR_mount (TARGET_NR_Linux + 21)
+#define TARGET_NR_umount (TARGET_NR_Linux + 22)
+#define TARGET_NR_setuid32 (TARGET_NR_Linux + 23)
+#define TARGET_NR_getuid32 (TARGET_NR_Linux + 24)
+#define TARGET_NR_stime (TARGET_NR_Linux + 25)
+#define TARGET_NR_ptrace (TARGET_NR_Linux + 26)
+#define TARGET_NR_alarm (TARGET_NR_Linux + 27)
+#define TARGET_NR_unused28 (TARGET_NR_Linux + 28)
+#define TARGET_NR_pause (TARGET_NR_Linux + 29)
+#define TARGET_NR_utime (TARGET_NR_Linux + 30)
+#define TARGET_NR_stty (TARGET_NR_Linux + 31)
+#define TARGET_NR_gtty (TARGET_NR_Linux + 32)
+#define TARGET_NR_access (TARGET_NR_Linux + 33)
+#define TARGET_NR_nice (TARGET_NR_Linux + 34)
+#define TARGET_NR_ftime (TARGET_NR_Linux + 35)
+#define TARGET_NR_sync (TARGET_NR_Linux + 36)
+#define TARGET_NR_kill (TARGET_NR_Linux + 37)
+#define TARGET_NR_rename (TARGET_NR_Linux + 38)
+#define TARGET_NR_mkdir (TARGET_NR_Linux + 39)
+#define TARGET_NR_rmdir (TARGET_NR_Linux + 40)
+#define TARGET_NR_dup (TARGET_NR_Linux + 41)
+#define TARGET_NR_pipe (TARGET_NR_Linux + 42)
+#define TARGET_NR_times (TARGET_NR_Linux + 43)
+#define TARGET_NR_prof (TARGET_NR_Linux + 44)
+#define TARGET_NR_brk (TARGET_NR_Linux + 45)
+#define TARGET_NR_setgid32 (TARGET_NR_Linux + 46)
+#define TARGET_NR_getgid32 (TARGET_NR_Linux + 47)
+#define TARGET_NR_signal (TARGET_NR_Linux + 48)
+#define TARGET_NR_geteuid32 (TARGET_NR_Linux + 49)
+#define TARGET_NR_getegid32 (TARGET_NR_Linux + 50)
+#define TARGET_NR_acct (TARGET_NR_Linux + 51)
+#define TARGET_NR_umount2 (TARGET_NR_Linux + 52)
+#define TARGET_NR_lock (TARGET_NR_Linux + 53)
+#define TARGET_NR_ioctl (TARGET_NR_Linux + 54)
+#define TARGET_NR_fcntl (TARGET_NR_Linux + 55)
+#define TARGET_NR_mpx (TARGET_NR_Linux + 56)
+#define TARGET_NR_setpgid (TARGET_NR_Linux + 57)
+#define TARGET_NR_ulimit (TARGET_NR_Linux + 58)
+#define TARGET_NR_unused59 (TARGET_NR_Linux + 59)
+#define TARGET_NR_umask (TARGET_NR_Linux + 60)
+#define TARGET_NR_chroot (TARGET_NR_Linux + 61)
+#define TARGET_NR_ustat (TARGET_NR_Linux + 62)
+#define TARGET_NR_dup2 (TARGET_NR_Linux + 63)
+#define TARGET_NR_getppid (TARGET_NR_Linux + 64)
+#define TARGET_NR_getpgrp (TARGET_NR_Linux + 65)
+#define TARGET_NR_setsid (TARGET_NR_Linux + 66)
+#define TARGET_NR_sigaction (TARGET_NR_Linux + 67)
+#define TARGET_NR_sgetmask (TARGET_NR_Linux + 68)
+#define TARGET_NR_ssetmask (TARGET_NR_Linux + 69)
+#define TARGET_NR_setreuid32 (TARGET_NR_Linux + 70)
+#define TARGET_NR_setregid32 (TARGET_NR_Linux + 71)
+#define TARGET_NR_sigsuspend (TARGET_NR_Linux + 72)
+#define TARGET_NR_sigpending (TARGET_NR_Linux + 73)
+#define TARGET_NR_sethostname (TARGET_NR_Linux + 74)
+#define TARGET_NR_setrlimit (TARGET_NR_Linux + 75)
+#define TARGET_NR_getrlimit (TARGET_NR_Linux + 76)
+#define TARGET_NR_getrusage (TARGET_NR_Linux + 77)
+#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 78)
+#define TARGET_NR_settimeofday (TARGET_NR_Linux + 79)
+#define TARGET_NR_getgroups32 (TARGET_NR_Linux + 80)
+#define TARGET_NR_setgroups32 (TARGET_NR_Linux + 81)
+#define TARGET_NR_reserved82 (TARGET_NR_Linux + 82)
+#define TARGET_NR_symlink (TARGET_NR_Linux + 83)
+#define TARGET_NR_unused84 (TARGET_NR_Linux + 84)
+#define TARGET_NR_readlink (TARGET_NR_Linux + 85)
+#define TARGET_NR_uselib (TARGET_NR_Linux + 86)
+#define TARGET_NR_swapon (TARGET_NR_Linux + 87)
+#define TARGET_NR_reboot (TARGET_NR_Linux + 88)
+#define TARGET_NR_readdir (TARGET_NR_Linux + 89)
+#define TARGET_NR_mmap (TARGET_NR_Linux + 90)
+#define TARGET_NR_munmap (TARGET_NR_Linux + 91)
+#define TARGET_NR_truncate (TARGET_NR_Linux + 92)
+#define TARGET_NR_ftruncate (TARGET_NR_Linux + 93)
+#define TARGET_NR_fchmod (TARGET_NR_Linux + 94)
+#define TARGET_NR_fchown32 (TARGET_NR_Linux + 95)
+#define TARGET_NR_getpriority (TARGET_NR_Linux + 96)
+#define TARGET_NR_setpriority (TARGET_NR_Linux + 97)
+#define TARGET_NR_profil (TARGET_NR_Linux + 98)
+#define TARGET_NR_statfs (TARGET_NR_Linux + 99)
+#define TARGET_NR_fstatfs (TARGET_NR_Linux + 100)
+#define TARGET_NR_ioperm (TARGET_NR_Linux + 101)
+#define TARGET_NR_socketcall (TARGET_NR_Linux + 102)
+#define TARGET_NR_syslog (TARGET_NR_Linux + 103)
+#define TARGET_NR_setitimer (TARGET_NR_Linux + 104)
+#define TARGET_NR_getitimer (TARGET_NR_Linux + 105)
+#define TARGET_NR_stat (TARGET_NR_Linux + 106)
+#define TARGET_NR_lstat (TARGET_NR_Linux + 107)
+#define TARGET_NR_fstat (TARGET_NR_Linux + 108)
+#define TARGET_NR_unused109 (TARGET_NR_Linux + 109)
+#define TARGET_NR_iopl (TARGET_NR_Linux + 110)
+#define TARGET_NR_vhangup (TARGET_NR_Linux + 111)
+#define TARGET_NR_idle (TARGET_NR_Linux + 112)
+#define TARGET_NR_vm86 (TARGET_NR_Linux + 113)
+#define TARGET_NR_wait4 (TARGET_NR_Linux + 114)
+#define TARGET_NR_swapoff (TARGET_NR_Linux + 115)
+#define TARGET_NR_sysinfo (TARGET_NR_Linux + 116)
+#define TARGET_NR_ipc (TARGET_NR_Linux + 117)
+#define TARGET_NR_fsync (TARGET_NR_Linux + 118)
+#define TARGET_NR_sigreturn (TARGET_NR_Linux + 119)
+#define TARGET_NR_clone (TARGET_NR_Linux + 120)
+#define TARGET_NR_setdomainname (TARGET_NR_Linux + 121)
+#define TARGET_NR_uname (TARGET_NR_Linux + 122)
+#define TARGET_NR_modify_ldt (TARGET_NR_Linux + 123)
+#define TARGET_NR_adjtimex (TARGET_NR_Linux + 124)
+#define TARGET_NR_mprotect (TARGET_NR_Linux + 125)
+#define TARGET_NR_sigprocmask (TARGET_NR_Linux + 126)
+#define TARGET_NR_create_module (TARGET_NR_Linux + 127)
+#define TARGET_NR_init_module (TARGET_NR_Linux + 128)
+#define TARGET_NR_delete_module (TARGET_NR_Linux + 129)
+#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 130)
+#define TARGET_NR_quotactl (TARGET_NR_Linux + 131)
+#define TARGET_NR_getpgid (TARGET_NR_Linux + 132)
+#define TARGET_NR_fchdir (TARGET_NR_Linux + 133)
+#define TARGET_NR_bdflush (TARGET_NR_Linux + 134)
+#define TARGET_NR_sysfs (TARGET_NR_Linux + 135)
+#define TARGET_NR_personality (TARGET_NR_Linux + 136)
+#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 137) /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid32 (TARGET_NR_Linux + 138)
+#define TARGET_NR_setfsgid32 (TARGET_NR_Linux + 139)
+#define TARGET_NR__llseek (TARGET_NR_Linux + 140)
+#define TARGET_NR_getdents (TARGET_NR_Linux + 141)
+#define TARGET_NR__newselect (TARGET_NR_Linux + 142)
+#define TARGET_NR_flock (TARGET_NR_Linux + 143)
+#define TARGET_NR_msync (TARGET_NR_Linux + 144)
+#define TARGET_NR_readv (TARGET_NR_Linux + 145)
+#define TARGET_NR_writev (TARGET_NR_Linux + 146)
+#define TARGET_NR_cacheflush (TARGET_NR_Linux + 147)
+#define TARGET_NR_cachectl (TARGET_NR_Linux + 148)
+#define TARGET_NR_sysmips (TARGET_NR_Linux + 149)
+#define TARGET_NR_unused150 (TARGET_NR_Linux + 150)
+#define TARGET_NR_getsid (TARGET_NR_Linux + 151)
+#define TARGET_NR_fdatasync (TARGET_NR_Linux + 152)
+#define TARGET_NR__sysctl (TARGET_NR_Linux + 153)
+#define TARGET_NR_mlock (TARGET_NR_Linux + 154)
+#define TARGET_NR_munlock (TARGET_NR_Linux + 155)
+#define TARGET_NR_mlockall (TARGET_NR_Linux + 156)
+#define TARGET_NR_munlockall (TARGET_NR_Linux + 157)
+#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 158)
+#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 159)
+#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 160)
+#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 161)
+#define TARGET_NR_sched_yield (TARGET_NR_Linux + 162)
+#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 163)
+#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 164)
+#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 165)
+#define TARGET_NR_nanosleep (TARGET_NR_Linux + 166)
+#define TARGET_NR_mremap (TARGET_NR_Linux + 167)
+#define TARGET_NR_accept (TARGET_NR_Linux + 168)
+#define TARGET_NR_bind (TARGET_NR_Linux + 169)
+#define TARGET_NR_connect (TARGET_NR_Linux + 170)
+#define TARGET_NR_getpeername (TARGET_NR_Linux + 171)
+#define TARGET_NR_getsockname (TARGET_NR_Linux + 172)
+#define TARGET_NR_getsockopt (TARGET_NR_Linux + 173)
+#define TARGET_NR_listen (TARGET_NR_Linux + 174)
+#define TARGET_NR_recv (TARGET_NR_Linux + 175)
+#define TARGET_NR_recvfrom (TARGET_NR_Linux + 176)
+#define TARGET_NR_recvmsg (TARGET_NR_Linux + 177)
+#define TARGET_NR_send (TARGET_NR_Linux + 178)
+#define TARGET_NR_sendmsg (TARGET_NR_Linux + 179)
+#define TARGET_NR_sendto (TARGET_NR_Linux + 180)
+#define TARGET_NR_setsockopt (TARGET_NR_Linux + 181)
+#define TARGET_NR_shutdown (TARGET_NR_Linux + 182)
+#define TARGET_NR_socket (TARGET_NR_Linux + 183)
+#define TARGET_NR_socketpair (TARGET_NR_Linux + 184)
+#define TARGET_NR_setresuid32 (TARGET_NR_Linux + 185)
+#define TARGET_NR_getresuid32 (TARGET_NR_Linux + 186)
+#define TARGET_NR_query_module (TARGET_NR_Linux + 187)
+#define TARGET_NR_poll (TARGET_NR_Linux + 188)
+#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 189)
+#define TARGET_NR_setresgid32 (TARGET_NR_Linux + 190)
+#define TARGET_NR_getresgid32 (TARGET_NR_Linux + 191)
+#define TARGET_NR_prctl (TARGET_NR_Linux + 192)
+#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 193)
+#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 194)
+#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 195)
+#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 196)
+#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 197)
+#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 198)
+#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 199)
+#define TARGET_NR_pread64 (TARGET_NR_Linux + 200)
+#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 201)
+#define TARGET_NR_chown32 (TARGET_NR_Linux + 202)
+#define TARGET_NR_getcwd (TARGET_NR_Linux + 203)
+#define TARGET_NR_capget (TARGET_NR_Linux + 204)
+#define TARGET_NR_capset (TARGET_NR_Linux + 205)
+#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 206)
+#define TARGET_NR_sendfile (TARGET_NR_Linux + 207)
+#define TARGET_NR_getpmsg (TARGET_NR_Linux + 208)
+#define TARGET_NR_putpmsg (TARGET_NR_Linux + 209)
+#define TARGET_NR_mmap2 (TARGET_NR_Linux + 210)
+#define TARGET_NR_truncate64 (TARGET_NR_Linux + 211)
+#define TARGET_NR_ftruncate64 (TARGET_NR_Linux + 212)
+#define TARGET_NR_stat64 (TARGET_NR_Linux + 213)
+#define TARGET_NR_lstat64 (TARGET_NR_Linux + 214)
+#define TARGET_NR_fstat64 (TARGET_NR_Linux + 215)
+#define TARGET_NR_pivot_root (TARGET_NR_Linux + 216)
+#define TARGET_NR_mincore (TARGET_NR_Linux + 217)
+#define TARGET_NR_madvise (TARGET_NR_Linux + 218)
+#define TARGET_NR_getdents64 (TARGET_NR_Linux + 219)
+#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 220)
+#define TARGET_NR_reserved221 (TARGET_NR_Linux + 221)
+#define TARGET_NR_gettid (TARGET_NR_Linux + 222)
+#define TARGET_NR_readahead (TARGET_NR_Linux + 223)
+#define TARGET_NR_setxattr (TARGET_NR_Linux + 224)
+#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 225)
+#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 226)
+#define TARGET_NR_getxattr (TARGET_NR_Linux + 227)
+#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 228)
+#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 229)
+#define TARGET_NR_listxattr (TARGET_NR_Linux + 230)
+#define TARGET_NR_llistxattr (TARGET_NR_Linux + 231)
+#define TARGET_NR_flistxattr (TARGET_NR_Linux + 232)
+#define TARGET_NR_removexattr (TARGET_NR_Linux + 233)
+#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 234)
+#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 235)
+#define TARGET_NR_tkill (TARGET_NR_Linux + 236)
+#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 237)
+#define TARGET_NR_futex (TARGET_NR_Linux + 238)
+#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 239)
+#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 240)
+#define TARGET_NR_io_setup (TARGET_NR_Linux + 241)
+#define TARGET_NR_io_destroy (TARGET_NR_Linux + 242)
+#define TARGET_NR_io_getevents (TARGET_NR_Linux + 243)
+#define TARGET_NR_io_submit (TARGET_NR_Linux + 244)
+#define TARGET_NR_io_cancel (TARGET_NR_Linux + 245)
+#define TARGET_NR_exit_group (TARGET_NR_Linux + 246)
+#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 247)
+#define TARGET_NR_epoll_create (TARGET_NR_Linux + 248)
+#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 249)
+#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 250)
+#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 251)
+#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 252)
+#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 253)
+#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 254)
+#define TARGET_NR_statfs64 (TARGET_NR_Linux + 255)
+#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 256)
+#define TARGET_NR_timer_create (TARGET_NR_Linux + 257)
+#define TARGET_NR_timer_settime (TARGET_NR_Linux + 258)
+#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 259)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 260)
+#define TARGET_NR_timer_delete (TARGET_NR_Linux + 261)
+#define TARGET_NR_clock_settime (TARGET_NR_Linux + 262)
+#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 263)
+#define TARGET_NR_clock_getres (TARGET_NR_Linux + 264)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 265)
+#define TARGET_NR_tgkill (TARGET_NR_Linux + 266)
+#define TARGET_NR_utimes (TARGET_NR_Linux + 267)
+#define TARGET_NR_mbind (TARGET_NR_Linux + 268)
+#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 269)
+#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 270)
+#define TARGET_NR_mq_open (TARGET_NR_Linux + 271)
+#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 272)
+#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 273)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 274)
+#define TARGET_NR_mq_notify (TARGET_NR_Linux + 275)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 276)
+#define TARGET_NR_vserver (TARGET_NR_Linux + 277)
+#define TARGET_NR_waitid (TARGET_NR_Linux + 278)
+/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 279) */
+#define TARGET_NR_add_key (TARGET_NR_Linux + 280)
+#define TARGET_NR_request_key (TARGET_NR_Linux + 281)
+#define TARGET_NR_keyctl (TARGET_NR_Linux + 282)
+
diff --git a/linux-user/mips/termbits.h b/linux-user/mips/termbits.h
new file mode 100644
index 0000000..fea7940
--- /dev/null
+++ b/linux-user/mips/termbits.h
@@ -0,0 +1,229 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 23
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* ioctls */
+
+#define TARGET_TCGETA 0x5401
+#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */
+#define TARGET_TCSETAW 0x5403
+#define TARGET_TCSETAF 0x5404
+
+#define TARGET_TCSBRK 0x5405
+#define TARGET_TCXONC 0x5406
+#define TARGET_TCFLSH 0x5407
+
+#define TARGET_TCGETS 0x540d
+#define TARGET_TCSETS 0x540e
+#define TARGET_TCSETSW 0x540f
+#define TARGET_TCSETSF 0x5410
+
+#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */
+#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */
+#define TARGET_TIOCOUTQ 0x7472 /* output queue size */
+#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */
+#define TARGET_TIOCMGET 0x741d /* get all modem bits */
+#define TARGET_TIOCMBIS 0x741b /* bis modem bits */
+#define TARGET_TIOCMBIC 0x741c /* bic modem bits */
+#define TARGET_TIOCMSET 0x741a /* set all modem bits */
+#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */
+#define TARGET_TIOCPKT_DATA 0x00 /* data packet */
+#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */
+#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */
+#define TARGET_TIOCPKT_STOP 0x04 /* stop output */
+#define TARGET_TIOCPKT_START 0x08 /* start output */
+#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
+#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
+/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */
+#define TARGET_TIOCNOTTY 0x5471 /* void tty association */
+#define TARGET_TIOCSETD 0x7401
+#define TARGET_TIOCGETD 0x7400
+
+#define TARGET_FIOCLEX 0x6601
+#define TARGET_FIONCLEX 0x6602
+#define TARGET_FIOASYNC 0x667d
+#define TARGET_FIONBIO 0x667e
+#define TARGET_FIOQSIZE 0x667f
+
+#define TARGET_TIOCGLTC 0x7474 /* get special local chars */
+#define TARGET_TIOCSLTC 0x7475 /* set special local chars */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */
+#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */
+
+#define TARGET_FIONREAD 0x467f
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+#define TARGET_TIOCGETP 0x7408
+#define TARGET_TIOCSETP 0x7409
+#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */
+
+/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */
+/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */
+/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */
+/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */
+/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */
+ /* 127-124 compat */
+
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+/* I hope the range from 0x5480 on is free ... */
+#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */
+#define TARGET_TIOCGSOFTCAR 0x5481
+#define TARGET_TIOCSSOFTCAR 0x5482
+#define TARGET_TIOCLINUX 0x5483
+#define TARGET_TIOCGSERIAL 0x5484
+#define TARGET_TIOCSSERIAL 0x5485
+#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCSERCONFIG 0x5488
+#define TARGET_TIOCSERGWILD 0x5489
+#define TARGET_TIOCSERSWILD 0x548a
+#define TARGET_TIOCGLCKTRMIOS 0x548b
+#define TARGET_TIOCSLCKTRMIOS 0x548c
+#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
new file mode 100644
index 0000000..0c90625
--- /dev/null
+++ b/linux-user/mmap.c
@@ -0,0 +1,417 @@
+/*
+ * mmap support for qemu
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "qemu.h"
+
+//#define DEBUG_MMAP
+
+/* NOTE: all the constants are the HOST ones, but addresses are target. */
+int target_mprotect(target_ulong start, target_ulong len, int prot)
+{
+ target_ulong end, host_start, host_end, addr;
+ int prot1, ret;
+
+#ifdef DEBUG_MMAP
+ printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+#endif
+
+ if ((start & ~TARGET_PAGE_MASK) != 0)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
+ return -EINVAL;
+ if (len == 0)
+ return 0;
+
+ host_start = start & qemu_host_page_mask;
+ host_end = HOST_PAGE_ALIGN(end);
+ if (start > host_start) {
+ /* handle host page containing start */
+ prot1 = prot;
+ for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ if (host_end == host_start + qemu_host_page_size) {
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ end = host_end;
+ }
+ ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
+ if (ret != 0)
+ return ret;
+ host_start += qemu_host_page_size;
+ }
+ if (end < host_end) {
+ prot1 = prot;
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
+ prot1 & PAGE_BITS);
+ if (ret != 0)
+ return ret;
+ host_end -= qemu_host_page_size;
+ }
+
+ /* handle the pages in the middle */
+ if (host_start < host_end) {
+ ret = mprotect(g2h(host_start), host_end - host_start, prot);
+ if (ret != 0)
+ return ret;
+ }
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ return 0;
+}
+
+/* map an incomplete host page */
+static int mmap_frag(target_ulong real_start,
+ target_ulong start, target_ulong end,
+ int prot, int flags, int fd, target_ulong offset)
+{
+ target_ulong real_end, ret, addr;
+ void *host_start;
+ int prot1, prot_new;
+
+ real_end = real_start + qemu_host_page_size;
+ host_start = g2h(real_start);
+
+ /* get the protection of the target pages outside the mapping */
+ prot1 = 0;
+ for(addr = real_start; addr < real_end; addr++) {
+ if (addr < start || addr >= end)
+ prot1 |= page_get_flags(addr);
+ }
+
+ if (prot1 == 0) {
+ /* no page was there, so we allocate one */
+ ret = (long)mmap(host_start, qemu_host_page_size, prot,
+ flags | MAP_ANONYMOUS, -1, 0);
+ if (ret == -1)
+ return ret;
+ prot1 = prot;
+ }
+ prot1 &= PAGE_BITS;
+
+ prot_new = prot | prot1;
+ if (!(flags & MAP_ANONYMOUS)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+ (prot & PROT_WRITE))
+ return -EINVAL;
+
+ /* adjust protection to be able to read */
+ if (!(prot1 & PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
+
+ /* read the corresponding file data */
+ pread(fd, g2h(start), end - start, offset);
+
+ /* put final protection */
+ if (prot_new != (prot1 | PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ } else {
+ /* just update the protection */
+ if (prot_new != prot1) {
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ }
+ }
+ return 0;
+}
+
+/* NOTE: all the constants are the HOST ones */
+long target_mmap(target_ulong start, target_ulong len, int prot,
+ int flags, int fd, target_ulong offset)
+{
+ target_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
+ long host_start;
+#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \
+ defined(__ia64)
+ static target_ulong last_start = 0x40000000;
+#elif defined(__CYGWIN__)
+ /* Cygwin doesn't have a whole lot of address space. */
+ static target_ulong last_start = 0x18000000;
+#endif
+
+#ifdef DEBUG_MMAP
+ {
+ printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
+ start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+ if (flags & MAP_FIXED)
+ printf("MAP_FIXED ");
+ if (flags & MAP_ANONYMOUS)
+ printf("MAP_ANON ");
+ switch(flags & MAP_TYPE) {
+ case MAP_PRIVATE:
+ printf("MAP_PRIVATE ");
+ break;
+ case MAP_SHARED:
+ printf("MAP_SHARED ");
+ break;
+ default:
+ printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
+ break;
+ }
+ printf("fd=%d offset=%lx\n", fd, offset);
+ }
+#endif
+
+ if (offset & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return start;
+ real_start = start & qemu_host_page_mask;
+
+ if (!(flags & MAP_FIXED)) {
+#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \
+ defined(__ia64) || defined(__CYGWIN__)
+ /* tell the kenel to search at the same place as i386 */
+ if (real_start == 0) {
+ real_start = last_start;
+ last_start += HOST_PAGE_ALIGN(len);
+ }
+#endif
+ if (qemu_host_page_size != qemu_real_host_page_size) {
+ /* NOTE: this code is only for debugging with '-p' option */
+ /* ??? Can also occur when TARGET_PAGE_SIZE > host page size. */
+ /* reserve a memory area */
+ /* ??? This needs fixing for remapping. */
+abort();
+ host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE;
+ real_start = (long)mmap(g2h(real_start), host_len, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (real_start == -1)
+ return real_start;
+ real_end = real_start + host_len;
+ start = HOST_PAGE_ALIGN(real_start);
+ end = start + HOST_PAGE_ALIGN(len);
+ if (start > real_start)
+ munmap((void *)real_start, start - real_start);
+ if (end < real_end)
+ munmap((void *)end, real_end - end);
+ /* use it as a fixed mapping */
+ flags |= MAP_FIXED;
+ } else {
+ /* if not fixed, no need to do anything */
+ host_offset = offset & qemu_host_page_mask;
+ host_len = len + offset - host_offset;
+ host_start = (long)mmap(real_start ? g2h(real_start) : NULL,
+ host_len, prot, flags, fd, host_offset);
+ if (host_start == -1)
+ return host_start;
+ /* update start so that it points to the file position at 'offset' */
+ if (!(flags & MAP_ANONYMOUS))
+ host_start += offset - host_offset;
+ start = h2g(host_start);
+ goto the_end1;
+ }
+ }
+
+ if (start & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ return -1;
+ }
+ end = start + len;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ /* worst case: we cannot map the file because the offset is not
+ aligned, so we read it */
+ if (!(flags & MAP_ANONYMOUS) &&
+ (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+ (prot & PROT_WRITE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ retaddr = target_mmap(start, len, prot | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (retaddr == -1)
+ return retaddr;
+ pread(fd, g2h(start), len, offset);
+ if (!(prot & PROT_WRITE)) {
+ ret = target_mprotect(start, len, prot);
+ if (ret != 0)
+ return ret;
+ }
+ goto the_end;
+ }
+
+ /* handle the start of the mapping */
+ if (start > real_start) {
+ if (real_end == real_start + qemu_host_page_size) {
+ /* one single host page */
+ ret = mmap_frag(real_start, start, end,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ return ret;
+ goto the_end1;
+ }
+ ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ return ret;
+ real_start += qemu_host_page_size;
+ }
+ /* handle the end of the mapping */
+ if (end < real_end) {
+ ret = mmap_frag(real_end - qemu_host_page_size,
+ real_end - qemu_host_page_size, real_end,
+ prot, flags, fd,
+ offset + real_end - qemu_host_page_size - start);
+ if (ret == -1)
+ return ret;
+ real_end -= qemu_host_page_size;
+ }
+
+ /* map the middle (easier) */
+ if (real_start < real_end) {
+ unsigned long offset1;
+ if (flags & MAP_ANONYMOUS)
+ offset1 = 0;
+ else
+ offset1 = offset + real_start - start;
+ ret = (long)mmap(g2h(real_start), real_end - real_start,
+ prot, flags, fd, offset1);
+ if (ret == -1)
+ return ret;
+ }
+ the_end1:
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ the_end:
+#ifdef DEBUG_MMAP
+ printf("ret=0x%lx\n", (long)start);
+ page_dump(stdout);
+ printf("\n");
+#endif
+ return start;
+}
+
+int target_munmap(target_ulong start, target_ulong len)
+{
+ target_ulong end, real_start, real_end, addr;
+ int prot, ret;
+
+#ifdef DEBUG_MMAP
+ printf("munmap: start=0x%lx len=0x%lx\n", start, len);
+#endif
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return -EINVAL;
+ end = start + len;
+ real_start = start & qemu_host_page_mask;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ if (start > real_start) {
+ /* handle host page containing start */
+ prot = 0;
+ for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (real_end == real_start + qemu_host_page_size) {
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ end = real_end;
+ }
+ if (prot != 0)
+ real_start += qemu_host_page_size;
+ }
+ if (end < real_end) {
+ prot = 0;
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (prot != 0)
+ real_end -= qemu_host_page_size;
+ }
+
+ /* unmap what we can */
+ if (real_start < real_end) {
+ ret = munmap((void *)real_start, real_end - real_start);
+ if (ret != 0)
+ return ret;
+ }
+
+ page_set_flags(start, start + len, 0);
+ return 0;
+}
+
+/* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
+ blocks which have been allocated starting on a host page */
+long target_mremap(target_ulong old_addr, target_ulong old_size,
+ target_ulong new_size, unsigned long flags,
+ target_ulong new_addr)
+{
+ int prot;
+
+ /* XXX: use 5 args syscall */
+ new_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
+ if (new_addr == -1)
+ return new_addr;
+ new_addr = h2g(new_addr);
+ prot = page_get_flags(old_addr);
+ page_set_flags(old_addr, old_addr + old_size, 0);
+ page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
+ return new_addr;
+}
+
+int target_msync(target_ulong start, target_ulong len, int flags)
+{
+ target_ulong end;
+
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (end == start)
+ return 0;
+
+ start &= qemu_host_page_mask;
+ return msync(g2h(start), end - start, flags);
+}
+
diff --git a/linux-user/path.c b/linux-user/path.c
new file mode 100644
index 0000000..7680970
--- /dev/null
+++ b/linux-user/path.c
@@ -0,0 +1,147 @@
+/* Code to mangle pathnames into those matching a given prefix.
+ eg. open("/lib/foo.so") => open("/usr/gnemul/i386-linux/lib/foo.so");
+
+ The assumption is that this area does not change.
+*/
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "qemu.h"
+
+struct pathelem
+{
+ /* Name of this, eg. lib */
+ char *name;
+ /* Full path name, eg. /usr/gnemul/x86-linux/lib. */
+ char *pathname;
+ struct pathelem *parent;
+ /* Children */
+ unsigned int num_entries;
+ struct pathelem *entries[0];
+};
+
+static struct pathelem *base;
+
+/* First N chars of S1 match S2, and S2 is N chars long. */
+static int strneq(const char *s1, unsigned int n, const char *s2)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ if (s1[i] != s2[i])
+ return 0;
+ return s2[i] == 0;
+}
+
+static struct pathelem *add_entry(struct pathelem *root, const char *name);
+
+static struct pathelem *new_entry(const char *root,
+ struct pathelem *parent,
+ const char *name)
+{
+ struct pathelem *new = malloc(sizeof(*new));
+ new->name = strdup(name);
+ asprintf(&new->pathname, "%s/%s", root, name);
+ new->num_entries = 0;
+ return new;
+}
+
+#define streq(a,b) (strcmp((a), (b)) == 0)
+
+static struct pathelem *add_dir_maybe(struct pathelem *path)
+{
+ DIR *dir;
+
+ if ((dir = opendir(path->pathname)) != NULL) {
+ struct dirent *dirent;
+
+ while ((dirent = readdir(dir)) != NULL) {
+ if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
+ path = add_entry(path, dirent->d_name);
+ }
+ }
+ closedir(dir);
+ }
+ return path;
+}
+
+static struct pathelem *add_entry(struct pathelem *root, const char *name)
+{
+ root->num_entries++;
+
+ root = realloc(root, sizeof(*root)
+ + sizeof(root->entries[0])*root->num_entries);
+
+ root->entries[root->num_entries-1] = new_entry(root->pathname, root, name);
+ root->entries[root->num_entries-1]
+ = add_dir_maybe(root->entries[root->num_entries-1]);
+ return root;
+}
+
+/* This needs to be done after tree is stabalized (ie. no more reallocs!). */
+static void set_parents(struct pathelem *child, struct pathelem *parent)
+{
+ unsigned int i;
+
+ child->parent = parent;
+ for (i = 0; i < child->num_entries; i++)
+ set_parents(child->entries[i], child);
+}
+
+void init_paths(const char *prefix)
+{
+ if (prefix[0] != '/' ||
+ prefix[0] == '\0' ||
+ !strcmp(prefix, "/"))
+ return;
+
+ base = new_entry("", NULL, prefix+1);
+ base = add_dir_maybe(base);
+ if (base->num_entries == 0) {
+ free (base);
+ base = NULL;
+ } else {
+ set_parents(base, base);
+ }
+}
+
+/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
+static const char *
+follow_path(const struct pathelem *cursor, const char *name)
+{
+ unsigned int i, namelen;
+
+ name += strspn(name, "/");
+ namelen = strcspn(name, "/");
+
+ if (namelen == 0)
+ return cursor->pathname;
+
+ if (strneq(name, namelen, ".."))
+ return follow_path(cursor->parent, name + namelen);
+
+ if (strneq(name, namelen, "."))
+ return follow_path(cursor, name + namelen);
+
+ for (i = 0; i < cursor->num_entries; i++)
+ if (strneq(name, namelen, cursor->entries[i]->name))
+ return follow_path(cursor->entries[i], name + namelen);
+
+ /* Not found */
+ return NULL;
+}
+
+/* Look for path in emulation dir, otherwise return name. */
+const char *path(const char *name)
+{
+ /* Only do absolute paths: quick and dirty, but should mostly be OK.
+ Could do relative by tracking cwd. */
+ if (!base || name[0] != '/')
+ return name;
+
+ return follow_path(base, name) ?: name;
+}
diff --git a/linux-user/ppc/syscall.h b/linux-user/ppc/syscall.h
new file mode 100644
index 0000000..eea8a7c
--- /dev/null
+++ b/linux-user/ppc/syscall.h
@@ -0,0 +1,130 @@
+/*
+ * PPC emulation for qemu: syscall definitions.
+ *
+ * Copyright (c) 2003 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* XXX: ABSOLUTELY BUGGY:
+ * for now, this is quite just a cut-and-paste from i386 target...
+ */
+
+/* default linux values for the selectors */
+#define __USER_DS (1)
+
+struct target_pt_regs {
+ unsigned long gpr[32];
+ unsigned long nip;
+ unsigned long msr;
+ unsigned long orig_gpr3; /* Used for restarting system calls */
+ unsigned long ctr;
+ unsigned long link;
+ unsigned long xer;
+ unsigned long ccr;
+ unsigned long mq; /* 601 only (not used at present) */
+ /* Used on APUS to hold IPL value. */
+ unsigned long trap; /* Reason for being here */
+ unsigned long dar; /* Fault registers */
+ unsigned long dsisr;
+ unsigned long result; /* Result of a system call */
+};
+
+/* ioctls */
+struct target_revectored_struct {
+ target_ulong __map[8]; /* 256 bits */
+};
+
+/*
+ * flags masks
+ */
+
+/* ipcs */
+
+#define TARGET_SEMOP 1
+#define TARGET_SEMGET 2
+#define TARGET_SEMCTL 3
+#define TARGET_MSGSND 11
+#define TARGET_MSGRCV 12
+#define TARGET_MSGGET 13
+#define TARGET_MSGCTL 14
+#define TARGET_SHMAT 21
+#define TARGET_SHMDT 22
+#define TARGET_SHMGET 23
+#define TARGET_SHMCTL 24
+
+struct target_msgbuf {
+ int mtype;
+ char mtext[1];
+};
+
+struct target_ipc_kludge {
+ unsigned int msgp; /* Really (struct msgbuf *) */
+ int msgtyp;
+};
+
+struct target_ipc_perm {
+ int key;
+ unsigned short uid;
+ unsigned short gid;
+ unsigned short cuid;
+ unsigned short cgid;
+ unsigned short mode;
+ unsigned short seq;
+};
+
+struct target_msqid_ds {
+ struct target_ipc_perm msg_perm;
+ unsigned int msg_first; /* really struct target_msg* */
+ unsigned int msg_last; /* really struct target_msg* */
+ unsigned int msg_stime; /* really target_time_t */
+ unsigned int msg_rtime; /* really target_time_t */
+ unsigned int msg_ctime; /* really target_time_t */
+ unsigned int wwait; /* really struct wait_queue* */
+ unsigned int rwait; /* really struct wait_queue* */
+ unsigned short msg_cbytes;
+ unsigned short msg_qnum;
+ unsigned short msg_qbytes;
+ unsigned short msg_lspid;
+ unsigned short msg_lrpid;
+};
+
+struct target_shmid_ds {
+ struct target_ipc_perm shm_perm;
+ int shm_segsz;
+ unsigned int shm_atime; /* really target_time_t */
+ unsigned int shm_dtime; /* really target_time_t */
+ unsigned int shm_ctime; /* really target_time_t */
+ unsigned short shm_cpid;
+ unsigned short shm_lpid;
+ short shm_nattch;
+ unsigned short shm_npages;
+ unsigned long *shm_pages;
+ void *attaches; /* really struct shm_desc * */
+};
+
+#define TARGET_IPC_RMID 0
+#define TARGET_IPC_SET 1
+#define TARGET_IPC_STAT 2
+
+union target_semun {
+ int val;
+ unsigned int buf; /* really struct semid_ds * */
+ unsigned int array; /* really unsigned short * */
+ unsigned int __buf; /* really struct seminfo * */
+ unsigned int __pad; /* really void* */
+};
+
+#define UNAME_MACHINE "ppc"
diff --git a/linux-user/ppc/syscall_nr.h b/linux-user/ppc/syscall_nr.h
new file mode 100644
index 0000000..b97189a
--- /dev/null
+++ b/linux-user/ppc/syscall_nr.h
@@ -0,0 +1,258 @@
+/*
+ * This file contains the system call numbers.
+ */
+#define TARGET_NR_restart_syscall 0
+#define TARGET_NR_exit 1
+#define TARGET_NR_fork 2
+#define TARGET_NR_read 3
+#define TARGET_NR_write 4
+#define TARGET_NR_open 5
+#define TARGET_NR_close 6
+#define TARGET_NR_waitpid 7
+#define TARGET_NR_creat 8
+#define TARGET_NR_link 9
+#define TARGET_NR_unlink 10
+#define TARGET_NR_execve 11
+#define TARGET_NR_chdir 12
+#define TARGET_NR_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown32 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid32 23
+#define TARGET_NR_getuid32 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#define TARGET_NR_sync 36
+#define TARGET_NR_kill 37
+#define TARGET_NR_rename 38
+#define TARGET_NR_mkdir 39
+#define TARGET_NR_rmdir 40
+#define TARGET_NR_dup 41
+#define TARGET_NR_pipe 42
+#define TARGET_NR_times 43
+#define TARGET_NR_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid32 46
+#define TARGET_NR_getgid32 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid32 49
+#define TARGET_NR_getegid32 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#define TARGET_NR_umask 60
+#define TARGET_NR_chroot 61
+#define TARGET_NR_ustat 62
+#define TARGET_NR_dup2 63
+#define TARGET_NR_getppid 64
+#define TARGET_NR_getpgrp 65
+#define TARGET_NR_setsid 66
+#define TARGET_NR_sigaction 67
+#define TARGET_NR_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid32 70
+#define TARGET_NR_setregid32 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups32 80
+#define TARGET_NR_setgroups32 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#define TARGET_NR_readlink 85
+#define TARGET_NR_uselib 86
+#define TARGET_NR_swapon 87
+#define TARGET_NR_reboot 88
+#define TARGET_NR_readdir 89
+#define TARGET_NR_mmap 90
+#define TARGET_NR_munmap 91
+#define TARGET_NR_truncate 92
+#define TARGET_NR_ftruncate 93
+#define TARGET_NR_fchmod 94
+#define TARGET_NR_fchown32 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#define TARGET_NR_socketcall 102
+#define TARGET_NR_syslog 103
+#define TARGET_NR_setitimer 104
+#define TARGET_NR_getitimer 105
+#define TARGET_NR_stat 106
+#define TARGET_NR_lstat 107
+#define TARGET_NR_fstat 108
+#define TARGET_NR_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86 113
+#define TARGET_NR_wait4 114
+#define TARGET_NR_swapoff 115
+#define TARGET_NR_sysinfo 116
+#define TARGET_NR_ipc 117
+#define TARGET_NR_fsync 118
+#define TARGET_NR_sigreturn 119
+#define TARGET_NR_clone 120
+#define TARGET_NR_setdomainname 121
+#define TARGET_NR_uname 122
+#define TARGET_NR_modify_ldt 123
+#define TARGET_NR_adjtimex 124
+#define TARGET_NR_mprotect 125
+#define TARGET_NR_sigprocmask 126
+#define TARGET_NR_create_module 127
+#define TARGET_NR_init_module 128
+#define TARGET_NR_delete_module 129
+#define TARGET_NR_get_kernel_syms 130
+#define TARGET_NR_quotactl 131
+#define TARGET_NR_getpgid 132
+#define TARGET_NR_fchdir 133
+#define TARGET_NR_bdflush 134
+#define TARGET_NR_sysfs 135
+#define TARGET_NR_personality 136
+#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid32 138
+#define TARGET_NR_setfsgid32 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#define TARGET_NR_flock 143
+#define TARGET_NR_msync 144
+#define TARGET_NR_readv 145
+#define TARGET_NR_writev 146
+#define TARGET_NR_getsid 147
+#define TARGET_NR_fdatasync 148
+#define TARGET_NR__sysctl 149
+#define TARGET_NR_mlock 150
+#define TARGET_NR_munlock 151
+#define TARGET_NR_mlockall 152
+#define TARGET_NR_munlockall 153
+#define TARGET_NR_sched_setparam 154
+#define TARGET_NR_sched_getparam 155
+#define TARGET_NR_sched_setscheduler 156
+#define TARGET_NR_sched_getscheduler 157
+#define TARGET_NR_sched_yield 158
+#define TARGET_NR_sched_get_priority_max 159
+#define TARGET_NR_sched_get_priority_min 160
+#define TARGET_NR_sched_rr_get_interval 161
+#define TARGET_NR_nanosleep 162
+#define TARGET_NR_mremap 163
+#define TARGET_NR_setresuid32 164
+#define TARGET_NR_getresuid32 165
+#define TARGET_NR_query_module 166
+#define TARGET_NR_poll 167
+#define TARGET_NR_nfsservctl 168
+#define TARGET_NR_setresgid32 169
+#define TARGET_NR_getresgid32 170
+#define TARGET_NR_prctl 171
+#define TARGET_NR_rt_sigreturn 172
+#define TARGET_NR_rt_sigaction 173
+#define TARGET_NR_rt_sigprocmask 174
+#define TARGET_NR_rt_sigpending 175
+#define TARGET_NR_rt_sigtimedwait 176
+#define TARGET_NR_rt_sigqueueinfo 177
+#define TARGET_NR_rt_sigsuspend 178
+#define TARGET_NR_pread64 179
+#define TARGET_NR_pwrite64 180
+#define TARGET_NR_chown32 181
+#define TARGET_NR_getcwd 182
+#define TARGET_NR_capget 183
+#define TARGET_NR_capset 184
+#define TARGET_NR_sigaltstack 185
+#define TARGET_NR_sendfile 186
+#define TARGET_NR_getpmsg 187 /* some people actually want streams */
+#define TARGET_NR_putpmsg 188 /* some people actually want streams */
+#define TARGET_NR_vfork 189
+#define TARGET_NR_ugetrlimit 190 /* SuS compliant getrlimit */
+#define TARGET_NR_readahead 191
+#define TARGET_NR_mmap2 192
+#define TARGET_NR_truncate64 193
+#define TARGET_NR_ftruncate64 194
+#define TARGET_NR_stat64 195
+#define TARGET_NR_lstat64 196
+#define TARGET_NR_fstat64 197
+#define TARGET_NR_pciconfig_read 198
+#define TARGET_NR_pciconfig_write 199
+#define TARGET_NR_pciconfig_iobase 200
+#define TARGET_NR_multiplexer 201
+#define TARGET_NR_getdents64 202
+#define TARGET_NR_pivot_root 203
+#define TARGET_NR_fcntl64 204
+#define TARGET_NR_madvise 205
+#define TARGET_NR_mincore 206
+#define TARGET_NR_gettid 207
+#define TARGET_NR_tkill 208
+#define TARGET_NR_setxattr 209
+#define TARGET_NR_lsetxattr 210
+#define TARGET_NR_fsetxattr 211
+#define TARGET_NR_getxattr 212
+#define TARGET_NR_lgetxattr 213
+#define TARGET_NR_fgetxattr 214
+#define TARGET_NR_listxattr 215
+#define TARGET_NR_llistxattr 216
+#define TARGET_NR_flistxattr 217
+#define TARGET_NR_removexattr 218
+#define TARGET_NR_lremovexattr 219
+#define TARGET_NR_fremovexattr 220
+#define TARGET_NR_futex 221
+#define TARGET_NR_sched_setaffinity 222
+#define TARGET_NR_sched_getaffinity 223
+/* 224 currently unused */
+#define TARGET_NR_tuxcall 225
+#define TARGET_NR_sendfile64 226
+#define TARGET_NR_io_setup 227
+#define TARGET_NR_io_destroy 228
+#define TARGET_NR_io_getevents 229
+#define TARGET_NR_io_submit 230
+#define TARGET_NR_io_cancel 231
+#define TARGET_NR_set_tid_address 232
+#define TARGET_NR_fadvise64 233
+#define TARGET_NR_exit_group 234
+#define TARGET_NR_lookup_dcookie 235
+#define TARGET_NR_epoll_create 236
+#define TARGET_NR_epoll_ctl 237
+#define TARGET_NR_epoll_wait 238
+#define TARGET_NR_remap_file_pages 239
+#define TARGET_NR_timer_create 240
+#define TARGET_NR_timer_settime 241
+#define TARGET_NR_timer_gettime 242
+#define TARGET_NR_timer_getoverrun 243
+#define TARGET_NR_timer_delete 244
+#define TARGET_NR_clock_settime 245
+#define TARGET_NR_clock_gettime 246
+#define TARGET_NR_clock_getres 247
+#define TARGET_NR_clock_nanosleep 248
+#define TARGET_NR_swapcontext 249
+#define TARGET_NR_tgkill 250
+#define TARGET_NR_utimes 251
+#define TARGET_NR_statfs64 252
+#define TARGET_NR_fstatfs64 253
+#define TARGET_NR_fadvise64_64 254
diff --git a/linux-user/ppc/termbits.h b/linux-user/ppc/termbits.h
new file mode 100644
index 0000000..6326747
--- /dev/null
+++ b/linux-user/ppc/termbits.h
@@ -0,0 +1,235 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+ unsigned int c_ispeed; /* input speed */
+ unsigned int c_ospeed; /* output speed */
+};
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VMIN 5
+#define TARGET_VEOL 6
+#define TARGET_VTIME 7
+#define TARGET_VEOL2 8
+#define TARGET_VSWTC 9
+
+#define TARGET_VWERASE 10
+#define TARGET_VREPRINT 11
+#define TARGET_VSUSP 12
+#define TARGET_VSTART 13
+#define TARGET_VSTOP 14
+#define TARGET_VLNEXT 15
+#define TARGET_VDISCARD 16
+
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IXON 0001000
+#define TARGET_IXOFF 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IUCLC 0010000
+#define TARGET_IMAXBEL 0020000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_ONLCR 0000002
+#define TARGET_OLCUC 0000004
+
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+
+#define TARGET_OFILL 00000100
+#define TARGET_OFDEL 00000200
+#define TARGET_NLDLY 00001400
+#define TARGET_NL0 00000000
+#define TARGET_NL1 00000400
+#define TARGET_NL2 00001000
+#define TARGET_NL3 00001400
+#define TARGET_TABDLY 00006000
+#define TARGET_TAB0 00000000
+#define TARGET_TAB1 00002000
+#define TARGET_TAB2 00004000
+#define TARGET_TAB3 00006000
+#define TARGET_CRDLY 00030000
+#define TARGET_CR0 00000000
+#define TARGET_CR1 00010000
+#define TARGET_CR2 00020000
+#define TARGET_CR3 00030000
+#define TARGET_FFDLY 00040000
+#define TARGET_FF0 00000000
+#define TARGET_FF1 00040000
+#define TARGET_BSDLY 00100000
+#define TARGET_BS0 00000000
+#define TARGET_BS1 00100000
+#define TARGET_VTDLY 00200000
+#define TARGET_VT0 00000000
+#define TARGET_VT1 00200000
+#define TARGET_XTABS 01000000 /* Hmm.. Linux/i386 considers this part of TABDLY.. */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0000377
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CBAUDEX 0000000
+#define TARGET_B57600 00020
+#define TARGET_B115200 00021
+#define TARGET_B230400 00022
+#define TARGET_B460800 00023
+#define TARGET_B500000 00024
+#define TARGET_B576000 00025
+#define TARGET_B921600 00026
+#define TARGET_B1000000 00027
+#define TARGET_B1152000 00030
+#define TARGET_B1500000 00031
+#define TARGET_B2000000 00032
+#define TARGET_B2500000 00033
+#define TARGET_B3000000 00034
+#define TARGET_B3500000 00035
+#define TARGET_B4000000 00036
+
+#define TARGET_CSIZE 00001400
+#define TARGET_CS5 00000000
+#define TARGET_CS6 00000400
+#define TARGET_CS7 00001000
+#define TARGET_CS8 00001400
+
+#define TARGET_CSTOPB 00002000
+#define TARGET_CREAD 00004000
+#define TARGET_PARENB 00010000
+#define TARGET_PARODD 00020000
+#define TARGET_HUPCL 00040000
+
+#define TARGET_CLOCAL 00100000
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000080
+#define TARGET_ICANON 0x00000100
+#define TARGET_XCASE 0x00004000
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000002
+#define TARGET_ECHOK 0x00000004
+#define TARGET_ECHONL 0x00000010
+#define TARGET_NOFLSH 0x80000000
+#define TARGET_TOSTOP 0x00400000
+#define TARGET_ECHOCTL 0x00000040
+#define TARGET_ECHOPRT 0x00000020
+#define TARGET_ECHOKE 0x00000001
+#define TARGET_FLUSHO 0x00800000
+#define TARGET_PENDIN 0x20000000
+#define TARGET_IEXTEN 0x00000400
+
+/* ioctls */
+
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+//#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t)
+
+#define TARGET_TCGETS TARGET_IOR('t', 19, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('t', 20, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('t', 21, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('t', 22, struct target_termios)
+
+#define TARGET_TCGETA TARGET_IOR('t', 23, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('t', 24, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('t', 25, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('t', 28, struct target_termio)
+
+#define TARGET_TCSBRK TARGET_IO('t', 29)
+#define TARGET_TCXONC TARGET_IO('t', 30)
+#define TARGET_TCFLSH TARGET_IO('t', 31)
+
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct target_winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct target_winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCGLTC TARGET_IOR('t', 116, struct target_ltchars)
+#define TARGET_TIOCSLTC TARGET_IOW('t', 117, struct target_ltchars)
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+
+#define TARGET_TIOCSTI 0x5412
+#define TARGET_TIOCMGET 0x5415
+#define TARGET_TIOCMBIS 0x5416
+#define TARGET_TIOCMBIC 0x5417
+#define TARGET_TIOCMSET 0x5418
+
+#define TARGET_TIOCGSOFTCAR 0x5419
+#define TARGET_TIOCSSOFTCAR 0x541A
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+
+#define TARGET_TIOCNOTTY 0x5422
+#define TARGET_TIOCSETD 0x5423
+#define TARGET_TIOCGETD 0x5424
+#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
new file mode 100644
index 0000000..218e846
--- /dev/null
+++ b/linux-user/qemu.h
@@ -0,0 +1,308 @@
+#ifndef QEMU_H
+#define QEMU_H
+
+#include "thunk.h"
+
+#include <signal.h>
+#include <string.h>
+#include "syscall_defs.h"
+
+#include "cpu.h"
+#include "syscall.h"
+#include "gdbstub.h"
+
+/* This struct is used to hold certain information about the image.
+ * Basically, it replicates in user space what would be certain
+ * task_struct fields in the kernel
+ */
+struct image_info {
+ unsigned long start_code;
+ unsigned long end_code;
+ unsigned long start_data;
+ unsigned long end_data;
+ unsigned long start_brk;
+ unsigned long brk;
+ unsigned long start_mmap;
+ unsigned long mmap;
+ unsigned long rss;
+ unsigned long start_stack;
+ unsigned long entry;
+ target_ulong code_offset;
+ target_ulong data_offset;
+ int personality;
+};
+
+#ifdef TARGET_I386
+/* Information about the current linux thread */
+struct vm86_saved_state {
+ uint32_t eax; /* return code */
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t eflags;
+ uint32_t eip;
+ uint16_t cs, ss, ds, es, fs, gs;
+};
+#endif
+
+#ifdef TARGET_ARM
+/* FPU emulator */
+#include "nwfpe/fpa11.h"
+#endif
+
+/* NOTE: we force a big alignment so that the stack stored after is
+ aligned too */
+typedef struct TaskState {
+ struct TaskState *next;
+#ifdef TARGET_ARM
+ /* FPA state */
+ FPA11 fpa;
+ /* Extra fields for semihosted binaries. */
+ uint32_t stack_base;
+ uint32_t heap_base;
+ uint32_t heap_limit;
+ int swi_errno;
+#endif
+#ifdef TARGET_I386
+ target_ulong target_v86;
+ struct vm86_saved_state vm86_saved_regs;
+ struct target_vm86plus_struct vm86plus;
+ uint32_t v86flags;
+ uint32_t v86mask;
+#endif
+ int used; /* non zero if used */
+ struct image_info *info;
+ uint8_t stack[0];
+} __attribute__((aligned(16))) TaskState;
+
+extern TaskState *first_task_state;
+extern const char *qemu_uname_release;
+
+/* ??? See if we can avoid exposing so much of the loader internals. */
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB w/4KB pages!
+ */
+#define MAX_ARG_PAGES 32
+
+/*
+ * This structure is used to hold the arguments that are
+ * used when loading binaries.
+ */
+struct linux_binprm {
+ char buf[128];
+ void *page[MAX_ARG_PAGES];
+ unsigned long p;
+ int fd;
+ int e_uid, e_gid;
+ int argc, envc;
+ char **argv;
+ char **envp;
+ char * filename; /* Name of binary */
+};
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
+target_ulong loader_build_argptr(int envc, int argc, target_ulong sp,
+ target_ulong stringp, int push_ptr);
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop);
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+
+void memcpy_to_target(target_ulong dest, const void *src,
+ unsigned long len);
+void target_set_brk(target_ulong new_brk);
+long do_brk(target_ulong new_brk);
+void syscall_init(void);
+long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
+ long arg4, long arg5, long arg6);
+void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
+extern CPUState *global_env;
+void cpu_loop(CPUState *env);
+void init_paths(const char *prefix);
+const char *path(const char *pathname);
+
+extern int loglevel;
+extern FILE *logfile;
+
+/* signal.c */
+void process_pending_signals(void *cpu_env);
+void signal_init(void);
+int queue_signal(int sig, target_siginfo_t *info);
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
+long do_sigreturn(CPUState *env);
+long do_rt_sigreturn(CPUState *env);
+
+#ifdef TARGET_I386
+/* vm86.c */
+void save_v86_state(CPUX86State *env);
+void handle_vm86_trap(CPUX86State *env, int trapno);
+void handle_vm86_fault(CPUX86State *env);
+int do_vm86(CPUX86State *env, long subfunction, target_ulong v86_addr);
+#endif
+
+/* mmap.c */
+int target_mprotect(target_ulong start, target_ulong len, int prot);
+long target_mmap(target_ulong start, target_ulong len, int prot,
+ int flags, int fd, target_ulong offset);
+int target_munmap(target_ulong start, target_ulong len);
+long target_mremap(target_ulong old_addr, target_ulong old_size,
+ target_ulong new_size, unsigned long flags,
+ target_ulong new_addr);
+int target_msync(target_ulong start, target_ulong len, int flags);
+
+/* user access */
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+#define access_ok(type,addr,size) (1)
+
+/* NOTE get_user and put_user use host addresses. */
+#define __put_user(x,ptr)\
+({\
+ int size = sizeof(*ptr);\
+ switch(size) {\
+ case 1:\
+ *(uint8_t *)(ptr) = (typeof(*ptr))(x);\
+ break;\
+ case 2:\
+ *(uint16_t *)(ptr) = tswap16((typeof(*ptr))(x));\
+ break;\
+ case 4:\
+ *(uint32_t *)(ptr) = tswap32((typeof(*ptr))(x));\
+ break;\
+ case 8:\
+ *(uint64_t *)(ptr) = tswap64((typeof(*ptr))(x));\
+ break;\
+ default:\
+ abort();\
+ }\
+ 0;\
+})
+
+#define __get_user(x, ptr) \
+({\
+ int size = sizeof(*ptr);\
+ switch(size) {\
+ case 1:\
+ x = (typeof(*ptr))*(uint8_t *)(ptr);\
+ break;\
+ case 2:\
+ x = (typeof(*ptr))tswap16(*(uint16_t *)(ptr));\
+ break;\
+ case 4:\
+ x = (typeof(*ptr))tswap32(*(uint32_t *)(ptr));\
+ break;\
+ case 8:\
+ x = (typeof(*ptr))tswap64(*(uint64_t *)(ptr));\
+ break;\
+ default:\
+ abort();\
+ }\
+ 0;\
+})
+
+#define put_user(x,ptr)\
+({\
+ int __ret;\
+ if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))\
+ __ret = __put_user(x, ptr);\
+ else\
+ __ret = -EFAULT;\
+ __ret;\
+})
+
+#define get_user(x,ptr)\
+({\
+ int __ret;\
+ if (access_ok(VERIFY_READ, ptr, sizeof(*ptr)))\
+ __ret = __get_user(x, ptr);\
+ else\
+ __ret = -EFAULT;\
+ __ret;\
+})
+
+/* Functions for accessing guest memory. The tget and tput functions
+ read/write single values, byteswapping as neccessary. The lock_user
+ gets a pointer to a contiguous area of guest memory, but does not perform
+ and byteswapping. lock_user may return either a pointer to the guest
+ memory, or a temporary buffer. */
+
+/* Lock an area of guest memory into the host. If copy is true then the
+ host area will have the same contents as the guest. */
+static inline void *lock_user(target_ulong guest_addr, long len, int copy)
+{
+#ifdef DEBUG_REMAP
+ void *addr;
+ addr = malloc(len);
+ if (copy)
+ memcpy(addr, g2h(guest_addr), len);
+ else
+ memset(addr, 0, len);
+ return addr;
+#else
+ return g2h(guest_addr);
+#endif
+}
+
+/* Unlock an area of guest memory. The first LEN bytes must be flushed back
+ to guest memory. */
+static inline void unlock_user(void *host_addr, target_ulong guest_addr,
+ long len)
+{
+#ifdef DEBUG_REMAP
+ if (host_addr == g2h(guest_addr))
+ return;
+ if (len > 0)
+ memcpy(g2h(guest_addr), host_addr, len);
+ free(host_addr);
+#endif
+}
+
+/* Return the length of a string in target memory. */
+static inline int target_strlen(target_ulong ptr)
+{
+ return strlen(g2h(ptr));
+}
+
+/* Like lock_user but for null terminated strings. */
+static inline void *lock_user_string(target_ulong guest_addr)
+{
+ long len;
+ len = target_strlen(guest_addr) + 1;
+ return lock_user(guest_addr, len, 1);
+}
+
+/* Helper macros for locking/ulocking a target struct. */
+#define lock_user_struct(host_ptr, guest_addr, copy) \
+ host_ptr = lock_user(guest_addr, sizeof(*host_ptr), copy)
+#define unlock_user_struct(host_ptr, guest_addr, copy) \
+ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0)
+
+#define tget8(addr) ldub(addr)
+#define tput8(addr, val) stb(addr, val)
+#define tget16(addr) lduw(addr)
+#define tput16(addr, val) stw(addr, val)
+#define tget32(addr) ldl(addr)
+#define tput32(addr, val) stl(addr, val)
+#define tget64(addr) ldq(addr)
+#define tput64(addr, val) stq(addr, val)
+#if TARGET_LONG_BITS == 64
+#define tgetl(addr) ldq(addr)
+#define tputl(addr, val) stq(addr, val)
+#else
+#define tgetl(addr) ldl(addr)
+#define tputl(addr, val) stl(addr, val)
+#endif
+
+#endif /* QEMU_H */
diff --git a/linux-user/sh4/syscall.h b/linux-user/sh4/syscall.h
new file mode 100644
index 0000000..014bf58
--- /dev/null
+++ b/linux-user/sh4/syscall.h
@@ -0,0 +1,12 @@
+struct target_pt_regs {
+ unsigned long regs[16];
+ unsigned long pc;
+ unsigned long pr;
+ unsigned long sr;
+ unsigned long gbr;
+ unsigned long mach;
+ unsigned long macl;
+ long tra;
+};
+
+#define UNAME_MACHINE "sh4"
diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h
new file mode 100644
index 0000000..c91ba1b
--- /dev/null
+++ b/linux-user/sh4/syscall_nr.h
@@ -0,0 +1,292 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall 0
+#define TARGET_NR_exit 1
+#define TARGET_NR_fork 2
+#define TARGET_NR_read 3
+#define TARGET_NR_write 4
+#define TARGET_NR_open 5
+#define TARGET_NR_close 6
+#define TARGET_NR_waitpid 7
+#define TARGET_NR_creat 8
+#define TARGET_NR_link 9
+#define TARGET_NR_unlink 10
+#define TARGET_NR_execve 11
+#define TARGET_NR_chdir 12
+#define TARGET_NR_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#define TARGET_NR_sync 36
+#define TARGET_NR_kill 37
+#define TARGET_NR_rename 38
+#define TARGET_NR_mkdir 39
+#define TARGET_NR_rmdir 40
+#define TARGET_NR_dup 41
+#define TARGET_NR_pipe 42
+#define TARGET_NR_times 43
+#define TARGET_NR_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#define TARGET_NR_umask 60
+#define TARGET_NR_chroot 61
+#define TARGET_NR_ustat 62
+#define TARGET_NR_dup2 63
+#define TARGET_NR_getppid 64
+#define TARGET_NR_getpgrp 65
+#define TARGET_NR_setsid 66
+#define TARGET_NR_sigaction 67
+#define TARGET_NR_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#define TARGET_NR_readlink 85
+#define TARGET_NR_uselib 86
+#define TARGET_NR_swapon 87
+#define TARGET_NR_reboot 88
+#define TARGET_NR_readdir 89
+#define TARGET_NR_mmap 90
+#define TARGET_NR_munmap 91
+#define TARGET_NR_truncate 92
+#define TARGET_NR_ftruncate 93
+#define TARGET_NR_fchmod 94
+#define TARGET_NR_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#define TARGET_NR_socketcall 102
+#define TARGET_NR_syslog 103
+#define TARGET_NR_setitimer 104
+#define TARGET_NR_getitimer 105
+#define TARGET_NR_stat 106
+#define TARGET_NR_lstat 107
+#define TARGET_NR_fstat 108
+#define TARGET_NR_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86old 113
+#define TARGET_NR_wait4 114
+#define TARGET_NR_swapoff 115
+#define TARGET_NR_sysinfo 116
+#define TARGET_NR_ipc 117
+#define TARGET_NR_fsync 118
+#define TARGET_NR_sigreturn 119
+#define TARGET_NR_clone 120
+#define TARGET_NR_setdomainname 121
+#define TARGET_NR_uname 122
+#define TARGET_NR_modify_ldt 123
+#define TARGET_NR_adjtimex 124
+#define TARGET_NR_mprotect 125
+#define TARGET_NR_sigprocmask 126
+#define TARGET_NR_create_module 127
+#define TARGET_NR_init_module 128
+#define TARGET_NR_delete_module 129
+#define TARGET_NR_get_kernel_syms 130
+#define TARGET_NR_quotactl 131
+#define TARGET_NR_getpgid 132
+#define TARGET_NR_fchdir 133
+#define TARGET_NR_bdflush 134
+#define TARGET_NR_sysfs 135
+#define TARGET_NR_personality 136
+#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#define TARGET_NR_flock 143
+#define TARGET_NR_msync 144
+#define TARGET_NR_readv 145
+#define TARGET_NR_writev 146
+#define TARGET_NR_getsid 147
+#define TARGET_NR_fdatasync 148
+#define TARGET_NR__sysctl 149
+#define TARGET_NR_mlock 150
+#define TARGET_NR_munlock 151
+#define TARGET_NR_mlockall 152
+#define TARGET_NR_munlockall 153
+#define TARGET_NR_sched_setparam 154
+#define TARGET_NR_sched_getparam 155
+#define TARGET_NR_sched_setscheduler 156
+#define TARGET_NR_sched_getscheduler 157
+#define TARGET_NR_sched_yield 158
+#define TARGET_NR_sched_get_priority_max 159
+#define TARGET_NR_sched_get_priority_min 160
+#define TARGET_NR_sched_rr_get_interval 161
+#define TARGET_NR_nanosleep 162
+#define TARGET_NR_mremap 163
+#define TARGET_NR_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_vm86 166
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#define TARGET_NR_prctl 172
+#define TARGET_NR_rt_sigreturn 173
+#define TARGET_NR_rt_sigaction 174
+#define TARGET_NR_rt_sigprocmask 175
+#define TARGET_NR_rt_sigpending 176
+#define TARGET_NR_rt_sigtimedwait 177
+#define TARGET_NR_rt_sigqueueinfo 178
+#define TARGET_NR_rt_sigsuspend 179
+#define TARGET_NR_pread64 180
+#define TARGET_NR_pwrite64 181
+#define TARGET_NR_chown 182
+#define TARGET_NR_getcwd 183
+#define TARGET_NR_capget 184
+#define TARGET_NR_capset 185
+#define TARGET_NR_sigaltstack 186
+#define TARGET_NR_sendfile 187
+#define TARGET_NR_streams1 188 /* some people actually want it */
+#define TARGET_NR_streams2 189 /* some people actually want it */
+#define TARGET_NR_vfork 190
+#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define TARGET_NR_mmap2 192
+#define TARGET_NR_truncate64 193
+#define TARGET_NR_ftruncate64 194
+#define TARGET_NR_stat64 195
+#define TARGET_NR_lstat64 196
+#define TARGET_NR_fstat64 197
+#define TARGET_NR_lchown32 198
+#define TARGET_NR_getuid32 199
+#define TARGET_NR_getgid32 200
+#define TARGET_NR_geteuid32 201
+#define TARGET_NR_getegid32 202
+#define TARGET_NR_setreuid32 203
+#define TARGET_NR_setregid32 204
+#define TARGET_NR_getgroups32 205
+#define TARGET_NR_setgroups32 206
+#define TARGET_NR_fchown32 207
+#define TARGET_NR_setresuid32 208
+#define TARGET_NR_getresuid32 209
+#define TARGET_NR_setresgid32 210
+#define TARGET_NR_getresgid32 211
+#define TARGET_NR_chown32 212
+#define TARGET_NR_setuid32 213
+#define TARGET_NR_setgid32 214
+#define TARGET_NR_setfsuid32 215
+#define TARGET_NR_setfsgid32 216
+#define TARGET_NR_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_fcntl64 221
+/* 223 is unused */
+#define TARGET_NR_gettid 224
+#define TARGET_NR_setxattr 226
+#define TARGET_NR_lsetxattr 227
+#define TARGET_NR_fsetxattr 228
+#define TARGET_NR_getxattr 229
+#define TARGET_NR_lgetxattr 230
+#define TARGET_NR_fgetxattr 231
+#define TARGET_NR_listxattr 232
+#define TARGET_NR_llistxattr 233
+#define TARGET_NR_flistxattr 234
+#define TARGET_NR_removexattr 235
+#define TARGET_NR_lremovexattr 236
+#define TARGET_NR_fremovexattr 237
+#define TARGET_NR_tkill 238
+#define TARGET_NR_sendfile64 239
+#define TARGET_NR_futex 240
+#define TARGET_NR_sched_setaffinity 241
+#define TARGET_NR_sched_getaffinity 242
+#define TARGET_NR_set_thread_area 243
+#define TARGET_NR_get_thread_area 244
+#define TARGET_NR_io_setup 245
+#define TARGET_NR_io_destroy 246
+#define TARGET_NR_io_getevents 247
+#define TARGET_NR_io_submit 248
+#define TARGET_NR_io_cancel 249
+#define TARGET_NR_fadvise64 250
+
+#define TARGET_NR_exit_group 252
+#define TARGET_NR_lookup_dcookie 253
+#define TARGET_NR_epoll_create 254
+#define TARGET_NR_epoll_ctl 255
+#define TARGET_NR_epoll_wait 256
+#define TARGET_NR_remap_file_pages 257
+#define TARGET_NR_set_tid_address 258
+#define TARGET_NR_timer_create 259
+#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1)
+#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3)
+#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4)
+#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5)
+#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6)
+#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8)
+#define TARGET_NR_statfs64 268
+#define TARGET_NR_fstatfs64 269
+#define TARGET_NR_tgkill 270
+#define TARGET_NR_utimes 271
+#define TARGET_NR_fadvise64_64 272
+#define TARGET_NR_vserver 273
+#define TARGET_NR_mbind 274
+#define TARGET_NR_get_mempolicy 275
+#define TARGET_NR_set_mempolicy 276
+#define TARGET_NR_mq_open 277
+#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1)
+#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3)
+#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5)
+#define TARGET_NR_sys_kexec_load 283
+#define TARGET_NR_waitid 284
+#define TARGET_NR_add_key 285
+#define TARGET_NR_request_key 286
+#define TARGET_NR_keyctl 287
+
+#define TARGET_NR_readahead 225 /* XXXXX */
diff --git a/linux-user/sh4/termbits.h b/linux-user/sh4/termbits.h
new file mode 100644
index 0000000..6dd5845
--- /dev/null
+++ b/linux-user/sh4/termbits.h
@@ -0,0 +1,274 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_cc characters */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8 0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_B500000 0010005
+#define TARGET_B576000 0010006
+#define TARGET_B921600 0010007
+#define TARGET_B1000000 0010010
+#define TARGET_B1152000 0010011
+#define TARGET_B1500000 0010012
+#define TARGET_B2000000 0010013
+#define TARGET_B2500000 0010014
+#define TARGET_B3000000 0010015
+#define TARGET_B3500000 0010016
+#define TARGET_B4000000 0010017
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* tcflow() and TCXONC use these */
+#define TARGET_TCOOFF 0
+#define TARGET_TCOON 1
+#define TARGET_TCIOFF 2
+#define TARGET_TCION 3
+
+/* tcflush() and TCFLSH use these */
+#define TARGET_TCIFLUSH 0
+#define TARGET_TCOFLUSH 1
+#define TARGET_TCIOFLUSH 2
+
+/* tcsetattr uses these */
+#define TARGET_TCSANOW 0
+#define TARGET_TCSADRAIN 1
+#define TARGET_TARGET_TCSAFLUSH 2
+
+/* ioctl */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t)
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+#define TARGET_TCGETA TARGET_IOR('t', 23, struct termio)
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+
+#define TARGET_TCSETA TARGET_IOW('t', 24, struct termio)
+#define TARGET_TCSETAW TARGET_IOW('t', 25, struct termio)
+#define TARGET_TCSETAF TARGET_IOW('t', 28, struct termio)
+#define TARGET_TCSBRK TARGET_IO('t', 29)
+#define TARGET_TCXONC TARGET_IO('t', 30)
+#define TARGET_TCFLSH TARGET_IO('t', 31)
+
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+#define TARGET_TIOCEXCL TARGET_IO('T', 12) /* 0x540C */
+#define TARGET_TIOCNXCL TARGET_IO('T', 13) /* 0x540D */
+#define TARGET_TIOCSCTTY TARGET_IO('T', 14) /* 0x540E */
+
+#define TARGET_TIOCSTI TARGET_IOW('T', 18, char) /* 0x5412 */
+#define TARGET_TIOCMGET TARGET_IOR('T', 21, unsigned int) /* 0x5415 */
+#define TARGET_TIOCMBIS TARGET_IOW('T', 22, unsigned int) /* 0x5416 */
+#define TARGET_TIOCMBIC TARGET_IOW('T', 23, unsigned int) /* 0x5417 */
+#define TARGET_TIOCMSET TARGET_IOW('T', 24, unsigned int) /* 0x5418 */
+#define TARGET_TIOCM_LE 0x001
+#define TARGET_TIOCM_DTR 0x002
+#define TARGET_TIOCM_RTS 0x004
+#define TARGET_TIOCM_ST 0x008
+#define TARGET_TIOCM_SR 0x010
+#define TARGET_TIOCM_CTS 0x020
+#define TARGET_TIOCM_CAR 0x040
+#define TARGET_TIOCM_RNG 0x080
+#define TARGET_TIOCM_DSR 0x100
+#define TARGET_TIOCM_CD TARGET_TIOCM_CAR
+#define TARGET_TIOCM_RI TARGET_TIOCM_RNG
+
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('T', 25, unsigned int) /* 0x5419 */
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('T', 26, unsigned int) /* 0x541A */
+#define TARGET_TIOCLINUX TARGET_IOW('T', 28, char) /* 0x541C */
+#define TARGET_TIOCCONS TARGET_IO('T', 29) /* 0x541D */
+#define TARGET_TIOCGSERIAL TARGET_IOR('T', 30, int) /* 0x541E */
+#define TARGET_TIOCSSERIAL TARGET_IOW('T', 31, int) /* 0x541F */
+#define TARGET_TIOCPKT TARGET_IOW('T', 32, int) /* 0x5420 */
+#define TARGET_TIOCPKT_DATA 0
+#define TARGET_TIOCPKT_FLUSHREAD 1
+#define TARGET_TIOCPKT_FLUSHWRITE 2
+#define TARGET_TIOCPKT_STOP 4
+#define TARGET_TIOCPKT_START 8
+#define TARGET_TIOCPKT_NOSTOP 16
+#define TARGET_TIOCPKT_DOSTOP 32
+
+
+#define TARGET_TIOCNOTTY TARGET_IO('T', 34) /* 0x5422 */
+#define TARGET_TIOCSETD TARGET_IOW('T', 35, int) /* 0x5423 */
+#define TARGET_TIOCGETD TARGET_IOR('T', 36, int) /* 0x5424 */
+#define TARGET_TCSBRKP TARGET_IOW('T', 37, int) /* 0x5425 */ /* Needed for POSIX tcse
+ndbreak() */
+#define TARGET_TIOCSBRK TARGET_IO('T', 39) /* 0x5427 */ /* BSD compatibility */
+#define TARGET_TIOCCBRK TARGET_IO('T', 40) /* 0x5428 */ /* BSD compatibility */
+#define TARGET_TIOCGSID TARGET_IOR('T', 41, pid_t) /* 0x5429 */ /* Return the session
+ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-m
+ux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+
+#define TARGET_TIOCSERCONFIG TARGET_IO('T', 83) /* 0x5453 */
+#define TARGET_TIOCSERGWILD TARGET_IOR('T', 84, int) /* 0x5454 */
+#define TARGET_TIOCSERSWILD TARGET_IOW('T', 85, int) /* 0x5455 */
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT TARGET_IOR('T', 88, int) /* 0x5458 */ /* For d
+ebugging only */
+#define TARGET_TIOCSERGETLSR TARGET_IOR('T', 89, unsigned int) /* 0x5459 */ /* Get line sta
+tus register */
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#define TARGET_TIOCSERGETMULTI TARGET_IOR('T', 90, int) /* 0x545A
+*/ /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI TARGET_IOW('T', 91, int) /* 0x545B
+*/ /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT TARGET_IO('T', 92) /* 0x545C */ /* wait for a change on
+serial input line(s) */
+#define TARGET_TIOCGICOUNT TARGET_IOR('T', 93, int) /* 0x545D */ /* read
+serial port inline interrupt counts */
diff --git a/linux-user/signal.c b/linux-user/signal.c
new file mode 100644
index 0000000..60f9eb7
--- /dev/null
+++ b/linux-user/signal.c
@@ -0,0 +1,2067 @@
+/*
+ * Emulation of Linux signals
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+
+#include "qemu.h"
+
+//#define DEBUG_SIGNAL
+
+#define MAX_SIGQUEUE_SIZE 1024
+
+struct sigqueue {
+ struct sigqueue *next;
+ target_siginfo_t info;
+};
+
+struct emulated_sigaction {
+ struct target_sigaction sa;
+ int pending; /* true if signal is pending */
+ struct sigqueue *first;
+ struct sigqueue info; /* in order to always have memory for the
+ first signal, we put it here */
+};
+
+static struct emulated_sigaction sigact_table[TARGET_NSIG];
+static struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
+static struct sigqueue *first_free; /* first free siginfo queue entry */
+static int signal_pending; /* non zero if a signal may be pending */
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc);
+
+static uint8_t host_to_target_signal_table[65] = {
+ [SIGHUP] = TARGET_SIGHUP,
+ [SIGINT] = TARGET_SIGINT,
+ [SIGQUIT] = TARGET_SIGQUIT,
+ [SIGILL] = TARGET_SIGILL,
+ [SIGTRAP] = TARGET_SIGTRAP,
+ [SIGABRT] = TARGET_SIGABRT,
+/* [SIGIOT] = TARGET_SIGIOT,*/
+ [SIGBUS] = TARGET_SIGBUS,
+ [SIGFPE] = TARGET_SIGFPE,
+ [SIGKILL] = TARGET_SIGKILL,
+ [SIGUSR1] = TARGET_SIGUSR1,
+ [SIGSEGV] = TARGET_SIGSEGV,
+ [SIGUSR2] = TARGET_SIGUSR2,
+ [SIGPIPE] = TARGET_SIGPIPE,
+ [SIGALRM] = TARGET_SIGALRM,
+ [SIGTERM] = TARGET_SIGTERM,
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = TARGET_SIGSTKFLT,
+#endif
+ [SIGCHLD] = TARGET_SIGCHLD,
+ [SIGCONT] = TARGET_SIGCONT,
+ [SIGSTOP] = TARGET_SIGSTOP,
+ [SIGTSTP] = TARGET_SIGTSTP,
+ [SIGTTIN] = TARGET_SIGTTIN,
+ [SIGTTOU] = TARGET_SIGTTOU,
+ [SIGURG] = TARGET_SIGURG,
+ [SIGXCPU] = TARGET_SIGXCPU,
+ [SIGXFSZ] = TARGET_SIGXFSZ,
+ [SIGVTALRM] = TARGET_SIGVTALRM,
+ [SIGPROF] = TARGET_SIGPROF,
+ [SIGWINCH] = TARGET_SIGWINCH,
+ [SIGIO] = TARGET_SIGIO,
+ [SIGPWR] = TARGET_SIGPWR,
+ [SIGSYS] = TARGET_SIGSYS,
+ /* next signals stay the same */
+};
+static uint8_t target_to_host_signal_table[65];
+
+static inline int host_to_target_signal(int sig)
+{
+ return host_to_target_signal_table[sig];
+}
+
+static inline int target_to_host_signal(int sig)
+{
+ return target_to_host_signal_table[sig];
+}
+
+static void host_to_target_sigset_internal(target_sigset_t *d,
+ const sigset_t *s)
+{
+ int i;
+ unsigned long sigmask;
+ uint32_t target_sigmask;
+
+ sigmask = ((unsigned long *)s)[0];
+ target_sigmask = 0;
+ for(i = 0; i < 32; i++) {
+ if (sigmask & (1 << i))
+ target_sigmask |= 1 << (host_to_target_signal(i + 1) - 1);
+ }
+#if TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 32
+ d->sig[0] = target_sigmask;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++) {
+ d->sig[i] = ((unsigned long *)s)[i];
+ }
+#elif TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 64 && TARGET_NSIG_WORDS == 2
+ d->sig[0] = target_sigmask;
+ d->sig[1] = sigmask >> 32;
+#else
+#warning host_to_target_sigset
+#endif
+}
+
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s)
+{
+ target_sigset_t d1;
+ int i;
+
+ host_to_target_sigset_internal(&d1, s);
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = tswapl(d1.sig[i]);
+}
+
+void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s)
+{
+ int i;
+ unsigned long sigmask;
+ target_ulong target_sigmask;
+
+ target_sigmask = s->sig[0];
+ sigmask = 0;
+ for(i = 0; i < 32; i++) {
+ if (target_sigmask & (1 << i))
+ sigmask |= 1 << (target_to_host_signal(i + 1) - 1);
+ }
+#if TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 32
+ ((unsigned long *)d)[0] = sigmask;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++) {
+ ((unsigned long *)d)[i] = s->sig[i];
+ }
+#elif TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 64 && TARGET_NSIG_WORDS == 2
+ ((unsigned long *)d)[0] = sigmask | ((unsigned long)(s->sig[1]) << 32);
+#else
+#warning target_to_host_sigset
+#endif /* TARGET_LONG_BITS */
+}
+
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s)
+{
+ target_sigset_t s1;
+ int i;
+
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ s1.sig[i] = tswapl(s->sig[i]);
+ target_to_host_sigset_internal(d, &s1);
+}
+
+void host_to_target_old_sigset(target_ulong *old_sigset,
+ const sigset_t *sigset)
+{
+ target_sigset_t d;
+ host_to_target_sigset(&d, sigset);
+ *old_sigset = d.sig[0];
+}
+
+void target_to_host_old_sigset(sigset_t *sigset,
+ const target_ulong *old_sigset)
+{
+ target_sigset_t d;
+ int i;
+
+ d.sig[0] = *old_sigset;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++)
+ d.sig[i] = 0;
+ target_to_host_sigset(sigset, &d);
+}
+
+/* siginfo conversion */
+
+static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
+ const siginfo_t *info)
+{
+ int sig;
+ sig = host_to_target_signal(info->si_signo);
+ tinfo->si_signo = sig;
+ tinfo->si_errno = 0;
+ tinfo->si_code = 0;
+ if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
+ sig == SIGBUS || sig == SIGTRAP) {
+ /* should never come here, but who knows. The information for
+ the target is irrelevant */
+ tinfo->_sifields._sigfault._addr = 0;
+ } else if (sig >= TARGET_SIGRTMIN) {
+ tinfo->_sifields._rt._pid = info->si_pid;
+ tinfo->_sifields._rt._uid = info->si_uid;
+ /* XXX: potential problem if 64 bit */
+ tinfo->_sifields._rt._sigval.sival_ptr =
+ (target_ulong)info->si_value.sival_ptr;
+ }
+}
+
+static void tswap_siginfo(target_siginfo_t *tinfo,
+ const target_siginfo_t *info)
+{
+ int sig;
+ sig = info->si_signo;
+ tinfo->si_signo = tswap32(sig);
+ tinfo->si_errno = tswap32(info->si_errno);
+ tinfo->si_code = tswap32(info->si_code);
+ if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
+ sig == SIGBUS || sig == SIGTRAP) {
+ tinfo->_sifields._sigfault._addr =
+ tswapl(info->_sifields._sigfault._addr);
+ } else if (sig >= TARGET_SIGRTMIN) {
+ tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid);
+ tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid);
+ tinfo->_sifields._rt._sigval.sival_ptr =
+ tswapl(info->_sifields._rt._sigval.sival_ptr);
+ }
+}
+
+
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
+{
+ host_to_target_siginfo_noswap(tinfo, info);
+ tswap_siginfo(tinfo, tinfo);
+}
+
+/* XXX: we support only POSIX RT signals are used. */
+/* XXX: find a solution for 64 bit (additionnal malloced data is needed) */
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
+{
+ info->si_signo = tswap32(tinfo->si_signo);
+ info->si_errno = tswap32(tinfo->si_errno);
+ info->si_code = tswap32(tinfo->si_code);
+ info->si_pid = tswap32(tinfo->_sifields._rt._pid);
+ info->si_uid = tswap32(tinfo->_sifields._rt._uid);
+ info->si_value.sival_ptr =
+ (void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);
+}
+
+void signal_init(void)
+{
+ struct sigaction act;
+ int i, j;
+
+ /* generate signal conversion tables */
+ for(i = 1; i <= 64; i++) {
+ if (host_to_target_signal_table[i] == 0)
+ host_to_target_signal_table[i] = i;
+ }
+ for(i = 1; i <= 64; i++) {
+ j = host_to_target_signal_table[i];
+ target_to_host_signal_table[j] = i;
+ }
+
+ /* set all host signal handlers. ALL signals are blocked during
+ the handlers to serialize them. */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = host_signal_handler;
+ for(i = 1; i < NSIG; i++) {
+ sigaction(i, &act, NULL);
+ }
+
+ memset(sigact_table, 0, sizeof(sigact_table));
+
+ first_free = &sigqueue_table[0];
+ for(i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++)
+ sigqueue_table[i].next = &sigqueue_table[i + 1];
+ sigqueue_table[MAX_SIGQUEUE_SIZE - 1].next = NULL;
+}
+
+/* signal queue handling */
+
+static inline struct sigqueue *alloc_sigqueue(void)
+{
+ struct sigqueue *q = first_free;
+ if (!q)
+ return NULL;
+ first_free = q->next;
+ return q;
+}
+
+static inline void free_sigqueue(struct sigqueue *q)
+{
+ q->next = first_free;
+ first_free = q;
+}
+
+/* abort execution with signal */
+void __attribute((noreturn)) force_sig(int sig)
+{
+ int host_sig;
+ host_sig = target_to_host_signal(sig);
+ fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
+ sig, strsignal(host_sig));
+#if 1
+ _exit(-host_sig);
+#else
+ {
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = SIG_DFL;
+ sigaction(SIGABRT, &act, NULL);
+ abort();
+ }
+#endif
+}
+
+/* queue a signal so that it will be send to the virtual CPU as soon
+ as possible */
+int queue_signal(int sig, target_siginfo_t *info)
+{
+ struct emulated_sigaction *k;
+ struct sigqueue *q, **pq;
+ target_ulong handler;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "queue_signal: sig=%d\n",
+ sig);
+#endif
+ k = &sigact_table[sig - 1];
+ handler = k->sa._sa_handler;
+ if (handler == TARGET_SIG_DFL) {
+ /* default handler : ignore some signal. The other are fatal */
+ if (sig != TARGET_SIGCHLD &&
+ sig != TARGET_SIGURG &&
+ sig != TARGET_SIGWINCH) {
+ force_sig(sig);
+ } else {
+ return 0; /* indicate ignored */
+ }
+ } else if (handler == TARGET_SIG_IGN) {
+ /* ignore signal */
+ return 0;
+ } else if (handler == TARGET_SIG_ERR) {
+ force_sig(sig);
+ } else {
+ pq = &k->first;
+ if (sig < TARGET_SIGRTMIN) {
+ /* if non real time signal, we queue exactly one signal */
+ if (!k->pending)
+ q = &k->info;
+ else
+ return 0;
+ } else {
+ if (!k->pending) {
+ /* first signal */
+ q = &k->info;
+ } else {
+ q = alloc_sigqueue();
+ if (!q)
+ return -EAGAIN;
+ while (*pq != NULL)
+ pq = &(*pq)->next;
+ }
+ }
+ *pq = q;
+ q->info = *info;
+ q->next = NULL;
+ k->pending = 1;
+ /* signal that a new signal is pending */
+ signal_pending = 1;
+ return 1; /* indicates that the signal was queued */
+ }
+}
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc)
+{
+ int sig;
+ target_siginfo_t tinfo;
+
+ /* the CPU emulator uses some host signals to detect exceptions,
+ we we forward to it some signals */
+ if (host_signum == SIGSEGV || host_signum == SIGBUS
+#if defined(TARGET_I386) && defined(USE_CODE_COPY)
+ || host_signum == SIGFPE
+#endif
+ ) {
+ if (cpu_signal_handler(host_signum, info, puc))
+ return;
+ }
+
+ /* get target signal number */
+ sig = host_to_target_signal(host_signum);
+ if (sig < 1 || sig > TARGET_NSIG)
+ return;
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "qemu: got signal %d\n", sig);
+#endif
+ host_to_target_siginfo_noswap(&tinfo, info);
+ if (queue_signal(sig, &tinfo) == 1) {
+ /* interrupt the virtual CPU as soon as possible */
+ cpu_interrupt(global_env, CPU_INTERRUPT_EXIT);
+ }
+}
+
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact)
+{
+ struct emulated_sigaction *k;
+ struct sigaction act1;
+ int host_sig;
+
+ if (sig < 1 || sig > TARGET_NSIG)
+ return -EINVAL;
+ k = &sigact_table[sig - 1];
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction sig=%d act=0x%08x, oact=0x%08x\n",
+ sig, (int)act, (int)oact);
+#endif
+ if (oact) {
+ oact->_sa_handler = tswapl(k->sa._sa_handler);
+ oact->sa_flags = tswapl(k->sa.sa_flags);
+ #if !defined(TARGET_MIPS)
+ oact->sa_restorer = tswapl(k->sa.sa_restorer);
+ #endif
+ oact->sa_mask = k->sa.sa_mask;
+ }
+ if (act) {
+ k->sa._sa_handler = tswapl(act->_sa_handler);
+ k->sa.sa_flags = tswapl(act->sa_flags);
+ #if !defined(TARGET_MIPS)
+ k->sa.sa_restorer = tswapl(act->sa_restorer);
+ #endif
+ k->sa.sa_mask = act->sa_mask;
+
+ /* we update the host linux signal state */
+ host_sig = target_to_host_signal(sig);
+ if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+ sigfillset(&act1.sa_mask);
+ act1.sa_flags = SA_SIGINFO;
+ if (k->sa.sa_flags & TARGET_SA_RESTART)
+ act1.sa_flags |= SA_RESTART;
+ /* NOTE: it is important to update the host kernel signal
+ ignore state to avoid getting unexpected interrupted
+ syscalls */
+ if (k->sa._sa_handler == TARGET_SIG_IGN) {
+ act1.sa_sigaction = (void *)SIG_IGN;
+ } else if (k->sa._sa_handler == TARGET_SIG_DFL) {
+ act1.sa_sigaction = (void *)SIG_DFL;
+ } else {
+ act1.sa_sigaction = host_signal_handler;
+ }
+ sigaction(host_sig, &act1, NULL);
+ }
+ }
+ return 0;
+}
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+static inline int copy_siginfo_to_user(target_siginfo_t *tinfo,
+ const target_siginfo_t *info)
+{
+ tswap_siginfo(tinfo, info);
+ return 0;
+}
+
+#ifdef TARGET_I386
+
+/* from the Linux kernel */
+
+struct target_fpreg {
+ uint16_t significand[4];
+ uint16_t exponent;
+};
+
+struct target_fpxreg {
+ uint16_t significand[4];
+ uint16_t exponent;
+ uint16_t padding[3];
+};
+
+struct target_xmmreg {
+ target_ulong element[4];
+};
+
+struct target_fpstate {
+ /* Regular FPU environment */
+ target_ulong cw;
+ target_ulong sw;
+ target_ulong tag;
+ target_ulong ipoff;
+ target_ulong cssel;
+ target_ulong dataoff;
+ target_ulong datasel;
+ struct target_fpreg _st[8];
+ uint16_t status;
+ uint16_t magic; /* 0xffff = regular FPU data only */
+
+ /* FXSR FPU environment */
+ target_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */
+ target_ulong mxcsr;
+ target_ulong reserved;
+ struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */
+ struct target_xmmreg _xmm[8];
+ target_ulong padding[56];
+};
+
+#define X86_FXSR_MAGIC 0x0000
+
+struct target_sigcontext {
+ uint16_t gs, __gsh;
+ uint16_t fs, __fsh;
+ uint16_t es, __esh;
+ uint16_t ds, __dsh;
+ target_ulong edi;
+ target_ulong esi;
+ target_ulong ebp;
+ target_ulong esp;
+ target_ulong ebx;
+ target_ulong edx;
+ target_ulong ecx;
+ target_ulong eax;
+ target_ulong trapno;
+ target_ulong err;
+ target_ulong eip;
+ uint16_t cs, __csh;
+ target_ulong eflags;
+ target_ulong esp_at_signal;
+ uint16_t ss, __ssh;
+ target_ulong fpstate; /* pointer */
+ target_ulong oldmask;
+ target_ulong cr2;
+};
+
+typedef struct target_sigaltstack {
+ target_ulong ss_sp;
+ int ss_flags;
+ target_ulong ss_size;
+} target_stack_t;
+
+struct target_ucontext {
+ target_ulong tuc_flags;
+ target_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+};
+
+struct sigframe
+{
+ target_ulong pretcode;
+ int sig;
+ struct target_sigcontext sc;
+ struct target_fpstate fpstate;
+ target_ulong extramask[TARGET_NSIG_WORDS-1];
+ char retcode[8];
+};
+
+struct rt_sigframe
+{
+ target_ulong pretcode;
+ int sig;
+ target_ulong pinfo;
+ target_ulong puc;
+ struct target_siginfo info;
+ struct target_ucontext uc;
+ struct target_fpstate fpstate;
+ char retcode[8];
+};
+
+/*
+ * Set up a signal frame.
+ */
+
+/* XXX: save x87 state */
+static int
+setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
+ CPUX86State *env, unsigned long mask)
+{
+ int err = 0;
+
+ err |= __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
+ err |= __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
+ err |= __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
+ err |= __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
+ err |= __put_user(env->regs[R_EDI], &sc->edi);
+ err |= __put_user(env->regs[R_ESI], &sc->esi);
+ err |= __put_user(env->regs[R_EBP], &sc->ebp);
+ err |= __put_user(env->regs[R_ESP], &sc->esp);
+ err |= __put_user(env->regs[R_EBX], &sc->ebx);
+ err |= __put_user(env->regs[R_EDX], &sc->edx);
+ err |= __put_user(env->regs[R_ECX], &sc->ecx);
+ err |= __put_user(env->regs[R_EAX], &sc->eax);
+ err |= __put_user(env->exception_index, &sc->trapno);
+ err |= __put_user(env->error_code, &sc->err);
+ err |= __put_user(env->eip, &sc->eip);
+ err |= __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
+ err |= __put_user(env->eflags, &sc->eflags);
+ err |= __put_user(env->regs[R_ESP], &sc->esp_at_signal);
+ err |= __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
+
+ cpu_x86_fsave(env, (void *)fpstate, 1);
+ fpstate->status = fpstate->sw;
+ err |= __put_user(0xffff, &fpstate->magic);
+ err |= __put_user(fpstate, &sc->fpstate);
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+ err |= __put_user(env->cr[2], &sc->cr2);
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+
+static inline void *
+get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
+{
+ unsigned long esp;
+
+ /* Default to using normal stack */
+ esp = env->regs[R_ESP];
+#if 0
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (sas_ss_flags(esp) == 0)
+ esp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* This is the legacy signal stack switching. */
+ else
+#endif
+ if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
+ !(ka->sa.sa_flags & TARGET_SA_RESTORER) &&
+ ka->sa.sa_restorer) {
+ esp = (unsigned long) ka->sa.sa_restorer;
+ }
+ return g2h((esp - frame_size) & -8ul);
+}
+
+static void setup_frame(int sig, struct emulated_sigaction *ka,
+ target_sigset_t *set, CPUX86State *env)
+{
+ struct sigframe *frame;
+ int i, err = 0;
+
+ frame = get_sigframe(ka, env, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+ err |= __put_user((/*current->exec_domain
+ && current->exec_domain->signal_invmap
+ && sig < 32
+ ? current->exec_domain->signal_invmap[sig]
+ : */ sig),
+ &frame->sig);
+ if (err)
+ goto give_sigsegv;
+
+ setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0]);
+ if (err)
+ goto give_sigsegv;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & TARGET_SA_RESTORER) {
+ err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
+ } else {
+ err |= __put_user(frame->retcode, &frame->pretcode);
+ /* This is popl %eax ; movl $,%eax ; int $0x80 */
+ err |= __put_user(0xb858, (short *)(frame->retcode+0));
+ err |= __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
+ err |= __put_user(0x80cd, (short *)(frame->retcode+6));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ env->regs[R_ESP] = h2g(frame);
+ env->eip = (unsigned long) ka->sa._sa_handler;
+
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ env->eflags &= ~TF_MASK;
+
+ return;
+
+give_sigsegv:
+ if (sig == TARGET_SIGSEGV)
+ ka->sa._sa_handler = TARGET_SIG_DFL;
+ force_sig(TARGET_SIGSEGV /* , current */);
+}
+
+static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUX86State *env)
+{
+ struct rt_sigframe *frame;
+ int i, err = 0;
+
+ frame = get_sigframe(ka, env, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= __put_user((/*current->exec_domain
+ && current->exec_domain->signal_invmap
+ && sig < 32
+ ? current->exec_domain->signal_invmap[sig]
+ : */sig),
+ &frame->sig);
+ err |= __put_user((target_ulong)&frame->info, &frame->pinfo);
+ err |= __put_user((target_ulong)&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.tuc_flags);
+ err |= __put_user(0, &frame->uc.tuc_link);
+ err |= __put_user(/*current->sas_ss_sp*/ 0,
+ &frame->uc.tuc_stack.ss_sp);
+ err |= __put_user(/* sas_ss_flags(regs->esp) */ 0,
+ &frame->uc.tuc_stack.ss_flags);
+ err |= __put_user(/* current->sas_ss_size */ 0,
+ &frame->uc.tuc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate,
+ env, set->sig[0]);
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & TARGET_SA_RESTORER) {
+ err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
+ } else {
+ err |= __put_user(frame->retcode, &frame->pretcode);
+ /* This is movl $,%eax ; int $0x80 */
+ err |= __put_user(0xb8, (char *)(frame->retcode+0));
+ err |= __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
+ err |= __put_user(0x80cd, (short *)(frame->retcode+5));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ env->regs[R_ESP] = (unsigned long) frame;
+ env->eip = (unsigned long) ka->sa._sa_handler;
+
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ env->eflags &= ~TF_MASK;
+
+ return;
+
+give_sigsegv:
+ if (sig == TARGET_SIGSEGV)
+ ka->sa._sa_handler = TARGET_SIG_DFL;
+ force_sig(TARGET_SIGSEGV /* , current */);
+}
+
+static int
+restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax)
+{
+ unsigned int err = 0;
+
+ cpu_x86_load_seg(env, R_GS, lduw(&sc->gs));
+ cpu_x86_load_seg(env, R_FS, lduw(&sc->fs));
+ cpu_x86_load_seg(env, R_ES, lduw(&sc->es));
+ cpu_x86_load_seg(env, R_DS, lduw(&sc->ds));
+
+ env->regs[R_EDI] = ldl(&sc->edi);
+ env->regs[R_ESI] = ldl(&sc->esi);
+ env->regs[R_EBP] = ldl(&sc->ebp);
+ env->regs[R_ESP] = ldl(&sc->esp);
+ env->regs[R_EBX] = ldl(&sc->ebx);
+ env->regs[R_EDX] = ldl(&sc->edx);
+ env->regs[R_ECX] = ldl(&sc->ecx);
+ env->eip = ldl(&sc->eip);
+
+ cpu_x86_load_seg(env, R_CS, lduw(&sc->cs) | 3);
+ cpu_x86_load_seg(env, R_SS, lduw(&sc->ss) | 3);
+
+ {
+ unsigned int tmpflags;
+ tmpflags = ldl(&sc->eflags);
+ env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
+ // regs->orig_eax = -1; /* disable syscall checks */
+ }
+
+ {
+ struct _fpstate * buf;
+ buf = (void *)ldl(&sc->fpstate);
+ if (buf) {
+#if 0
+ if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+ goto badframe;
+#endif
+ cpu_x86_frstor(env, (void *)buf, 1);
+ }
+ }
+
+ *peax = ldl(&sc->eax);
+ return err;
+#if 0
+badframe:
+ return 1;
+#endif
+}
+
+long do_sigreturn(CPUX86State *env)
+{
+ struct sigframe *frame = (struct sigframe *)g2h(env->regs[R_ESP] - 8);
+ target_sigset_t target_set;
+ sigset_t set;
+ int eax, i;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_sigreturn\n");
+#endif
+ /* set blocked signals */
+ if (__get_user(target_set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ /* restore registers */
+ if (restore_sigcontext(env, &frame->sc, &eax))
+ goto badframe;
+ return eax;
+
+badframe:
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUX86State *env)
+{
+ struct rt_sigframe *frame = (struct rt_sigframe *)g2h(env->regs[R_ESP] - 4);
+ sigset_t set;
+ // stack_t st;
+ int eax;
+
+#if 0
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+#endif
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax))
+ goto badframe;
+
+#if 0
+ if (__copy_from_user(&st, &frame->uc.tuc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, regs->esp);
+#endif
+ return eax;
+
+badframe:
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+#elif defined(TARGET_ARM)
+
+struct target_sigcontext {
+ target_ulong trap_no;
+ target_ulong error_code;
+ target_ulong oldmask;
+ target_ulong arm_r0;
+ target_ulong arm_r1;
+ target_ulong arm_r2;
+ target_ulong arm_r3;
+ target_ulong arm_r4;
+ target_ulong arm_r5;
+ target_ulong arm_r6;
+ target_ulong arm_r7;
+ target_ulong arm_r8;
+ target_ulong arm_r9;
+ target_ulong arm_r10;
+ target_ulong arm_fp;
+ target_ulong arm_ip;
+ target_ulong arm_sp;
+ target_ulong arm_lr;
+ target_ulong arm_pc;
+ target_ulong arm_cpsr;
+ target_ulong fault_address;
+};
+
+typedef struct target_sigaltstack {
+ target_ulong ss_sp;
+ int ss_flags;
+ target_ulong ss_size;
+} target_stack_t;
+
+struct target_ucontext {
+ target_ulong tuc_flags;
+ target_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+};
+
+struct sigframe
+{
+ struct target_sigcontext sc;
+ target_ulong extramask[TARGET_NSIG_WORDS-1];
+ target_ulong retcode;
+};
+
+struct rt_sigframe
+{
+ struct target_siginfo *pinfo;
+ void *puc;
+ struct target_siginfo info;
+ struct target_ucontext uc;
+ target_ulong retcode;
+};
+
+#define TARGET_CONFIG_CPU_32 1
+
+/*
+ * For ARM syscalls, we encode the syscall number into the instruction.
+ */
+#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE))
+#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE))
+
+/*
+ * For Thumb syscalls, we pass the syscall number via r7. We therefore
+ * need two 16-bit instructions.
+ */
+#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn))
+#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn))
+
+static const target_ulong retcodes[4] = {
+ SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
+ SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN
+};
+
+
+#define __put_user_error(x,p,e) __put_user(x, p)
+#define __get_user_error(x,p,e) __get_user(x, p)
+
+static inline int valid_user_regs(CPUState *regs)
+{
+ return 1;
+}
+
+static int
+setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
+ CPUState *env, unsigned long mask)
+{
+ int err = 0;
+
+ __put_user_error(env->regs[0], &sc->arm_r0, err);
+ __put_user_error(env->regs[1], &sc->arm_r1, err);
+ __put_user_error(env->regs[2], &sc->arm_r2, err);
+ __put_user_error(env->regs[3], &sc->arm_r3, err);
+ __put_user_error(env->regs[4], &sc->arm_r4, err);
+ __put_user_error(env->regs[5], &sc->arm_r5, err);
+ __put_user_error(env->regs[6], &sc->arm_r6, err);
+ __put_user_error(env->regs[7], &sc->arm_r7, err);
+ __put_user_error(env->regs[8], &sc->arm_r8, err);
+ __put_user_error(env->regs[9], &sc->arm_r9, err);
+ __put_user_error(env->regs[10], &sc->arm_r10, err);
+ __put_user_error(env->regs[11], &sc->arm_fp, err);
+ __put_user_error(env->regs[12], &sc->arm_ip, err);
+ __put_user_error(env->regs[13], &sc->arm_sp, err);
+ __put_user_error(env->regs[14], &sc->arm_lr, err);
+ __put_user_error(env->regs[15], &sc->arm_pc, err);
+#ifdef TARGET_CONFIG_CPU_32
+ __put_user_error(cpsr_read(env), &sc->arm_cpsr, err);
+#endif
+
+ __put_user_error(/* current->thread.trap_no */ 0, &sc->trap_no, err);
+ __put_user_error(/* current->thread.error_code */ 0, &sc->error_code, err);
+ __put_user_error(/* current->thread.address */ 0, &sc->fault_address, err);
+ __put_user_error(mask, &sc->oldmask, err);
+
+ return err;
+}
+
+static inline void *
+get_sigframe(struct emulated_sigaction *ka, CPUState *regs, int framesize)
+{
+ unsigned long sp = regs->regs[13];
+
+#if 0
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+#endif
+ /*
+ * ATPCS B01 mandates 8-byte alignment
+ */
+ return g2h((sp - framesize) & ~7);
+}
+
+static int
+setup_return(CPUState *env, struct emulated_sigaction *ka,
+ target_ulong *rc, void *frame, int usig)
+{
+ target_ulong handler = (target_ulong)ka->sa._sa_handler;
+ target_ulong retcode;
+ int thumb = 0;
+#if defined(TARGET_CONFIG_CPU_32)
+#if 0
+ target_ulong cpsr = env->cpsr;
+
+ /*
+ * Maybe we need to deliver a 32-bit signal to a 26-bit task.
+ */
+ if (ka->sa.sa_flags & SA_THIRTYTWO)
+ cpsr = (cpsr & ~MODE_MASK) | USR_MODE;
+
+#ifdef CONFIG_ARM_THUMB
+ if (elf_hwcap & HWCAP_THUMB) {
+ /*
+ * The LSB of the handler determines if we're going to
+ * be using THUMB or ARM mode for this signal handler.
+ */
+ thumb = handler & 1;
+
+ if (thumb)
+ cpsr |= T_BIT;
+ else
+ cpsr &= ~T_BIT;
+ }
+#endif
+#endif
+#endif /* TARGET_CONFIG_CPU_32 */
+
+ if (ka->sa.sa_flags & TARGET_SA_RESTORER) {
+ retcode = (target_ulong)ka->sa.sa_restorer;
+ } else {
+ unsigned int idx = thumb;
+
+ if (ka->sa.sa_flags & TARGET_SA_SIGINFO)
+ idx += 2;
+
+ if (__put_user(retcodes[idx], rc))
+ return 1;
+#if 0
+ flush_icache_range((target_ulong)rc,
+ (target_ulong)(rc + 1));
+#endif
+ retcode = ((target_ulong)rc) + thumb;
+ }
+
+ env->regs[0] = usig;
+ env->regs[13] = h2g(frame);
+ env->regs[14] = retcode;
+ env->regs[15] = handler & (thumb ? ~1 : ~3);
+
+#if 0
+#ifdef TARGET_CONFIG_CPU_32
+ env->cpsr = cpsr;
+#endif
+#endif
+
+ return 0;
+}
+
+static void setup_frame(int usig, struct emulated_sigaction *ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct sigframe *frame = get_sigframe(ka, regs, sizeof(*frame));
+ int i, err = 0;
+
+ err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ return;
+ }
+
+ if (err == 0)
+ err = setup_return(regs, ka, &frame->retcode, frame, usig);
+ // return err;
+}
+
+static void setup_rt_frame(int usig, struct emulated_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ struct rt_sigframe *frame = get_sigframe(ka, env, sizeof(*frame));
+ int i, err = 0;
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+ return /* 1 */;
+
+ __put_user_error(&frame->info, (target_ulong *)&frame->pinfo, err);
+ __put_user_error(&frame->uc, (target_ulong *)&frame->puc, err);
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Clear all the bits of the ucontext we don't use. */
+ memset(&frame->uc, 0, offsetof(struct target_ucontext, tuc_mcontext));
+
+ err |= setup_sigcontext(&frame->uc.tuc_mcontext, /*&frame->fpstate,*/
+ env, set->sig[0]);
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]))
+ return;
+ }
+
+ if (err == 0)
+ err = setup_return(env, ka, &frame->retcode, frame, usig);
+
+ if (err == 0) {
+ /*
+ * For realtime signals we must also set the second and third
+ * arguments for the signal handler.
+ * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
+ */
+ env->regs[1] = (target_ulong)frame->pinfo;
+ env->regs[2] = (target_ulong)frame->puc;
+ }
+
+ // return err;
+}
+
+static int
+restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
+{
+ int err = 0;
+ uint32_t cpsr;
+
+ __get_user_error(env->regs[0], &sc->arm_r0, err);
+ __get_user_error(env->regs[1], &sc->arm_r1, err);
+ __get_user_error(env->regs[2], &sc->arm_r2, err);
+ __get_user_error(env->regs[3], &sc->arm_r3, err);
+ __get_user_error(env->regs[4], &sc->arm_r4, err);
+ __get_user_error(env->regs[5], &sc->arm_r5, err);
+ __get_user_error(env->regs[6], &sc->arm_r6, err);
+ __get_user_error(env->regs[7], &sc->arm_r7, err);
+ __get_user_error(env->regs[8], &sc->arm_r8, err);
+ __get_user_error(env->regs[9], &sc->arm_r9, err);
+ __get_user_error(env->regs[10], &sc->arm_r10, err);
+ __get_user_error(env->regs[11], &sc->arm_fp, err);
+ __get_user_error(env->regs[12], &sc->arm_ip, err);
+ __get_user_error(env->regs[13], &sc->arm_sp, err);
+ __get_user_error(env->regs[14], &sc->arm_lr, err);
+ __get_user_error(env->regs[15], &sc->arm_pc, err);
+#ifdef TARGET_CONFIG_CPU_32
+ __get_user_error(cpsr, &sc->arm_cpsr, err);
+ cpsr_write(env, cpsr, 0xffffffff);
+#endif
+
+ err |= !valid_user_regs(env);
+
+ return err;
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct sigframe *frame;
+ target_sigset_t set;
+ sigset_t host_set;
+ int i;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame = (struct sigframe *)g2h(env->regs[13]);
+
+#if 0
+ if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+#endif
+ if (__get_user(set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&host_set, &set);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (restore_sigcontext(env, &frame->sc))
+ goto badframe;
+
+#if 0
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt(current))
+ send_sig(SIGTRAP, current, 1);
+#endif
+ return env->regs[0];
+
+badframe:
+ force_sig(SIGSEGV /* , current */);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ struct rt_sigframe *frame;
+ sigset_t host_set;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame = (struct rt_sigframe *)env->regs[13];
+
+#if 0
+ if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+#endif
+ target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (restore_sigcontext(env, &frame->uc.tuc_mcontext))
+ goto badframe;
+
+#if 0
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt(current))
+ send_sig(SIGTRAP, current, 1);
+#endif
+ return env->regs[0];
+
+badframe:
+ force_sig(SIGSEGV /* , current */);
+ return 0;
+}
+
+#elif defined(TARGET_SPARC)
+
+#define __SUNOS_MAXWIN 31
+
+/* This is what SunOS does, so shall I. */
+struct target_sigcontext {
+ target_ulong sigc_onstack; /* state to restore */
+
+ target_ulong sigc_mask; /* sigmask to restore */
+ target_ulong sigc_sp; /* stack pointer */
+ target_ulong sigc_pc; /* program counter */
+ target_ulong sigc_npc; /* next program counter */
+ target_ulong sigc_psr; /* for condition codes etc */
+ target_ulong sigc_g1; /* User uses these two registers */
+ target_ulong sigc_o0; /* within the trampoline code. */
+
+ /* Now comes information regarding the users window set
+ * at the time of the signal.
+ */
+ target_ulong sigc_oswins; /* outstanding windows */
+
+ /* stack ptrs for each regwin buf */
+ char *sigc_spbuf[__SUNOS_MAXWIN];
+
+ /* Windows to restore after signal */
+ struct {
+ target_ulong locals[8];
+ target_ulong ins[8];
+ } sigc_wbuf[__SUNOS_MAXWIN];
+};
+/* A Sparc stack frame */
+struct sparc_stackf {
+ target_ulong locals[8];
+ target_ulong ins[6];
+ struct sparc_stackf *fp;
+ target_ulong callers_pc;
+ char *structptr;
+ target_ulong xargs[6];
+ target_ulong xxargs[1];
+};
+
+typedef struct {
+ struct {
+ target_ulong psr;
+ target_ulong pc;
+ target_ulong npc;
+ target_ulong y;
+ target_ulong u_regs[16]; /* globals and ins */
+ } si_regs;
+ int si_mask;
+} __siginfo_t;
+
+typedef struct {
+ unsigned long si_float_regs [32];
+ unsigned long si_fsr;
+ unsigned long si_fpqdepth;
+ struct {
+ unsigned long *insn_addr;
+ unsigned long insn;
+ } si_fpqueue [16];
+} qemu_siginfo_fpu_t;
+
+
+struct target_signal_frame {
+ struct sparc_stackf ss;
+ __siginfo_t info;
+ qemu_siginfo_fpu_t *fpu_save;
+ target_ulong insns[2] __attribute__ ((aligned (8)));
+ target_ulong extramask[TARGET_NSIG_WORDS - 1];
+ target_ulong extra_size; /* Should be 0 */
+ qemu_siginfo_fpu_t fpu_state;
+};
+struct target_rt_signal_frame {
+ struct sparc_stackf ss;
+ siginfo_t info;
+ target_ulong regs[20];
+ sigset_t mask;
+ qemu_siginfo_fpu_t *fpu_save;
+ unsigned int insns[2];
+ stack_t stack;
+ unsigned int extra_size; /* Should be 0 */
+ qemu_siginfo_fpu_t fpu_state;
+};
+
+#define UREG_O0 16
+#define UREG_O6 22
+#define UREG_I0 0
+#define UREG_I1 1
+#define UREG_I2 2
+#define UREG_I6 6
+#define UREG_I7 7
+#define UREG_L0 8
+#define UREG_FP UREG_I6
+#define UREG_SP UREG_O6
+
+static inline void *get_sigframe(struct emulated_sigaction *sa, CPUState *env, unsigned long framesize)
+{
+ unsigned long sp;
+
+ sp = env->regwptr[UREG_FP];
+#if 0
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (sa->sa_flags & TARGET_SA_ONSTACK) {
+ if (!on_sig_stack(sp) && !((current->sas_ss_sp + current->sas_ss_size) & 7))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+#endif
+ return g2h(sp - framesize);
+}
+
+static int
+setup___siginfo(__siginfo_t *si, CPUState *env, target_ulong mask)
+{
+ int err = 0, i;
+
+ err |= __put_user(env->psr, &si->si_regs.psr);
+ err |= __put_user(env->pc, &si->si_regs.pc);
+ err |= __put_user(env->npc, &si->si_regs.npc);
+ err |= __put_user(env->y, &si->si_regs.y);
+ for (i=0; i < 8; i++) {
+ err |= __put_user(env->gregs[i], &si->si_regs.u_regs[i]);
+ }
+ for (i=0; i < 8; i++) {
+ err |= __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]);
+ }
+ err |= __put_user(mask, &si->si_mask);
+ return err;
+}
+
+#if 0
+static int
+setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
+ CPUState *env, unsigned long mask)
+{
+ int err = 0;
+
+ err |= __put_user(mask, &sc->sigc_mask);
+ err |= __put_user(env->regwptr[UREG_SP], &sc->sigc_sp);
+ err |= __put_user(env->pc, &sc->sigc_pc);
+ err |= __put_user(env->npc, &sc->sigc_npc);
+ err |= __put_user(env->psr, &sc->sigc_psr);
+ err |= __put_user(env->gregs[1], &sc->sigc_g1);
+ err |= __put_user(env->regwptr[UREG_O0], &sc->sigc_o0);
+
+ return err;
+}
+#endif
+#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7)))
+
+static void setup_frame(int sig, struct emulated_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_signal_frame *sf;
+ int sigframe_size, err, i;
+
+ /* 1. Make sure everything is clean */
+ //synchronize_user_stack();
+
+ sigframe_size = NF_ALIGNEDSZ;
+
+ sf = (struct target_signal_frame *)
+ get_sigframe(ka, env, sigframe_size);
+
+ //fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]);
+#if 0
+ if (invalid_frame_pointer(sf, sigframe_size))
+ goto sigill_and_return;
+#endif
+ /* 2. Save the current process state */
+ err = setup___siginfo(&sf->info, env, set->sig[0]);
+ err |= __put_user(0, &sf->extra_size);
+
+ //err |= save_fpu_state(regs, &sf->fpu_state);
+ //err |= __put_user(&sf->fpu_state, &sf->fpu_save);
+
+ err |= __put_user(set->sig[0], &sf->info.si_mask);
+ for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
+ err |= __put_user(set->sig[i + 1], &sf->extramask[i]);
+ }
+
+ for (i = 0; i < 8; i++) {
+ err |= __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ err |= __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]);
+ }
+ if (err)
+ goto sigsegv;
+
+ /* 3. signal handler back-trampoline and parameters */
+ env->regwptr[UREG_FP] = h2g(sf);
+ env->regwptr[UREG_I0] = sig;
+ env->regwptr[UREG_I1] = h2g(&sf->info);
+ env->regwptr[UREG_I2] = h2g(&sf->info);
+
+ /* 4. signal handler */
+ env->pc = (unsigned long) ka->sa._sa_handler;
+ env->npc = (env->pc + 4);
+ /* 5. return to kernel instructions */
+ if (ka->sa.sa_restorer)
+ env->regwptr[UREG_I7] = (unsigned long)ka->sa.sa_restorer;
+ else {
+ env->regwptr[UREG_I7] = h2g(&(sf->insns[0]) - 2);
+
+ /* mov __NR_sigreturn, %g1 */
+ err |= __put_user(0x821020d8, &sf->insns[0]);
+
+ /* t 0x10 */
+ err |= __put_user(0x91d02010, &sf->insns[1]);
+ if (err)
+ goto sigsegv;
+
+ /* Flush instruction space. */
+ //flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+ // tb_flush(env);
+ }
+ return;
+
+ //sigill_and_return:
+ force_sig(TARGET_SIGILL);
+sigsegv:
+ //fprintf(stderr, "force_sig\n");
+ force_sig(TARGET_SIGSEGV);
+}
+static inline int
+restore_fpu_state(CPUState *env, qemu_siginfo_fpu_t *fpu)
+{
+ int err;
+#if 0
+#ifdef CONFIG_SMP
+ if (current->flags & PF_USEDFPU)
+ regs->psr &= ~PSR_EF;
+#else
+ if (current == last_task_used_math) {
+ last_task_used_math = 0;
+ regs->psr &= ~PSR_EF;
+ }
+#endif
+ current->used_math = 1;
+ current->flags &= ~PF_USEDFPU;
+#endif
+#if 0
+ if (verify_area (VERIFY_READ, fpu, sizeof(*fpu)))
+ return -EFAULT;
+#endif
+
+ err = __copy_from_user(&env->fpr[0], &fpu->si_float_regs[0],
+ (sizeof(unsigned long) * 32));
+ err |= __get_user(env->fsr, &fpu->si_fsr);
+#if 0
+ err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth);
+ if (current->thread.fpqdepth != 0)
+ err |= __copy_from_user(&current->thread.fpqueue[0],
+ &fpu->si_fpqueue[0],
+ ((sizeof(unsigned long) +
+ (sizeof(unsigned long *)))*16));
+#endif
+ return err;
+}
+
+
+static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_signal_frame *sf;
+ uint32_t up_psr, pc, npc;
+ target_sigset_t set;
+ sigset_t host_set;
+ target_ulong fpu_save;
+ int err, i;
+
+ sf = (struct target_signal_frame *)g2h(env->regwptr[UREG_FP]);
+#if 0
+ fprintf(stderr, "sigreturn\n");
+ fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]);
+#endif
+ //cpu_dump_state(env, stderr, fprintf, 0);
+
+ /* 1. Make sure we are not getting garbage from the user */
+#if 0
+ if (verify_area (VERIFY_READ, sf, sizeof (*sf)))
+ goto segv_and_exit;
+#endif
+
+ if (((uint) sf) & 3)
+ goto segv_and_exit;
+
+ err = __get_user(pc, &sf->info.si_regs.pc);
+ err |= __get_user(npc, &sf->info.si_regs.npc);
+
+ if ((pc | npc) & 3)
+ goto segv_and_exit;
+
+ /* 2. Restore the state */
+ err |= __get_user(up_psr, &sf->info.si_regs.psr);
+
+ /* User can only change condition codes and FPU enabling in %psr. */
+ env->psr = (up_psr & (PSR_ICC /* | PSR_EF */))
+ | (env->psr & ~(PSR_ICC /* | PSR_EF */));
+
+ env->pc = pc;
+ env->npc = npc;
+ err |= __get_user(env->y, &sf->info.si_regs.y);
+ for (i=0; i < 8; i++) {
+ err |= __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]);
+ }
+ for (i=0; i < 8; i++) {
+ err |= __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]);
+ }
+
+ err |= __get_user(fpu_save, (target_ulong *)&sf->fpu_save);
+
+ //if (fpu_save)
+ // err |= restore_fpu_state(env, fpu_save);
+
+ /* This is pretty much atomic, no amount locking would prevent
+ * the races which exist anyways.
+ */
+ err |= __get_user(set.sig[0], &sf->info.si_mask);
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ err |= (__get_user(set.sig[i], &sf->extramask[i - 1]));
+ }
+
+ target_to_host_sigset_internal(&host_set, &set);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (err)
+ goto segv_and_exit;
+
+ return env->regwptr[0];
+
+segv_and_exit:
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -ENOSYS;
+}
+
+#elif defined(TARGET_MIPS)
+
+struct target_sigcontext {
+ uint32_t sc_regmask; /* Unused */
+ uint32_t sc_status;
+ uint64_t sc_pc;
+ uint64_t sc_regs[32];
+ uint64_t sc_fpregs[32];
+ uint32_t sc_ownedfp; /* Unused */
+ uint32_t sc_fpc_csr;
+ uint32_t sc_fpc_eir; /* Unused */
+ uint32_t sc_used_math;
+ uint32_t sc_dsp; /* dsp status, was sc_ssflags */
+ uint64_t sc_mdhi;
+ uint64_t sc_mdlo;
+ target_ulong sc_hi1; /* Was sc_cause */
+ target_ulong sc_lo1; /* Was sc_badvaddr */
+ target_ulong sc_hi2; /* Was sc_sigset[4] */
+ target_ulong sc_lo2;
+ target_ulong sc_hi3;
+ target_ulong sc_lo3;
+};
+
+struct sigframe {
+ uint32_t sf_ass[4]; /* argument save space for o32 */
+ uint32_t sf_code[2]; /* signal trampoline */
+ struct target_sigcontext sf_sc;
+ target_sigset_t sf_mask;
+};
+
+/* Install trampoline to jump back from signal handler */
+static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
+{
+ int err;
+
+ /*
+ * Set up the return code ...
+ *
+ * li v0, __NR__foo_sigreturn
+ * syscall
+ */
+
+ err = __put_user(0x24020000 + syscall, tramp + 0);
+ err |= __put_user(0x0000000c , tramp + 1);
+ /* flush_cache_sigtramp((unsigned long) tramp); */
+ return err;
+}
+
+static inline int
+setup_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+ int err = 0;
+
+ err |= __put_user(regs->PC, &sc->sc_pc);
+
+ #define save_gp_reg(i) do { \
+ err |= __put_user(regs->gpr[i], &sc->sc_regs[i]); \
+ } while(0)
+ __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2);
+ save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6);
+ save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10);
+ save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
+ save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18);
+ save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22);
+ save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
+ save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30);
+ save_gp_reg(31);
+ #undef save_gp_reg
+
+ err |= __put_user(regs->HI, &sc->sc_mdhi);
+ err |= __put_user(regs->LO, &sc->sc_mdlo);
+
+ /* Not used yet, but might be useful if we ever have DSP suppport */
+#if 0
+ if (cpu_has_dsp) {
+ err |= __put_user(mfhi1(), &sc->sc_hi1);
+ err |= __put_user(mflo1(), &sc->sc_lo1);
+ err |= __put_user(mfhi2(), &sc->sc_hi2);
+ err |= __put_user(mflo2(), &sc->sc_lo2);
+ err |= __put_user(mfhi3(), &sc->sc_hi3);
+ err |= __put_user(mflo3(), &sc->sc_lo3);
+ err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+ }
+ /* same with 64 bit */
+ #ifdef CONFIG_64BIT
+ err |= __put_user(regs->hi, &sc->sc_hi[0]);
+ err |= __put_user(regs->lo, &sc->sc_lo[0]);
+ if (cpu_has_dsp) {
+ err |= __put_user(mfhi1(), &sc->sc_hi[1]);
+ err |= __put_user(mflo1(), &sc->sc_lo[1]);
+ err |= __put_user(mfhi2(), &sc->sc_hi[2]);
+ err |= __put_user(mflo2(), &sc->sc_lo[2]);
+ err |= __put_user(mfhi3(), &sc->sc_hi[3]);
+ err |= __put_user(mflo3(), &sc->sc_lo[3]);
+ err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+ }
+ #endif
+
+
+ #endif
+
+
+ #if 0
+ err |= __put_user(!!used_math(), &sc->sc_used_math);
+
+ if (!used_math())
+ goto out;
+
+ /*
+ * Save FPU state to signal context. Signal handler will "inherit"
+ * current FPU state.
+ */
+ preempt_disable();
+
+ if (!is_fpu_owner()) {
+ own_fpu();
+ restore_fp(current);
+ }
+ err |= save_fp_context(sc);
+
+ preempt_enable();
+ out:
+#endif
+ return err;
+}
+
+static inline int
+restore_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+ int err = 0;
+
+ err |= __get_user(regs->CP0_EPC, &sc->sc_pc);
+
+ err |= __get_user(regs->HI, &sc->sc_mdhi);
+ err |= __get_user(regs->LO, &sc->sc_mdlo);
+
+ #define restore_gp_reg(i) do { \
+ err |= __get_user(regs->gpr[i], &sc->sc_regs[i]); \
+ } while(0)
+ restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3);
+ restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6);
+ restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9);
+ restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12);
+ restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15);
+ restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18);
+ restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21);
+ restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24);
+ restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27);
+ restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30);
+ restore_gp_reg(31);
+ #undef restore_gp_reg
+
+#if 0
+ if (cpu_has_dsp) {
+ err |= __get_user(treg, &sc->sc_hi1); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg);
+ err |= __get_user(treg, &sc->sc_hi2); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg);
+ err |= __get_user(treg, &sc->sc_hi3); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg);
+ err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+ }
+ #ifdef CONFIG_64BIT
+ err |= __get_user(regs->hi, &sc->sc_hi[0]);
+ err |= __get_user(regs->lo, &sc->sc_lo[0]);
+ if (cpu_has_dsp) {
+ err |= __get_user(treg, &sc->sc_hi[1]); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_lo[1]); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_hi[2]); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_lo[2]); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_hi[3]); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_lo[3]); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+ }
+ #endif
+
+ err |= __get_user(used_math, &sc->sc_used_math);
+ conditional_used_math(used_math);
+
+ preempt_disable();
+
+ if (used_math()) {
+ /* restore fpu context if we have used it before */
+ own_fpu();
+ err |= restore_fp_context(sc);
+ } else {
+ /* signal handler may have used FPU. Give it up. */
+ lose_fpu();
+ }
+
+ preempt_enable();
+#endif
+ return err;
+}
+/*
+ * Determine which stack to use..
+ */
+static inline void *
+get_sigframe(struct emulated_sigaction *ka, CPUState *regs, size_t frame_size)
+{
+ unsigned long sp;
+
+ /* Default to using normal stack */
+ sp = regs->gpr[29];
+
+ /*
+ * FPU emulator may have it's own trampoline active just
+ * above the user stack, 16-bytes before the next lowest
+ * 16 byte boundary. Try to avoid trashing it.
+ */
+ sp -= 32;
+
+#if 0
+ /* This is the X/Open sanctioned signal stack switching. */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+#endif
+
+ return g2h((sp - frame_size) & ~7);
+}
+
+static void setup_frame(int sig, struct emulated_sigaction * ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct sigframe *frame;
+ int i;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+ goto give_sigsegv;
+
+ install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
+
+ if(setup_sigcontext(regs, &frame->sf_sc))
+ goto give_sigsegv;
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if(__put_user(set->sig[i], &frame->sf_mask.sig[i]))
+ goto give_sigsegv;
+ }
+
+ /*
+ * Arguments to signal handler:
+ *
+ * a0 = signal number
+ * a1 = 0 (should be cause)
+ * a2 = pointer to struct sigcontext
+ *
+ * $25 and PC point to the signal handler, $29 points to the
+ * struct sigframe.
+ */
+ regs->gpr[ 4] = sig;
+ regs->gpr[ 5] = 0;
+ regs->gpr[ 6] = h2g(&frame->sf_sc);
+ regs->gpr[29] = h2g(frame);
+ regs->gpr[31] = h2g(frame->sf_code);
+ /* The original kernel code sets CP0_EPC to the handler
+ * since it returns to userland using eret
+ * we cannot do this here, and we must set PC directly */
+ regs->PC = regs->gpr[25] = ka->sa._sa_handler;
+ return;
+
+give_sigsegv:
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return;
+}
+
+long do_sigreturn(CPUState *regs)
+{
+ struct sigframe *frame;
+ sigset_t blocked;
+ target_sigset_t target_set;
+ int i;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_sigreturn\n");
+#endif
+ frame = (struct sigframe *) regs->gpr[29];
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if(__get_user(target_set.sig[i], &frame->sf_mask.sig[i]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&blocked, &target_set);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (restore_sigcontext(regs, &frame->sf_sc))
+ goto badframe;
+
+#if 0
+ /*
+ * Don't let your children do this ...
+ */
+ __asm__ __volatile__(
+ "move\t$29, %0\n\t"
+ "j\tsyscall_exit"
+ :/* no outputs */
+ :"r" (&regs));
+ /* Unreached */
+#endif
+
+ regs->PC = regs->CP0_EPC;
+ /* I am not sure this is right, but it seems to work
+ * maybe a problem with nested signals ? */
+ regs->CP0_EPC = 0;
+ return 0;
+
+badframe:
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return 0;
+
+}
+
+static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -ENOSYS;
+}
+
+#else
+
+static void setup_frame(int sig, struct emulated_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_frame: not implemented\n");
+}
+
+static void setup_rt_frame(int sig, struct emulated_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_sigreturn: not implemented\n");
+ return -ENOSYS;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -ENOSYS;
+}
+
+#endif
+
+void process_pending_signals(void *cpu_env)
+{
+ int sig;
+ target_ulong handler;
+ sigset_t set, old_set;
+ target_sigset_t target_old_set;
+ struct emulated_sigaction *k;
+ struct sigqueue *q;
+
+ if (!signal_pending)
+ return;
+
+ k = sigact_table;
+ for(sig = 1; sig <= TARGET_NSIG; sig++) {
+ if (k->pending)
+ goto handle_signal;
+ k++;
+ }
+ /* if no signal is pending, just return */
+ signal_pending = 0;
+ return;
+
+ handle_signal:
+#ifdef DEBUG_SIGNAL
+ fprintf(stderr, "qemu: process signal %d\n", sig);
+#endif
+ /* dequeue signal */
+ q = k->first;
+ k->first = q->next;
+ if (!k->first)
+ k->pending = 0;
+
+ sig = gdb_handlesig (cpu_env, sig);
+ if (!sig) {
+ fprintf (stderr, "Lost signal\n");
+ abort();
+ }
+
+ handler = k->sa._sa_handler;
+ if (handler == TARGET_SIG_DFL) {
+ /* default handler : ignore some signal. The other are fatal */
+ if (sig != TARGET_SIGCHLD &&
+ sig != TARGET_SIGURG &&
+ sig != TARGET_SIGWINCH) {
+ force_sig(sig);
+ }
+ } else if (handler == TARGET_SIG_IGN) {
+ /* ignore sig */
+ } else if (handler == TARGET_SIG_ERR) {
+ force_sig(sig);
+ } else {
+ /* compute the blocked signals during the handler execution */
+ target_to_host_sigset(&set, &k->sa.sa_mask);
+ /* SA_NODEFER indicates that the current signal should not be
+ blocked during the handler */
+ if (!(k->sa.sa_flags & TARGET_SA_NODEFER))
+ sigaddset(&set, target_to_host_signal(sig));
+
+ /* block signals in the handler using Linux */
+ sigprocmask(SIG_BLOCK, &set, &old_set);
+ /* save the previous blocked signal state to restore it at the
+ end of the signal execution (see do_sigreturn) */
+ host_to_target_sigset_internal(&target_old_set, &old_set);
+
+ /* if the CPU is in VM86 mode, we restore the 32 bit values */
+#ifdef TARGET_I386
+ {
+ CPUX86State *env = cpu_env;
+ if (env->eflags & VM_MASK)
+ save_v86_state(env);
+ }
+#endif
+ /* prepare the stack frame of the virtual CPU */
+ if (k->sa.sa_flags & TARGET_SA_SIGINFO)
+ setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env);
+ else
+ setup_frame(sig, k, &target_old_set, cpu_env);
+ if (k->sa.sa_flags & TARGET_SA_RESETHAND)
+ k->sa._sa_handler = TARGET_SIG_DFL;
+ }
+ if (q != &k->info)
+ free_sigqueue(q);
+}
+
+
diff --git a/linux-user/socket.h b/linux-user/socket.h
new file mode 100644
index 0000000..f13ca45
--- /dev/null
+++ b/linux-user/socket.h
@@ -0,0 +1,138 @@
+
+#if defined(TARGET_MIPS)
+ // MIPS special values for constants
+
+ /*
+ * For setsockopt(2)
+ *
+ * This defines are ABI conformant as far as Linux supports these ...
+ */
+ #define TARGET_SOL_SOCKET 0xffff
+
+ #define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */
+ #define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */
+ #define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send
+ SIGPIPE when they die. */
+ #define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */
+ #define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of
+ broadcast messages. */
+ #define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable
+ socket to transmit pending data. */
+ #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. */
+ #if 0
+ To add: #define TARGET_SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */
+ #endif
+
+ #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */
+ #define TARGET_SO_STYLE SO_TYPE /* Synonym */
+ #define TARGET_SO_ERROR 0x1007 /* get error status and clear */
+ #define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */
+ #define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */
+ #define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */
+ #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */
+ #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */
+ #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */
+ #define TARGET_SO_ACCEPTCONN 0x1009
+
+ /* linux-specific, might as well be the same as on i386 */
+ #define TARGET_SO_NO_CHECK 11
+ #define TARGET_SO_PRIORITY 12
+ #define TARGET_SO_BSDCOMPAT 14
+
+ #define TARGET_SO_PASSCRED 17
+ #define TARGET_SO_PEERCRED 18
+
+ /* Security levels - as per NRL IPv6 - don't actually do anything */
+ #define TARGET_SO_SECURITY_AUTHENTICATION 22
+ #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+ #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24
+
+ #define TARGET_SO_BINDTODEVICE 25
+
+ /* Socket filtering */
+ #define TARGET_SO_ATTACH_FILTER 26
+ #define TARGET_SO_DETACH_FILTER 27
+
+ #define TARGET_SO_PEERNAME 28
+ #define TARGET_SO_TIMESTAMP 29
+ #define SCM_TIMESTAMP SO_TIMESTAMP
+
+ #define TARGET_SO_PEERSEC 30
+ #define TARGET_SO_SNDBUFFORCE 31
+ #define TARGET_SO_RCVBUFFORCE 33
+
+ /** sock_type - Socket types
+ *
+ * Please notice that for binary compat reasons MIPS has to
+ * override the enum sock_type in include/linux/net.h, so
+ * we define ARCH_HAS_SOCKET_TYPES here.
+ *
+ * @SOCK_DGRAM - datagram (conn.less) socket
+ * @SOCK_STREAM - stream (connection) socket
+ * @SOCK_RAW - raw socket
+ * @SOCK_RDM - reliably-delivered message
+ * @SOCK_SEQPACKET - sequential packet socket
+ * @SOCK_PACKET - linux specific way of getting packets at the dev level.
+ * For writing rarp and other similar things on the user level.
+ */
+ enum sock_type {
+ TARGET_SOCK_DGRAM = 1,
+ TARGET_SOCK_STREAM = 2,
+ TARGET_SOCK_RAW = 3,
+ TARGET_SOCK_RDM = 4,
+ TARGET_SOCK_SEQPACKET = 5,
+ TARGET_SOCK_DCCP = 6,
+ TARGET_SOCK_PACKET = 10,
+ };
+
+ #define TARGET_SOCK_MAX (SOCK_PACKET + 1)
+
+#else
+
+ /* For setsockopt(2) */
+ #define TARGET_SOL_SOCKET 1
+
+ #define TARGET_SO_DEBUG 1
+ #define TARGET_SO_REUSEADDR 2
+ #define TARGET_SO_TYPE 3
+ #define TARGET_SO_ERROR 4
+ #define TARGET_SO_DONTROUTE 5
+ #define TARGET_SO_BROADCAST 6
+ #define TARGET_SO_SNDBUF 7
+ #define TARGET_SO_RCVBUF 8
+ #define TARGET_SO_SNDBUFFORCE 32
+ #define TARGET_SO_RCVBUFFORCE 33
+ #define TARGET_SO_KEEPALIVE 9
+ #define TARGET_SO_OOBINLINE 10
+ #define TARGET_SO_NO_CHECK 11
+ #define TARGET_SO_PRIORITY 12
+ #define TARGET_SO_LINGER 13
+ #define TARGET_SO_BSDCOMPAT 14
+ /* To add :#define TARGET_SO_REUSEPORT 15 */
+ #define TARGET_SO_PASSCRED 16
+ #define TARGET_SO_PEERCRED 17
+ #define TARGET_SO_RCVLOWAT 18
+ #define TARGET_SO_SNDLOWAT 19
+ #define TARGET_SO_RCVTIMEO 20
+ #define TARGET_SO_SNDTIMEO 21
+
+ /* Security levels - as per NRL IPv6 - don't actually do anything */
+ #define TARGET_SO_SECURITY_AUTHENTICATION 22
+ #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+ #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24
+
+ #define TARGET_SO_BINDTODEVICE 25
+
+ /* Socket filtering */
+ #define TARGET_SO_ATTACH_FILTER 26
+ #define TARGET_SO_DETACH_FILTER 27
+
+ #define TARGET_SO_PEERNAME 28
+ #define TARGET_SO_TIMESTAMP 29
+ #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP
+
+ #define TARGET_SO_ACCEPTCONN 30
+
+ #define TARGET_SO_PEERSEC 31
+
+#endif
diff --git a/linux-user/sparc/syscall.h b/linux-user/sparc/syscall.h
new file mode 100644
index 0000000..5be90fa
--- /dev/null
+++ b/linux-user/sparc/syscall.h
@@ -0,0 +1,9 @@
+struct target_pt_regs {
+ target_ulong psr;
+ target_ulong pc;
+ target_ulong npc;
+ target_ulong y;
+ target_ulong u_regs[16];
+};
+
+#define UNAME_MACHINE "sun4"
diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h
new file mode 100644
index 0000000..afb364f
--- /dev/null
+++ b/linux-user/sparc/syscall_nr.h
@@ -0,0 +1,220 @@
+#define TARGET_NR_exit 1 /* Common */
+#define TARGET_NR_fork 2 /* Common */
+#define TARGET_NR_read 3 /* Common */
+#define TARGET_NR_write 4 /* Common */
+#define TARGET_NR_open 5 /* Common */
+#define TARGET_NR_close 6 /* Common */
+#define TARGET_NR_wait4 7 /* Common */
+#define TARGET_NR_creat 8 /* Common */
+#define TARGET_NR_link 9 /* Common */
+#define TARGET_NR_unlink 10 /* Common */
+#define TARGET_NR_execv 11 /* SunOS Specific */
+#define TARGET_NR_chdir 12 /* Common */
+#define TARGET_NR_chown 13 /* Common */
+#define TARGET_NR_mknod 14 /* Common */
+#define TARGET_NR_chmod 15 /* Common */
+#define TARGET_NR_lchown 16 /* Common */
+#define TARGET_NR_brk 17 /* Common */
+#define TARGET_NR_perfctr 18 /* Performance counter operations */
+#define TARGET_NR_lseek 19 /* Common */
+#define TARGET_NR_getpid 20 /* Common */
+#define TARGET_NR_capget 21 /* Linux Specific */
+#define TARGET_NR_capset 22 /* Linux Specific */
+#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define TARGET_NR_getuid 24 /* Common */
+#define TARGET_NR_ptrace 26 /* Common */
+#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define TARGET_NR_sigaltstack 28 /* Common */
+#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */
+#define TARGET_NR_lchown32 31 /* Linux sparc32 specific */
+#define TARGET_NR_fchown32 32 /* Linux sparc32 specific */
+#define TARGET_NR_access 33 /* Common */
+#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+#define TARGET_NR_chown32 35 /* Linux sparc32 specific */
+#define TARGET_NR_sync 36 /* Common */
+#define TARGET_NR_kill 37 /* Common */
+#define TARGET_NR_stat 38 /* Common */
+#define TARGET_NR_sendfile 39 /* Linux Specific */
+#define TARGET_NR_lstat 40 /* Common */
+#define TARGET_NR_dup 41 /* Common */
+#define TARGET_NR_pipe 42 /* Common */
+#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */
+#define TARGET_NR_getuid32 44 /* Linux sparc32 specific */
+#define TARGET_NR_umount2 45 /* Linux Specific */
+#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define TARGET_NR_getgid 47 /* Common */
+#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */
+#define TARGET_NR_getegid 50 /* SunOS calls getgid() */
+#define TARGET_NR_acct 51 /* Common */
+#define TARGET_NR_getgid32 53 /* Linux sparc32 specific */
+#define TARGET_NR_ioctl 54 /* Common */
+#define TARGET_NR_reboot 55 /* Common */
+#define TARGET_NR_mmap2 56 /* Linux sparc32 Specific */
+#define TARGET_NR_symlink 57 /* Common */
+#define TARGET_NR_readlink 58 /* Common */
+#define TARGET_NR_execve 59 /* Common */
+#define TARGET_NR_umask 60 /* Common */
+#define TARGET_NR_chroot 61 /* Common */
+#define TARGET_NR_fstat 62 /* Common */
+#define TARGET_NR_fstat64 63 /* Linux sparc32 Specific */
+#define TARGET_NR_getpagesize 64 /* Common */
+#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */
+#define TARGET_NR_vfork 66 /* Common */
+#define TARGET_NR_pread 67 /* Linux Specific */
+#define TARGET_NR_pwrite 68 /* Linux Specific */
+#define TARGET_NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */
+#define TARGET_NR_getegid32 70 /* Linux sparc32, sstk under SunOS */
+#define TARGET_NR_mmap 71 /* Common */
+#define TARGET_NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */
+#define TARGET_NR_munmap 73 /* Common */
+#define TARGET_NR_mprotect 74 /* Common */
+#define TARGET_NR_madvise 75 /* Common */
+#define TARGET_NR_vhangup 76 /* Common */
+#define TARGET_NR_truncate64 77 /* Linux sparc32 Specific */
+#define TARGET_NR_mincore 78 /* Common */
+#define TARGET_NR_getgroups 79 /* Common */
+#define TARGET_NR_setgroups 80 /* Common */
+#define TARGET_NR_getpgrp 81 /* Common */
+#define TARGET_NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */
+#define TARGET_NR_setitimer 83 /* Common */
+#define TARGET_NR_ftruncate64 84 /* Linux sparc32 Specific */
+#define TARGET_NR_swapon 85 /* Common */
+#define TARGET_NR_getitimer 86 /* Common */
+#define TARGET_NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */
+#define TARGET_NR_sethostname 88 /* Common */
+#define TARGET_NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */
+#define TARGET_NR_dup2 90 /* Common */
+#define TARGET_NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */
+#define TARGET_NR_fcntl 92 /* Common */
+#define TARGET_NR_select 93 /* Common */
+#define TARGET_NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */
+#define TARGET_NR_fsync 95 /* Common */
+#define TARGET_NR_setpriority 96 /* Common */
+#define TARGET_NR_socket 97 /* Common */
+#define TARGET_NR_connect 98 /* Common */
+#define TARGET_NR_accept 99 /* Common */
+#define TARGET_NR_getpriority 100 /* Common */
+#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */
+#define TARGET_NR_rt_sigaction 102 /* Linux Specific */
+#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */
+#define TARGET_NR_rt_sigpending 104 /* Linux Specific */
+#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */
+#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */
+#define TARGET_NR_setresuid32 108 /* Linux Specific, sigvec under SunOS */
+#define TARGET_NR_getresuid32 109 /* Linux Specific, sigblock under SunOS */
+#define TARGET_NR_setresgid32 110 /* Linux Specific, sigsetmask under SunOS */
+#define TARGET_NR_getresgid32 111 /* Linux Specific, sigpause under SunOS */
+#define TARGET_NR_setregid32 112 /* Linux sparc32, sigstack under SunOS */
+#define TARGET_NR_recvmsg 113 /* Common */
+#define TARGET_NR_sendmsg 114 /* Common */
+#define TARGET_NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */
+#define TARGET_NR_gettimeofday 116 /* Common */
+#define TARGET_NR_getrusage 117 /* Common */
+#define TARGET_NR_getsockopt 118 /* Common */
+#define TARGET_NR_getcwd 119 /* Linux Specific */
+#define TARGET_NR_readv 120 /* Common */
+#define TARGET_NR_writev 121 /* Common */
+#define TARGET_NR_settimeofday 122 /* Common */
+#define TARGET_NR_fchown 123 /* Common */
+#define TARGET_NR_fchmod 124 /* Common */
+#define TARGET_NR_recvfrom 125 /* Common */
+#define TARGET_NR_setreuid 126 /* Common */
+#define TARGET_NR_setregid 127 /* Common */
+#define TARGET_NR_rename 128 /* Common */
+#define TARGET_NR_truncate 129 /* Common */
+#define TARGET_NR_ftruncate 130 /* Common */
+#define TARGET_NR_flock 131 /* Common */
+#define TARGET_NR_lstat64 132 /* Linux sparc32 Specific */
+#define TARGET_NR_sendto 133 /* Common */
+#define TARGET_NR_shutdown 134 /* Common */
+#define TARGET_NR_socketpair 135 /* Common */
+#define TARGET_NR_mkdir 136 /* Common */
+#define TARGET_NR_rmdir 137 /* Common */
+#define TARGET_NR_utimes 138 /* SunOS Specific */
+#define TARGET_NR_stat64 139 /* Linux sparc32 Specific */
+#define TARGET_NR_getpeername 141 /* Common */
+#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */
+#define TARGET_NR_getrlimit 144 /* Common */
+#define TARGET_NR_setrlimit 145 /* Common */
+#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
+#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define TARGET_NR_getsockname 150 /* Common */
+#define TARGET_NR_poll 153 /* Common */
+#define TARGET_NR_getdents64 154 /* Linux specific */
+#define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */
+#define TARGET_NR_statfs 157 /* Common */
+#define TARGET_NR_fstatfs 158 /* Common */
+#define TARGET_NR_umount 159 /* Common */
+#define TARGET_NR_getdomainname 162 /* SunOS Specific */
+#define TARGET_NR_setdomainname 163 /* Common */
+#define TARGET_NR_quotactl 165 /* Common */
+#define TARGET_NR_mount 167 /* Common */
+#define TARGET_NR_ustat 168 /* Common */
+#define TARGET_NR_getdents 174 /* Common */
+#define TARGET_NR_setsid 175 /* Common */
+#define TARGET_NR_fchdir 176 /* Common */
+#define TARGET_NR_sigpending 183 /* Common */
+#define TARGET_NR_query_module 184 /* Linux Specific */
+#define TARGET_NR_setpgid 185 /* Common */
+#define TARGET_NR_tkill 187 /* SunOS: fpathconf */
+#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
+#define TARGET_NR_uname 189 /* Linux Specific */
+#define TARGET_NR_init_module 190 /* Linux Specific */
+#define TARGET_NR_personality 191 /* Linux Specific */
+#define TARGET_NR_getppid 197 /* Linux Specific */
+#define TARGET_NR_sigaction 198 /* Linux Specific */
+#define TARGET_NR_sgetmask 199 /* Linux Specific */
+#define TARGET_NR_ssetmask 200 /* Linux Specific */
+#define TARGET_NR_sigsuspend 201 /* Linux Specific */
+#define TARGET_NR_oldlstat 202 /* Linux Specific */
+#define TARGET_NR_uselib 203 /* Linux Specific */
+#define TARGET_NR_readdir 204 /* Linux Specific */
+#define TARGET_NR_readahead 205 /* Linux Specific */
+#define TARGET_NR_socketcall 206 /* Linux Specific */
+#define TARGET_NR_syslog 207 /* Linux Specific */
+#define TARGET_NR_waitpid 212 /* Linux Specific */
+#define TARGET_NR_swapoff 213 /* Linux Specific */
+#define TARGET_NR_sysinfo 214 /* Linux Specific */
+#define TARGET_NR_ipc 215 /* Linux Specific */
+#define TARGET_NR_sigreturn 216 /* Linux Specific */
+#define TARGET_NR_clone 217 /* Linux Specific */
+#define TARGET_NR_adjtimex 219 /* Linux Specific */
+#define TARGET_NR_sigprocmask 220 /* Linux Specific */
+#define TARGET_NR_create_module 221 /* Linux Specific */
+#define TARGET_NR_delete_module 222 /* Linux Specific */
+#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */
+#define TARGET_NR_getpgid 224 /* Linux Specific */
+#define TARGET_NR_bdflush 225 /* Linux Specific */
+#define TARGET_NR_sysfs 226 /* Linux Specific */
+#define TARGET_NR_afs_syscall 227 /* Linux Specific */
+#define TARGET_NR_setfsuid 228 /* Linux Specific */
+#define TARGET_NR_setfsgid 229 /* Linux Specific */
+#define TARGET_NR__newselect 230 /* Linux Specific */
+#define TARGET_NR_time 231 /* Linux Specific */
+#define TARGET_NR_stime 233 /* Linux Specific */
+#define TARGET_NR__llseek 236 /* Linux Specific */
+#define TARGET_NR_mlock 237
+#define TARGET_NR_munlock 238
+#define TARGET_NR_mlockall 239
+#define TARGET_NR_munlockall 240
+#define TARGET_NR_sched_setparam 241
+#define TARGET_NR_sched_getparam 242
+#define TARGET_NR_sched_setscheduler 243
+#define TARGET_NR_sched_getscheduler 244
+#define TARGET_NR_sched_yield 245
+#define TARGET_NR_sched_get_priority_max 246
+#define TARGET_NR_sched_get_priority_min 247
+#define TARGET_NR_sched_rr_get_interval 248
+#define TARGET_NR_nanosleep 249
+#define TARGET_NR_mremap 250
+#define TARGET_NR__sysctl 251
+#define TARGET_NR_getsid 252
+#define TARGET_NR_fdatasync 253
+#define TARGET_NR_nfsservctl 254
+#define TARGET_NR_aplib 255
+#define TARGET_NR__exit TARGET_NR_exit
diff --git a/linux-user/sparc/termbits.h b/linux-user/sparc/termbits.h
new file mode 100644
index 0000000..cad45b2
--- /dev/null
+++ b/linux-user/sparc/termbits.h
@@ -0,0 +1,279 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_cc characters */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VEOL 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+
+#define TARGET_VSUSP 10
+#define TARGET_VDSUSP 11 /* SunOS POSIX nicety I do believe... */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+
+/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
+ * shared with eof/eol
+ */
+#define TARGET_VMIN TARGET_VEOF
+#define TARGET_VTIME TARGET_VEOL
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0x00000001
+#define TARGET_BRKINT 0x00000002
+#define TARGET_IGNPAR 0x00000004
+#define TARGET_PARMRK 0x00000008
+#define TARGET_INPCK 0x00000010
+#define TARGET_ISTRIP 0x00000020
+#define TARGET_INLCR 0x00000040
+#define TARGET_IGNCR 0x00000080
+#define TARGET_ICRNL 0x00000100
+#define TARGET_IUCLC 0x00000200
+#define TARGET_IXON 0x00000400
+#define TARGET_IXANY 0x00000800
+#define TARGET_IXOFF 0x00001000
+#define TARGET_IMAXBEL 0x00002000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0x00000001
+#define TARGET_OLCUC 0x00000002
+#define TARGET_ONLCR 0x00000004
+#define TARGET_OCRNL 0x00000008
+#define TARGET_ONOCR 0x00000010
+#define TARGET_ONLRET 0x00000020
+#define TARGET_OFILL 0x00000040
+#define TARGET_OFDEL 0x00000080
+#define TARGET_NLDLY 0x00000100
+#define TARGET_NL0 0x00000000
+#define TARGET_NL1 0x00000100
+#define TARGET_CRDLY 0x00000600
+#define TARGET_CR0 0x00000000
+#define TARGET_CR1 0x00000200
+#define TARGET_CR2 0x00000400
+#define TARGET_CR3 0x00000600
+#define TARGET_TABDLY 0x00001800
+#define TARGET_TAB0 0x00000000
+#define TARGET_TAB1 0x00000800
+#define TARGET_TAB2 0x00001000
+#define TARGET_TAB3 0x00001800
+#define TARGET_XTABS 0x00001800
+#define TARGET_BSDLY 0x00002000
+#define TARGET_BS0 0x00000000
+#define TARGET_BS1 0x00002000
+#define TARGET_VTDLY 0x00004000
+#define TARGET_VT0 0x00000000
+#define TARGET_VT1 0x00004000
+#define TARGET_FFDLY 0x00008000
+#define TARGET_FF0 0x00000000
+#define TARGET_FF1 0x00008000
+#define TARGET_PAGEOUT 0x00010000 /* SUNOS specific */
+#define TARGET_WRAP 0x00020000 /* SUNOS specific */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0x0000100f
+#define TARGET_B0 0x00000000 /* hang up */
+#define TARGET_B50 0x00000001
+#define TARGET_B75 0x00000002
+#define TARGET_B110 0x00000003
+#define TARGET_B134 0x00000004
+#define TARGET_B150 0x00000005
+#define TARGET_B200 0x00000006
+#define TARGET_B300 0x00000007
+#define TARGET_B600 0x00000008
+#define TARGET_B1200 0x00000009
+#define TARGET_B1800 0x0000000a
+#define TARGET_B2400 0x0000000b
+#define TARGET_B4800 0x0000000c
+#define TARGET_B9600 0x0000000d
+#define TARGET_B19200 0x0000000e
+#define TARGET_B38400 0x0000000f
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0x00000030
+#define TARGET_CS5 0x00000000
+#define TARGET_CS6 0x00000010
+#define TARGET_CS7 0x00000020
+#define TARGET_CS8 0x00000030
+#define TARGET_CSTOPB 0x00000040
+#define TARGET_CREAD 0x00000080
+#define TARGET_PARENB 0x00000100
+#define TARGET_PARODD 0x00000200
+#define TARGET_HUPCL 0x00000400
+#define TARGET_CLOCAL 0x00000800
+#define TARGET_CBAUDEX 0x00001000
+/* We'll never see these speeds with the Zilogs, but for completeness... */
+#define TARGET_B57600 0x00001001
+#define TARGET_B115200 0x00001002
+#define TARGET_B230400 0x00001003
+#define TARGET_B460800 0x00001004
+/* This is what we can do with the Zilogs. */
+#define TARGET_B76800 0x00001005
+/* This is what we can do with the SAB82532. */
+#define TARGET_B153600 0x00001006
+#define TARGET_B307200 0x00001007
+#define TARGET_B614400 0x00001008
+#define TARGET_B921600 0x00001009
+/* And these are the rest... */
+#define TARGET_B500000 0x0000100a
+#define TARGET_B576000 0x0000100b
+#define TARGET_B1000000 0x0000100c
+#define TARGET_B1152000 0x0000100d
+#define TARGET_B1500000 0x0000100e
+#define TARGET_B2000000 0x0000100f
+/* These have totally bogus values and nobody uses them
+ so far. Later on we'd have to use say 0x10000x and
+ adjust CBAUD constant and drivers accordingly.
+#define B2500000 0x00001010
+#define B3000000 0x00001011
+#define B3500000 0x00001012
+#define B4000000 0x00001013 */
+#define TARGET_CIBAUD 0x100f0000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 0x40000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 0x80000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000001
+#define TARGET_ICANON 0x00000002
+#define TARGET_XCASE 0x00000004
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000010
+#define TARGET_ECHOK 0x00000020
+#define TARGET_ECHONL 0x00000040
+#define TARGET_NOFLSH 0x00000080
+#define TARGET_TOSTOP 0x00000100
+#define TARGET_ECHOCTL 0x00000200
+#define TARGET_ECHOPRT 0x00000400
+#define TARGET_ECHOKE 0x00000800
+#define TARGET_DEFECHO 0x00001000 /* SUNOS thing, what is it? */
+#define TARGET_FLUSHO 0x00002000
+#define TARGET_PENDIN 0x00004000
+#define TARGET_IEXTEN 0x00008000
+
+/* ioctls */
+
+/* Big T */
+#define TARGET_TCGETA TARGET_IOR('T', 1, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('T', 2, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('T', 3, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('T', 4, struct target_termio)
+#define TARGET_TCSBRK TARGET_IO('T', 5)
+#define TARGET_TCXONC TARGET_IO('T', 6)
+#define TARGET_TCFLSH TARGET_IO('T', 7)
+#define TARGET_TCGETS TARGET_IOR('T', 8, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('T', 9, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('T', 10, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('T', 11, struct target_termios)
+
+/* Note that all the ioctls that are not available in Linux have a
+ * double underscore on the front to: a) avoid some programs to
+ * thing we support some ioctls under Linux (autoconfiguration stuff)
+ */
+/* Little t */
+#define TARGET_TIOCGETD TARGET_IOR('t', 0, int)
+#define TARGET_TIOCSETD TARGET_IOW('t', 1, int)
+//#define __TIOCHPCL _IO('t', 2) /* SunOS Specific */
+//#define __TIOCMODG _IOR('t', 3, int) /* SunOS Specific */
+//#define __TIOCMODS _IOW('t', 4, int) /* SunOS Specific */
+//#define __TIOCGETP _IOR('t', 8, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETP _IOW('t', 9, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETN _IOW('t', 10, struct sgttyb) /* SunOS Specific */
+#define TARGET_TIOCEXCL TARGET_IO('t', 13)
+#define TARGET_TIOCNXCL TARGET_IO('t', 14)
+//#define __TIOCFLUSH _IOW('t', 16, int) /* SunOS Specific */
+//#define __TIOCSETC _IOW('t', 17, struct tchars) /* SunOS Specific */
+//#define __TIOCGETC _IOR('t', 18, struct tchars) /* SunOS Specific */
+//#define __TIOCTCNTL _IOW('t', 32, int) /* SunOS Specific */
+//#define __TIOCSIGNAL _IOW('t', 33, int) /* SunOS Specific */
+//#define __TIOCSETX _IOW('t', 34, int) /* SunOS Specific */
+//#define __TIOCGETX _IOR('t', 35, int) /* SunOS Specific */
+#define TARGET_TIOCCONS TARGET_IO('t', 36)
+//#define __TIOCSSIZE _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
+//#define __TIOCGSIZE _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('t', 100, int)
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('t', 101, int)
+//#define __TIOCUCNTL _IOW('t', 102, int) /* SunOS Specific */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+//#define __TIOCREMOTE _IOW('t', 105, int) /* SunOS Specific */
+#define TARGET_TIOCMGET TARGET_IOR('t', 106, int)
+#define TARGET_TIOCMBIC TARGET_IOW('t', 107, int)
+#define TARGET_TIOCMBIS TARGET_IOW('t', 108, int)
+#define TARGET_TIOCMSET TARGET_IOW('t', 109, int)
+#define TARGET_TIOCSTART TARGET_IO('t', 110)
+#define TARGET_TIOCSTOP TARGET_IO('t', 111)
+#define TARGET_TIOCPKT TARGET_IOW('t', 112, int)
+#define TARGET_TIOCNOTTY TARGET_IO('t', 113)
+#define TARGET_TIOCSTI TARGET_IOW('t', 114, char)
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int)
+//#define __TIOCGLTC _IOR('t', 116, struct ltchars) /* SunOS Specific */
+//#define __TIOCSLTC _IOW('t', 117, struct ltchars) /* SunOS Specific */
+/* 118 is the non-posix setpgrp tty ioctl */
+/* 119 is the non-posix getpgrp tty ioctl */
+//#define __TIOCCDTR TARGET_IO('t', 120) /* SunOS Specific */
+//#define __TIOCSDTR TARGET_IO('t', 121) /* SunOS Specific */
+#define TARGET_TIOCCBRK TARGET_IO('t', 122)
+#define TARGET_TIOCSBRK TARGET_IO('t', 123)
+//#define __TIOCLGET TARGET_IOW('t', 124, int) /* SunOS Specific */
+//#define __TIOCLSET TARGET_IOW('t', 125, int) /* SunOS Specific */
+//#define __TIOCLBIC TARGET_IOW('t', 126, int) /* SunOS Specific */
+//#define __TIOCLBIS TARGET_IOW('t', 127, int) /* SunOS Specific */
+//#define __TIOCISPACE TARGET_IOR('t', 128, int) /* SunOS Specific */
+//#define __TIOCISIZE TARGET_IOR('t', 129, int) /* SunOS Specific */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 130, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 131, int)
+#define TARGET_TIOCSCTTY TARGET_IO('t', 132)
+#define TARGET_TIOCGSID TARGET_IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
+#define TARGET_TIOCGPTN TARGET_IOR('t', 134, unsigned int) /* Get Pty Number */
+#define TARGET_TIOCSPTLCK TARGET_IOW('t', 135, int) /* Lock/unlock PTY */
+
+/* Little f */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
+ * someday. This is completely bogus, I know...
+ */
+//#define __TCGETSTAT TARGET_IO('T', 200) /* Rutgers specific */
+//#define __TCSETSTAT TARGET_IO('T', 201) /* Rutgers specific */
+
+/* Linux specific, no SunOS equivalent. */
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TCSBRKP 0x5425
+#define TARGET_TIOCTTYGSTRUCT 0x5426
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x545C /* Wait input */
+#define TARGET_TIOCGICOUNT 0x545D /* Read serial port inline interrupt counts */
+
diff --git a/linux-user/sparc64/syscall.h b/linux-user/sparc64/syscall.h
new file mode 100644
index 0000000..c361558
--- /dev/null
+++ b/linux-user/sparc64/syscall.h
@@ -0,0 +1,10 @@
+struct target_pt_regs {
+ target_ulong u_regs[16];
+ target_ulong tstate;
+ target_ulong pc;
+ target_ulong npc;
+ target_ulong y;
+ target_ulong fprs;
+};
+
+#define UNAME_MACHINE "sun4u"
diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h
new file mode 100644
index 0000000..9274c85
--- /dev/null
+++ b/linux-user/sparc64/syscall_nr.h
@@ -0,0 +1,286 @@
+#define TARGET_NR_restart_syscall 0 /* Linux Specific */
+#define TARGET_NR_exit 1 /* Common */
+#define TARGET_NR_fork 2 /* Common */
+#define TARGET_NR_read 3 /* Common */
+#define TARGET_NR_write 4 /* Common */
+#define TARGET_NR_open 5 /* Common */
+#define TARGET_NR_close 6 /* Common */
+#define TARGET_NR_wait4 7 /* Common */
+#define TARGET_NR_creat 8 /* Common */
+#define TARGET_NR_link 9 /* Common */
+#define TARGET_NR_unlink 10 /* Common */
+#define TARGET_NR_execv 11 /* SunOS Specific */
+#define TARGET_NR_chdir 12 /* Common */
+#define TARGET_NR_chown 13 /* Common */
+#define TARGET_NR_mknod 14 /* Common */
+#define TARGET_NR_chmod 15 /* Common */
+#define TARGET_NR_lchown 16 /* Common */
+#define TARGET_NR_brk 17 /* Common */
+#define TARGET_NR_perfctr 18 /* Performance counter operations */
+#define TARGET_NR_lseek 19 /* Common */
+#define TARGET_NR_getpid 20 /* Common */
+#define TARGET_NR_capget 21 /* Linux Specific */
+#define TARGET_NR_capset 22 /* Linux Specific */
+#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define TARGET_NR_getuid 24 /* Common */
+/* #define TARGET_NR_time alias 25 ENOSYS under SunOS */
+#define TARGET_NR_ptrace 26 /* Common */
+#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define TARGET_NR_sigaltstack 28 /* Common */
+#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */
+/* #define TARGET_NR_lchown32 31 Linux sparc32 specific */
+/* #define TARGET_NR_fchown32 32 Linux sparc32 specific */
+#define TARGET_NR_access 33 /* Common */
+#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+/* #define TARGET_NR_chown32 35 Linux sparc32 specific */
+#define TARGET_NR_sync 36 /* Common */
+#define TARGET_NR_kill 37 /* Common */
+#define TARGET_NR_stat 38 /* Common */
+#define TARGET_NR_sendfile 39 /* Linux Specific */
+#define TARGET_NR_lstat 40 /* Common */
+#define TARGET_NR_dup 41 /* Common */
+#define TARGET_NR_pipe 42 /* Common */
+#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */
+/* #define TARGET_NR_getuid32 44 Linux sparc32 specific */
+#define TARGET_NR_umount2 45 /* Linux Specific */
+#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define TARGET_NR_getgid 47 /* Common */
+#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */
+#define TARGET_NR_getegid 50 /* SunOS calls getgid() */
+#define TARGET_NR_acct 51 /* Common */
+#define TARGET_NR_memory_ordering 52 /* Linux Specific */
+/* #define TARGET_NR_getgid32 53 Linux sparc32 specific */
+#define TARGET_NR_ioctl 54 /* Common */
+#define TARGET_NR_reboot 55 /* Common */
+/* #define TARGET_NR_mmap2 56 Linux sparc32 Specific */
+#define TARGET_NR_symlink 57 /* Common */
+#define TARGET_NR_readlink 58 /* Common */
+#define TARGET_NR_execve 59 /* Common */
+#define TARGET_NR_umask 60 /* Common */
+#define TARGET_NR_chroot 61 /* Common */
+#define TARGET_NR_fstat 62 /* Common */
+/* #define TARGET_NR_fstat64 63 Linux sparc32 Specific */
+#define TARGET_NR_getpagesize 64 /* Common */
+#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */
+#define TARGET_NR_vfork 66 /* Common */
+#define TARGET_NR_pread64 67 /* Linux Specific */
+#define TARGET_NR_pwrite64 68 /* Linux Specific */
+/* #define TARGET_NR_geteuid32 69 Linux sparc32, sbrk under SunOS */
+/* #define TARGET_NR_getegid32 70 Linux sparc32, sstk under SunOS */
+#define TARGET_NR_mmap 71 /* Common */
+/* #define TARGET_NR_setreuid32 72 Linux sparc32, vadvise under SunOS */
+#define TARGET_NR_munmap 73 /* Common */
+#define TARGET_NR_mprotect 74 /* Common */
+#define TARGET_NR_madvise 75 /* Common */
+#define TARGET_NR_vhangup 76 /* Common */
+/* #define TARGET_NR_truncate64 77 Linux sparc32 Specific */
+#define TARGET_NR_mincore 78 /* Common */
+#define TARGET_NR_getgroups 79 /* Common */
+#define TARGET_NR_setgroups 80 /* Common */
+#define TARGET_NR_getpgrp 81 /* Common */
+/* #define TARGET_NR_setgroups32 82 Linux sparc32, setpgrp under SunOS */
+#define TARGET_NR_setitimer 83 /* Common */
+/* #define TARGET_NR_ftruncate64 84 Linux sparc32 Specific */
+#define TARGET_NR_swapon 85 /* Common */
+#define TARGET_NR_getitimer 86 /* Common */
+/* #define TARGET_NR_setuid32 87 Linux sparc32, gethostname under SunOS */
+#define TARGET_NR_sethostname 88 /* Common */
+/* #define TARGET_NR_setgid32 89 Linux sparc32, getdtablesize under SunOS */
+#define TARGET_NR_dup2 90 /* Common */
+/* #define TARGET_NR_setfsuid32 91 Linux sparc32, getdopt under SunOS */
+#define TARGET_NR_fcntl 92 /* Common */
+#define TARGET_NR_select 93 /* Common */
+/* #define TARGET_NR_setfsgid32 94 Linux sparc32, setdopt under SunOS */
+#define TARGET_NR_fsync 95 /* Common */
+#define TARGET_NR_setpriority 96 /* Common */
+#define TARGET_NR_socket 97 /* Common */
+#define TARGET_NR_connect 98 /* Common */
+#define TARGET_NR_accept 99 /* Common */
+#define TARGET_NR_getpriority 100 /* Common */
+#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */
+#define TARGET_NR_rt_sigaction 102 /* Linux Specific */
+#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */
+#define TARGET_NR_rt_sigpending 104 /* Linux Specific */
+#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */
+#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */
+#define TARGET_NR_setresuid 108 /* Linux Specific, sigvec under SunOS */
+#define TARGET_NR_getresuid 109 /* Linux Specific, sigblock under SunOS */
+#define TARGET_NR_setresgid 110 /* Linux Specific, sigsetmask under SunOS */
+#define TARGET_NR_getresgid 111 /* Linux Specific, sigpause under SunOS */
+/* #define TARGET_NR_setregid32 75 Linux sparc32, sigstack under SunOS */
+#define TARGET_NR_recvmsg 113 /* Common */
+#define TARGET_NR_sendmsg 114 /* Common */
+/* #define TARGET_NR_getgroups32 115 Linux sparc32, vtrace under SunOS */
+#define TARGET_NR_gettimeofday 116 /* Common */
+#define TARGET_NR_getrusage 117 /* Common */
+#define TARGET_NR_getsockopt 118 /* Common */
+#define TARGET_NR_getcwd 119 /* Linux Specific */
+#define TARGET_NR_readv 120 /* Common */
+#define TARGET_NR_writev 121 /* Common */
+#define TARGET_NR_settimeofday 122 /* Common */
+#define TARGET_NR_fchown 123 /* Common */
+#define TARGET_NR_fchmod 124 /* Common */
+#define TARGET_NR_recvfrom 125 /* Common */
+#define TARGET_NR_setreuid 126 /* Common */
+#define TARGET_NR_setregid 127 /* Common */
+#define TARGET_NR_rename 128 /* Common */
+#define TARGET_NR_truncate 129 /* Common */
+#define TARGET_NR_ftruncate 130 /* Common */
+#define TARGET_NR_flock 131 /* Common */
+/* #define TARGET_NR_lstat64 132 Linux sparc32 Specific */
+#define TARGET_NR_sendto 133 /* Common */
+#define TARGET_NR_shutdown 134 /* Common */
+#define TARGET_NR_socketpair 135 /* Common */
+#define TARGET_NR_mkdir 136 /* Common */
+#define TARGET_NR_rmdir 137 /* Common */
+#define TARGET_NR_utimes 138 /* SunOS Specific */
+/* #define TARGET_NR_stat64 139 Linux sparc32 Specific */
+#define TARGET_NR_sendfile64 140 /* adjtime under SunOS */
+#define TARGET_NR_getpeername 141 /* Common */
+#define TARGET_NR_futex 142 /* gethostid under SunOS */
+#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */
+#define TARGET_NR_getrlimit 144 /* Common */
+#define TARGET_NR_setrlimit 145 /* Common */
+#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
+#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define TARGET_NR_getsockname 150 /* Common */
+/* #define TARGET_NR_getmsg 151 SunOS Specific */
+/* #define TARGET_NR_putmsg 152 SunOS Specific */
+#define TARGET_NR_poll 153 /* Common */
+#define TARGET_NR_getdents64 154 /* Linux specific */
+/* #define TARGET_NR_fcntl64 155 Linux sparc32 Specific */
+/* #define TARGET_NR_getdirentries 156 SunOS Specific */
+#define TARGET_NR_statfs 157 /* Common */
+#define TARGET_NR_fstatfs 158 /* Common */
+#define TARGET_NR_umount 159 /* Common */
+#define TARGET_NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */
+#define TARGET_NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */
+#define TARGET_NR_getdomainname 162 /* SunOS Specific */
+#define TARGET_NR_setdomainname 163 /* Common */
+#define TARGET_NR_utrap_install 164 /* SYSV ABI/v9 required */
+#define TARGET_NR_quotactl 165 /* Common */
+#define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */
+#define TARGET_NR_mount 167 /* Common */
+#define TARGET_NR_ustat 168 /* Common */
+#define TARGET_NR_setxattr 169 /* SunOS: semsys */
+#define TARGET_NR_lsetxattr 170 /* SunOS: msgsys */
+#define TARGET_NR_fsetxattr 171 /* SunOS: shmsys */
+#define TARGET_NR_getxattr 172 /* SunOS: auditsys */
+#define TARGET_NR_lgetxattr 173 /* SunOS: rfssys */
+#define TARGET_NR_getdents 174 /* Common */
+#define TARGET_NR_setsid 175 /* Common */
+#define TARGET_NR_fchdir 176 /* Common */
+#define TARGET_NR_fgetxattr 177 /* SunOS: fchroot */
+#define TARGET_NR_listxattr 178 /* SunOS: vpixsys */
+#define TARGET_NR_llistxattr 179 /* SunOS: aioread */
+#define TARGET_NR_flistxattr 180 /* SunOS: aiowrite */
+#define TARGET_NR_removexattr 181 /* SunOS: aiowait */
+#define TARGET_NR_lremovexattr 182 /* SunOS: aiocancel */
+#define TARGET_NR_sigpending 183 /* Common */
+#define TARGET_NR_query_module 184 /* Linux Specific */
+#define TARGET_NR_setpgid 185 /* Common */
+#define TARGET_NR_fremovexattr 186 /* SunOS: pathconf */
+#define TARGET_NR_tkill 187 /* SunOS: fpathconf */
+#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
+#define TARGET_NR_uname 189 /* Linux Specific */
+#define TARGET_NR_init_module 190 /* Linux Specific */
+#define TARGET_NR_personality 191 /* Linux Specific */
+#define TARGET_NR_remap_file_pages 192 /* Linux Specific */
+#define TARGET_NR_epoll_create 193 /* Linux Specific */
+#define TARGET_NR_epoll_ctl 194 /* Linux Specific */
+#define TARGET_NR_epoll_wait 195 /* Linux Specific */
+/* #define TARGET_NR_ulimit 196 Linux Specific */
+#define TARGET_NR_getppid 197 /* Linux Specific */
+#define TARGET_NR_sigaction 198 /* Linux Specific */
+#define TARGET_NR_sgetmask 199 /* Linux Specific */
+#define TARGET_NR_ssetmask 200 /* Linux Specific */
+#define TARGET_NR_sigsuspend 201 /* Linux Specific */
+#define TARGET_NR_oldlstat 202 /* Linux Specific */
+#define TARGET_NR_uselib 203 /* Linux Specific */
+#define TARGET_NR_readdir 204 /* Linux Specific */
+#define TARGET_NR_readahead 205 /* Linux Specific */
+#define TARGET_NR_socketcall 206 /* Linux Specific */
+#define TARGET_NR_syslog 207 /* Linux Specific */
+#define TARGET_NR_lookup_dcookie 208 /* Linux Specific */
+#define TARGET_NR_fadvise64 209 /* Linux Specific */
+#define TARGET_NR_fadvise64_64 210 /* Linux Specific */
+#define TARGET_NR_tgkill 211 /* Linux Specific */
+#define TARGET_NR_waitpid 212 /* Linux Specific */
+#define TARGET_NR_swapoff 213 /* Linux Specific */
+#define TARGET_NR_sysinfo 214 /* Linux Specific */
+#define TARGET_NR_ipc 215 /* Linux Specific */
+#define TARGET_NR_sigreturn 216 /* Linux Specific */
+#define TARGET_NR_clone 217 /* Linux Specific */
+/* #define TARGET_NR_modify_ldt 218 Linux Specific - i386 specific, unused */
+#define TARGET_NR_adjtimex 219 /* Linux Specific */
+#define TARGET_NR_sigprocmask 220 /* Linux Specific */
+#define TARGET_NR_create_module 221 /* Linux Specific */
+#define TARGET_NR_delete_module 222 /* Linux Specific */
+#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */
+#define TARGET_NR_getpgid 224 /* Linux Specific */
+#define TARGET_NR_bdflush 225 /* Linux Specific */
+#define TARGET_NR_sysfs 226 /* Linux Specific */
+#define TARGET_NR_afs_syscall 227 /* Linux Specific */
+#define TARGET_NR_setfsuid 228 /* Linux Specific */
+#define TARGET_NR_setfsgid 229 /* Linux Specific */
+#define TARGET_NR__newselect 230 /* Linux Specific */
+#ifdef __KERNEL__
+#define TARGET_NR_time 231 /* Linux sparc32 */
+#endif
+/* #define TARGET_NR_oldstat 232 Linux Specific */
+#define TARGET_NR_stime 233 /* Linux Specific */
+#define TARGET_NR_statfs64 234 /* Linux Specific */
+#define TARGET_NR_fstatfs64 235 /* Linux Specific */
+#define TARGET_NR__llseek 236 /* Linux Specific */
+#define TARGET_NR_mlock 237
+#define TARGET_NR_munlock 238
+#define TARGET_NR_mlockall 239
+#define TARGET_NR_munlockall 240
+#define TARGET_NR_sched_setparam 241
+#define TARGET_NR_sched_getparam 242
+#define TARGET_NR_sched_setscheduler 243
+#define TARGET_NR_sched_getscheduler 244
+#define TARGET_NR_sched_yield 245
+#define TARGET_NR_sched_get_priority_max 246
+#define TARGET_NR_sched_get_priority_min 247
+#define TARGET_NR_sched_rr_get_interval 248
+#define TARGET_NR_nanosleep 249
+#define TARGET_NR_mremap 250
+#define TARGET_NR__sysctl 251
+#define TARGET_NR_getsid 252
+#define TARGET_NR_fdatasync 253
+#define TARGET_NR_nfsservctl 254
+#define TARGET_NR_aplib 255
+#define TARGET_NR_clock_settime 256
+#define TARGET_NR_clock_gettime 257
+#define TARGET_NR_clock_getres 258
+#define TARGET_NR_clock_nanosleep 259
+#define TARGET_NR_sched_getaffinity 260
+#define TARGET_NR_sched_setaffinity 261
+#define TARGET_NR_timer_settime 262
+#define TARGET_NR_timer_gettime 263
+#define TARGET_NR_timer_getoverrun 264
+#define TARGET_NR_timer_delete 265
+#define TARGET_NR_timer_create 266
+/* #define TARGET_NR_vserver 267 Reserved for VSERVER */
+#define TARGET_NR_io_setup 268
+#define TARGET_NR_io_destroy 269
+#define TARGET_NR_io_submit 270
+#define TARGET_NR_io_cancel 271
+#define TARGET_NR_io_getevents 272
+#define TARGET_NR_mq_open 273
+#define TARGET_NR_mq_unlink 274
+#define TARGET_NR_mq_timedsend 275
+#define TARGET_NR_mq_timedreceive 276
+#define TARGET_NR_mq_notify 277
+#define TARGET_NR_mq_getsetattr 278
+#define TARGET_NR_waitid 279
+/*#define TARGET_NR_sys_setaltroot 280 available (was setaltroot) */
+#define TARGET_NR_add_key 281
+#define TARGET_NR_request_key 282
+#define TARGET_NR_keyctl 283
diff --git a/linux-user/sparc64/termbits.h b/linux-user/sparc64/termbits.h
new file mode 100644
index 0000000..cad45b2
--- /dev/null
+++ b/linux-user/sparc64/termbits.h
@@ -0,0 +1,279 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* c_cc characters */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VEOL 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+
+#define TARGET_VSUSP 10
+#define TARGET_VDSUSP 11 /* SunOS POSIX nicety I do believe... */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+
+/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
+ * shared with eof/eol
+ */
+#define TARGET_VMIN TARGET_VEOF
+#define TARGET_VTIME TARGET_VEOL
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0x00000001
+#define TARGET_BRKINT 0x00000002
+#define TARGET_IGNPAR 0x00000004
+#define TARGET_PARMRK 0x00000008
+#define TARGET_INPCK 0x00000010
+#define TARGET_ISTRIP 0x00000020
+#define TARGET_INLCR 0x00000040
+#define TARGET_IGNCR 0x00000080
+#define TARGET_ICRNL 0x00000100
+#define TARGET_IUCLC 0x00000200
+#define TARGET_IXON 0x00000400
+#define TARGET_IXANY 0x00000800
+#define TARGET_IXOFF 0x00001000
+#define TARGET_IMAXBEL 0x00002000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0x00000001
+#define TARGET_OLCUC 0x00000002
+#define TARGET_ONLCR 0x00000004
+#define TARGET_OCRNL 0x00000008
+#define TARGET_ONOCR 0x00000010
+#define TARGET_ONLRET 0x00000020
+#define TARGET_OFILL 0x00000040
+#define TARGET_OFDEL 0x00000080
+#define TARGET_NLDLY 0x00000100
+#define TARGET_NL0 0x00000000
+#define TARGET_NL1 0x00000100
+#define TARGET_CRDLY 0x00000600
+#define TARGET_CR0 0x00000000
+#define TARGET_CR1 0x00000200
+#define TARGET_CR2 0x00000400
+#define TARGET_CR3 0x00000600
+#define TARGET_TABDLY 0x00001800
+#define TARGET_TAB0 0x00000000
+#define TARGET_TAB1 0x00000800
+#define TARGET_TAB2 0x00001000
+#define TARGET_TAB3 0x00001800
+#define TARGET_XTABS 0x00001800
+#define TARGET_BSDLY 0x00002000
+#define TARGET_BS0 0x00000000
+#define TARGET_BS1 0x00002000
+#define TARGET_VTDLY 0x00004000
+#define TARGET_VT0 0x00000000
+#define TARGET_VT1 0x00004000
+#define TARGET_FFDLY 0x00008000
+#define TARGET_FF0 0x00000000
+#define TARGET_FF1 0x00008000
+#define TARGET_PAGEOUT 0x00010000 /* SUNOS specific */
+#define TARGET_WRAP 0x00020000 /* SUNOS specific */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0x0000100f
+#define TARGET_B0 0x00000000 /* hang up */
+#define TARGET_B50 0x00000001
+#define TARGET_B75 0x00000002
+#define TARGET_B110 0x00000003
+#define TARGET_B134 0x00000004
+#define TARGET_B150 0x00000005
+#define TARGET_B200 0x00000006
+#define TARGET_B300 0x00000007
+#define TARGET_B600 0x00000008
+#define TARGET_B1200 0x00000009
+#define TARGET_B1800 0x0000000a
+#define TARGET_B2400 0x0000000b
+#define TARGET_B4800 0x0000000c
+#define TARGET_B9600 0x0000000d
+#define TARGET_B19200 0x0000000e
+#define TARGET_B38400 0x0000000f
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0x00000030
+#define TARGET_CS5 0x00000000
+#define TARGET_CS6 0x00000010
+#define TARGET_CS7 0x00000020
+#define TARGET_CS8 0x00000030
+#define TARGET_CSTOPB 0x00000040
+#define TARGET_CREAD 0x00000080
+#define TARGET_PARENB 0x00000100
+#define TARGET_PARODD 0x00000200
+#define TARGET_HUPCL 0x00000400
+#define TARGET_CLOCAL 0x00000800
+#define TARGET_CBAUDEX 0x00001000
+/* We'll never see these speeds with the Zilogs, but for completeness... */
+#define TARGET_B57600 0x00001001
+#define TARGET_B115200 0x00001002
+#define TARGET_B230400 0x00001003
+#define TARGET_B460800 0x00001004
+/* This is what we can do with the Zilogs. */
+#define TARGET_B76800 0x00001005
+/* This is what we can do with the SAB82532. */
+#define TARGET_B153600 0x00001006
+#define TARGET_B307200 0x00001007
+#define TARGET_B614400 0x00001008
+#define TARGET_B921600 0x00001009
+/* And these are the rest... */
+#define TARGET_B500000 0x0000100a
+#define TARGET_B576000 0x0000100b
+#define TARGET_B1000000 0x0000100c
+#define TARGET_B1152000 0x0000100d
+#define TARGET_B1500000 0x0000100e
+#define TARGET_B2000000 0x0000100f
+/* These have totally bogus values and nobody uses them
+ so far. Later on we'd have to use say 0x10000x and
+ adjust CBAUD constant and drivers accordingly.
+#define B2500000 0x00001010
+#define B3000000 0x00001011
+#define B3500000 0x00001012
+#define B4000000 0x00001013 */
+#define TARGET_CIBAUD 0x100f0000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 0x40000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 0x80000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000001
+#define TARGET_ICANON 0x00000002
+#define TARGET_XCASE 0x00000004
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000010
+#define TARGET_ECHOK 0x00000020
+#define TARGET_ECHONL 0x00000040
+#define TARGET_NOFLSH 0x00000080
+#define TARGET_TOSTOP 0x00000100
+#define TARGET_ECHOCTL 0x00000200
+#define TARGET_ECHOPRT 0x00000400
+#define TARGET_ECHOKE 0x00000800
+#define TARGET_DEFECHO 0x00001000 /* SUNOS thing, what is it? */
+#define TARGET_FLUSHO 0x00002000
+#define TARGET_PENDIN 0x00004000
+#define TARGET_IEXTEN 0x00008000
+
+/* ioctls */
+
+/* Big T */
+#define TARGET_TCGETA TARGET_IOR('T', 1, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('T', 2, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('T', 3, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('T', 4, struct target_termio)
+#define TARGET_TCSBRK TARGET_IO('T', 5)
+#define TARGET_TCXONC TARGET_IO('T', 6)
+#define TARGET_TCFLSH TARGET_IO('T', 7)
+#define TARGET_TCGETS TARGET_IOR('T', 8, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('T', 9, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('T', 10, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('T', 11, struct target_termios)
+
+/* Note that all the ioctls that are not available in Linux have a
+ * double underscore on the front to: a) avoid some programs to
+ * thing we support some ioctls under Linux (autoconfiguration stuff)
+ */
+/* Little t */
+#define TARGET_TIOCGETD TARGET_IOR('t', 0, int)
+#define TARGET_TIOCSETD TARGET_IOW('t', 1, int)
+//#define __TIOCHPCL _IO('t', 2) /* SunOS Specific */
+//#define __TIOCMODG _IOR('t', 3, int) /* SunOS Specific */
+//#define __TIOCMODS _IOW('t', 4, int) /* SunOS Specific */
+//#define __TIOCGETP _IOR('t', 8, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETP _IOW('t', 9, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETN _IOW('t', 10, struct sgttyb) /* SunOS Specific */
+#define TARGET_TIOCEXCL TARGET_IO('t', 13)
+#define TARGET_TIOCNXCL TARGET_IO('t', 14)
+//#define __TIOCFLUSH _IOW('t', 16, int) /* SunOS Specific */
+//#define __TIOCSETC _IOW('t', 17, struct tchars) /* SunOS Specific */
+//#define __TIOCGETC _IOR('t', 18, struct tchars) /* SunOS Specific */
+//#define __TIOCTCNTL _IOW('t', 32, int) /* SunOS Specific */
+//#define __TIOCSIGNAL _IOW('t', 33, int) /* SunOS Specific */
+//#define __TIOCSETX _IOW('t', 34, int) /* SunOS Specific */
+//#define __TIOCGETX _IOR('t', 35, int) /* SunOS Specific */
+#define TARGET_TIOCCONS TARGET_IO('t', 36)
+//#define __TIOCSSIZE _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
+//#define __TIOCGSIZE _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('t', 100, int)
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('t', 101, int)
+//#define __TIOCUCNTL _IOW('t', 102, int) /* SunOS Specific */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+//#define __TIOCREMOTE _IOW('t', 105, int) /* SunOS Specific */
+#define TARGET_TIOCMGET TARGET_IOR('t', 106, int)
+#define TARGET_TIOCMBIC TARGET_IOW('t', 107, int)
+#define TARGET_TIOCMBIS TARGET_IOW('t', 108, int)
+#define TARGET_TIOCMSET TARGET_IOW('t', 109, int)
+#define TARGET_TIOCSTART TARGET_IO('t', 110)
+#define TARGET_TIOCSTOP TARGET_IO('t', 111)
+#define TARGET_TIOCPKT TARGET_IOW('t', 112, int)
+#define TARGET_TIOCNOTTY TARGET_IO('t', 113)
+#define TARGET_TIOCSTI TARGET_IOW('t', 114, char)
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int)
+//#define __TIOCGLTC _IOR('t', 116, struct ltchars) /* SunOS Specific */
+//#define __TIOCSLTC _IOW('t', 117, struct ltchars) /* SunOS Specific */
+/* 118 is the non-posix setpgrp tty ioctl */
+/* 119 is the non-posix getpgrp tty ioctl */
+//#define __TIOCCDTR TARGET_IO('t', 120) /* SunOS Specific */
+//#define __TIOCSDTR TARGET_IO('t', 121) /* SunOS Specific */
+#define TARGET_TIOCCBRK TARGET_IO('t', 122)
+#define TARGET_TIOCSBRK TARGET_IO('t', 123)
+//#define __TIOCLGET TARGET_IOW('t', 124, int) /* SunOS Specific */
+//#define __TIOCLSET TARGET_IOW('t', 125, int) /* SunOS Specific */
+//#define __TIOCLBIC TARGET_IOW('t', 126, int) /* SunOS Specific */
+//#define __TIOCLBIS TARGET_IOW('t', 127, int) /* SunOS Specific */
+//#define __TIOCISPACE TARGET_IOR('t', 128, int) /* SunOS Specific */
+//#define __TIOCISIZE TARGET_IOR('t', 129, int) /* SunOS Specific */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 130, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 131, int)
+#define TARGET_TIOCSCTTY TARGET_IO('t', 132)
+#define TARGET_TIOCGSID TARGET_IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
+#define TARGET_TIOCGPTN TARGET_IOR('t', 134, unsigned int) /* Get Pty Number */
+#define TARGET_TIOCSPTLCK TARGET_IOW('t', 135, int) /* Lock/unlock PTY */
+
+/* Little f */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
+ * someday. This is completely bogus, I know...
+ */
+//#define __TCGETSTAT TARGET_IO('T', 200) /* Rutgers specific */
+//#define __TCSETSTAT TARGET_IO('T', 201) /* Rutgers specific */
+
+/* Linux specific, no SunOS equivalent. */
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TCSBRKP 0x5425
+#define TARGET_TIOCTTYGSTRUCT 0x5426
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x545C /* Wait input */
+#define TARGET_TIOCGICOUNT 0x545D /* Read serial port inline interrupt counts */
+
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
new file mode 100644
index 0000000..60ed9a6
--- /dev/null
+++ b/linux-user/syscall.c
@@ -0,0 +1,3841 @@
+/*
+ * Linux syscalls
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <elf.h>
+#include <endian.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/swap.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/times.h>
+#include <sys/shm.h>
+#include <sys/statfs.h>
+#include <utime.h>
+#include <sys/sysinfo.h>
+//#include <sys/user.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#define termios host_termios
+#define winsize host_winsize
+#define termio host_termio
+#define sgttyb host_sgttyb /* same as target */
+#define tchars host_tchars /* same as target */
+#define ltchars host_ltchars /* same as target */
+
+#include <linux/termios.h>
+#include <linux/unistd.h>
+#include <linux/utsname.h>
+#include <linux/cdrom.h>
+#include <linux/hdreg.h>
+#include <linux/soundcard.h>
+#include <linux/dirent.h>
+#include <linux/kd.h>
+
+#include "qemu.h"
+
+//#define DEBUG
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC)
+/* 16 bit uid wrappers emulation */
+#define USE_UID16
+#endif
+
+//#include <linux/msdos_fs.h>
+#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
+#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])
+
+
+#undef _syscall0
+#undef _syscall1
+#undef _syscall2
+#undef _syscall3
+#undef _syscall4
+#undef _syscall5
+#undef _syscall6
+
+#define _syscall0(type,name) \
+type name (void) \
+{ \
+ return syscall(__NR_##name); \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name (type1 arg1) \
+{ \
+ return syscall(__NR_##name, arg1); \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name (type1 arg1,type2 arg2) \
+{ \
+ return syscall(__NR_##name, arg1, arg2); \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name (type1 arg1,type2 arg2,type3 arg3) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3); \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4); \
+}
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \
+}
+
+
+#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \
+}
+
+
+#define __NR_sys_uname __NR_uname
+#define __NR_sys_getcwd1 __NR_getcwd
+#define __NR_sys_getdents __NR_getdents
+#define __NR_sys_getdents64 __NR_getdents64
+#define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo
+
+#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
+#define __NR__llseek __NR_lseek
+#endif
+
+#ifdef __NR_gettid
+_syscall0(int, gettid)
+#else
+static int gettid(void) {
+ return -ENOSYS;
+}
+#endif
+_syscall1(int,sys_uname,struct new_utsname *,buf)
+_syscall2(int,sys_getcwd1,char *,buf,size_t,size)
+_syscall3(int, sys_getdents, uint, fd, struct dirent *, dirp, uint, count);
+_syscall3(int, sys_getdents64, uint, fd, struct dirent64 *, dirp, uint, count);
+_syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
+ loff_t *, res, uint, wh);
+_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
+#ifdef __NR_exit_group
+_syscall1(int,exit_group,int,error_code)
+#endif
+
+extern int personality(int);
+extern int flock(int, int);
+extern int setfsuid(int);
+extern int setfsgid(int);
+extern int setresuid(uid_t, uid_t, uid_t);
+extern int getresuid(uid_t *, uid_t *, uid_t *);
+extern int setresgid(gid_t, gid_t, gid_t);
+extern int getresgid(gid_t *, gid_t *, gid_t *);
+extern int setgroups(int, gid_t *);
+
+static inline long get_errno(long ret)
+{
+ if (ret == -1)
+ return -errno;
+ else
+ return ret;
+}
+
+static inline int is_error(long ret)
+{
+ return (unsigned long)ret >= (unsigned long)(-4096);
+}
+
+static target_ulong target_brk;
+static target_ulong target_original_brk;
+
+void target_set_brk(target_ulong new_brk)
+{
+ target_original_brk = target_brk = new_brk;
+}
+
+long do_brk(target_ulong new_brk)
+{
+ target_ulong brk_page;
+ long mapped_addr;
+ int new_alloc_size;
+
+ if (!new_brk)
+ return target_brk;
+ if (new_brk < target_original_brk)
+ return -ENOMEM;
+
+ brk_page = HOST_PAGE_ALIGN(target_brk);
+
+ /* If the new brk is less than this, set it and we're done... */
+ if (new_brk < brk_page) {
+ target_brk = new_brk;
+ return target_brk;
+ }
+
+ /* We need to allocate more memory after the brk... */
+ new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
+ mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0));
+ if (is_error(mapped_addr)) {
+ return mapped_addr;
+ } else {
+ target_brk = new_brk;
+ return target_brk;
+ }
+}
+
+static inline fd_set *target_to_host_fds(fd_set *fds,
+ target_long *target_fds, int n)
+{
+#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
+ return (fd_set *)target_fds;
+#else
+ int i, b;
+ if (target_fds) {
+ FD_ZERO(fds);
+ for(i = 0;i < n; i++) {
+ b = (tswapl(target_fds[i / TARGET_LONG_BITS]) >>
+ (i & (TARGET_LONG_BITS - 1))) & 1;
+ if (b)
+ FD_SET(i, fds);
+ }
+ return fds;
+ } else {
+ return NULL;
+ }
+#endif
+}
+
+static inline void host_to_target_fds(target_long *target_fds,
+ fd_set *fds, int n)
+{
+#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
+ /* nothing to do */
+#else
+ int i, nw, j, k;
+ target_long v;
+
+ if (target_fds) {
+ nw = (n + TARGET_LONG_BITS - 1) / TARGET_LONG_BITS;
+ k = 0;
+ for(i = 0;i < nw; i++) {
+ v = 0;
+ for(j = 0; j < TARGET_LONG_BITS; j++) {
+ v |= ((FD_ISSET(k, fds) != 0) << j);
+ k++;
+ }
+ target_fds[i] = tswapl(v);
+ }
+ }
+#endif
+}
+
+#if defined(__alpha__)
+#define HOST_HZ 1024
+#else
+#define HOST_HZ 100
+#endif
+
+static inline long host_to_target_clock_t(long ticks)
+{
+#if HOST_HZ == TARGET_HZ
+ return ticks;
+#else
+ return ((int64_t)ticks * TARGET_HZ) / HOST_HZ;
+#endif
+}
+
+static inline void host_to_target_rusage(target_ulong target_addr,
+ const struct rusage *rusage)
+{
+ struct target_rusage *target_rusage;
+
+ lock_user_struct(target_rusage, target_addr, 0);
+ target_rusage->ru_utime.tv_sec = tswapl(rusage->ru_utime.tv_sec);
+ target_rusage->ru_utime.tv_usec = tswapl(rusage->ru_utime.tv_usec);
+ target_rusage->ru_stime.tv_sec = tswapl(rusage->ru_stime.tv_sec);
+ target_rusage->ru_stime.tv_usec = tswapl(rusage->ru_stime.tv_usec);
+ target_rusage->ru_maxrss = tswapl(rusage->ru_maxrss);
+ target_rusage->ru_ixrss = tswapl(rusage->ru_ixrss);
+ target_rusage->ru_idrss = tswapl(rusage->ru_idrss);
+ target_rusage->ru_isrss = tswapl(rusage->ru_isrss);
+ target_rusage->ru_minflt = tswapl(rusage->ru_minflt);
+ target_rusage->ru_majflt = tswapl(rusage->ru_majflt);
+ target_rusage->ru_nswap = tswapl(rusage->ru_nswap);
+ target_rusage->ru_inblock = tswapl(rusage->ru_inblock);
+ target_rusage->ru_oublock = tswapl(rusage->ru_oublock);
+ target_rusage->ru_msgsnd = tswapl(rusage->ru_msgsnd);
+ target_rusage->ru_msgrcv = tswapl(rusage->ru_msgrcv);
+ target_rusage->ru_nsignals = tswapl(rusage->ru_nsignals);
+ target_rusage->ru_nvcsw = tswapl(rusage->ru_nvcsw);
+ target_rusage->ru_nivcsw = tswapl(rusage->ru_nivcsw);
+ unlock_user_struct(target_rusage, target_addr, 1);
+}
+
+static inline void target_to_host_timeval(struct timeval *tv,
+ target_ulong target_addr)
+{
+ struct target_timeval *target_tv;
+
+ lock_user_struct(target_tv, target_addr, 1);
+ tv->tv_sec = tswapl(target_tv->tv_sec);
+ tv->tv_usec = tswapl(target_tv->tv_usec);
+ unlock_user_struct(target_tv, target_addr, 0);
+}
+
+static inline void host_to_target_timeval(target_ulong target_addr,
+ const struct timeval *tv)
+{
+ struct target_timeval *target_tv;
+
+ lock_user_struct(target_tv, target_addr, 0);
+ target_tv->tv_sec = tswapl(tv->tv_sec);
+ target_tv->tv_usec = tswapl(tv->tv_usec);
+ unlock_user_struct(target_tv, target_addr, 1);
+}
+
+
+static long do_select(long n,
+ target_ulong rfd_p, target_ulong wfd_p,
+ target_ulong efd_p, target_ulong target_tv)
+{
+ fd_set rfds, wfds, efds;
+ fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
+ target_long *target_rfds, *target_wfds, *target_efds;
+ struct timeval tv, *tv_ptr;
+ long ret;
+ int ok;
+
+ if (rfd_p) {
+ target_rfds = lock_user(rfd_p, sizeof(target_long) * n, 1);
+ rfds_ptr = target_to_host_fds(&rfds, target_rfds, n);
+ } else {
+ target_rfds = NULL;
+ rfds_ptr = NULL;
+ }
+ if (wfd_p) {
+ target_wfds = lock_user(wfd_p, sizeof(target_long) * n, 1);
+ wfds_ptr = target_to_host_fds(&wfds, target_wfds, n);
+ } else {
+ target_wfds = NULL;
+ wfds_ptr = NULL;
+ }
+ if (efd_p) {
+ target_efds = lock_user(efd_p, sizeof(target_long) * n, 1);
+ efds_ptr = target_to_host_fds(&efds, target_efds, n);
+ } else {
+ target_efds = NULL;
+ efds_ptr = NULL;
+ }
+
+ if (target_tv) {
+ target_to_host_timeval(&tv, target_tv);
+ tv_ptr = &tv;
+ } else {
+ tv_ptr = NULL;
+ }
+ ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr));
+ ok = !is_error(ret);
+
+ if (ok) {
+ host_to_target_fds(target_rfds, rfds_ptr, n);
+ host_to_target_fds(target_wfds, wfds_ptr, n);
+ host_to_target_fds(target_efds, efds_ptr, n);
+
+ if (target_tv) {
+ host_to_target_timeval(target_tv, &tv);
+ }
+ }
+ if (target_rfds)
+ unlock_user(target_rfds, rfd_p, ok ? sizeof(target_long) * n : 0);
+ if (target_wfds)
+ unlock_user(target_wfds, wfd_p, ok ? sizeof(target_long) * n : 0);
+ if (target_efds)
+ unlock_user(target_efds, efd_p, ok ? sizeof(target_long) * n : 0);
+
+ return ret;
+}
+
+static inline void target_to_host_sockaddr(struct sockaddr *addr,
+ target_ulong target_addr,
+ socklen_t len)
+{
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(target_addr, len, 1);
+ memcpy(addr, target_saddr, len);
+ addr->sa_family = tswap16(target_saddr->sa_family);
+ unlock_user(target_saddr, target_addr, 0);
+}
+
+static inline void host_to_target_sockaddr(target_ulong target_addr,
+ struct sockaddr *addr,
+ socklen_t len)
+{
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(target_addr, len, 0);
+ memcpy(target_saddr, addr, len);
+ target_saddr->sa_family = tswap16(addr->sa_family);
+ unlock_user(target_saddr, target_addr, len);
+}
+
+/* ??? Should this also swap msgh->name? */
+static inline void target_to_host_cmsg(struct msghdr *msgh,
+ struct target_msghdr *target_msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
+ socklen_t space = 0;
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+
+ int len = tswapl(target_cmsg->cmsg_len)
+ - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr));
+
+ space += CMSG_SPACE(len);
+ if (space > msgh->msg_controllen) {
+ space -= CMSG_SPACE(len);
+ gemu_log("Host cmsg overflow\n");
+ break;
+ }
+
+ cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
+ cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
+ cmsg->cmsg_len = CMSG_LEN(len);
+
+ if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(data, target_data, len);
+ } else {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = len / sizeof(int);
+
+ for (i = 0; i < numfds; i++)
+ fd[i] = tswap32(target_fd[i]);
+ }
+
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
+ }
+
+ msgh->msg_controllen = space;
+}
+
+/* ??? Should this also swap msgh->name? */
+static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
+ struct msghdr *msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
+ socklen_t space = 0;
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+
+ int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
+
+ space += TARGET_CMSG_SPACE(len);
+ if (space > tswapl(target_msgh->msg_controllen)) {
+ space -= TARGET_CMSG_SPACE(len);
+ gemu_log("Target cmsg overflow\n");
+ break;
+ }
+
+ target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
+ target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
+ target_cmsg->cmsg_len = tswapl(TARGET_CMSG_LEN(len));
+
+ if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(target_data, data, len);
+ } else {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = len / sizeof(int);
+
+ for (i = 0; i < numfds; i++)
+ target_fd[i] = tswap32(fd[i]);
+ }
+
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
+ }
+
+ msgh->msg_controllen = tswapl(space);
+}
+
+static long do_setsockopt(int sockfd, int level, int optname,
+ target_ulong optval, socklen_t optlen)
+{
+ int val, ret;
+
+ switch(level) {
+ case SOL_TCP:
+ /* TCP options all take an 'int' value. */
+ if (optlen < sizeof(uint32_t))
+ return -EINVAL;
+
+ val = tget32(optval);
+ ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
+ break;
+ case SOL_IP:
+ switch(optname) {
+ case IP_TOS:
+ case IP_TTL:
+ case IP_HDRINCL:
+ case IP_ROUTER_ALERT:
+ case IP_RECVOPTS:
+ case IP_RETOPTS:
+ case IP_PKTINFO:
+ case IP_MTU_DISCOVER:
+ case IP_RECVERR:
+ case IP_RECVTOS:
+#ifdef IP_FREEBIND
+ case IP_FREEBIND:
+#endif
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ val = 0;
+ if (optlen >= sizeof(uint32_t)) {
+ val = tget32(optval);
+ } else if (optlen >= 1) {
+ val = tget8(optval);
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
+ break;
+ default:
+ goto unimplemented;
+ }
+ break;
+ case TARGET_SOL_SOCKET:
+ switch (optname) {
+ /* Options with 'int' argument. */
+ case TARGET_SO_DEBUG:
+ optname = SO_DEBUG;
+ break;
+ case TARGET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ break;
+ case TARGET_SO_TYPE:
+ optname = SO_TYPE;
+ break;
+ case TARGET_SO_ERROR:
+ optname = SO_ERROR;
+ break;
+ case TARGET_SO_DONTROUTE:
+ optname = SO_DONTROUTE;
+ break;
+ case TARGET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ break;
+ case TARGET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ break;
+ case TARGET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ break;
+ case TARGET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ break;
+ case TARGET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ break;
+ case TARGET_SO_NO_CHECK:
+ optname = SO_NO_CHECK;
+ break;
+ case TARGET_SO_PRIORITY:
+ optname = SO_PRIORITY;
+ break;
+#ifdef SO_BSDCOMPAT
+ case TARGET_SO_BSDCOMPAT:
+ optname = SO_BSDCOMPAT;
+ break;
+#endif
+ case TARGET_SO_PASSCRED:
+ optname = SO_PASSCRED;
+ break;
+ case TARGET_SO_TIMESTAMP:
+ optname = SO_TIMESTAMP;
+ break;
+ case TARGET_SO_RCVLOWAT:
+ optname = SO_RCVLOWAT;
+ break;
+ case TARGET_SO_RCVTIMEO:
+ optname = SO_RCVTIMEO;
+ break;
+ case TARGET_SO_SNDTIMEO:
+ optname = SO_SNDTIMEO;
+ break;
+ break;
+ default:
+ goto unimplemented;
+ }
+ if (optlen < sizeof(uint32_t))
+ return -EINVAL;
+
+ val = tget32(optval);
+ ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val)));
+ break;
+ default:
+ unimplemented:
+ gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname);
+ ret = -ENOSYS;
+ }
+ return ret;
+}
+
+static long do_getsockopt(int sockfd, int level, int optname,
+ target_ulong optval, target_ulong optlen)
+{
+ int len, lv, val, ret;
+
+ switch(level) {
+ case TARGET_SOL_SOCKET:
+ level = SOL_SOCKET;
+ switch (optname) {
+ case TARGET_SO_LINGER:
+ case TARGET_SO_RCVTIMEO:
+ case TARGET_SO_SNDTIMEO:
+ case TARGET_SO_PEERCRED:
+ case TARGET_SO_PEERNAME:
+ /* These don't just return a single integer */
+ goto unimplemented;
+ default:
+ goto int_case;
+ }
+ break;
+ case SOL_TCP:
+ /* TCP options all take an 'int' value. */
+ int_case:
+ len = tget32(optlen);
+ if (len < 0)
+ return -EINVAL;
+ lv = sizeof(int);
+ ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
+ if (ret < 0)
+ return ret;
+ val = tswap32(val);
+ if (len > lv)
+ len = lv;
+ if (len == 4)
+ tput32(optval, val);
+ else
+ tput8(optval, val);
+ tput32(optlen, len);
+ break;
+ case SOL_IP:
+ switch(optname) {
+ case IP_TOS:
+ case IP_TTL:
+ case IP_HDRINCL:
+ case IP_ROUTER_ALERT:
+ case IP_RECVOPTS:
+ case IP_RETOPTS:
+ case IP_PKTINFO:
+ case IP_MTU_DISCOVER:
+ case IP_RECVERR:
+ case IP_RECVTOS:
+#ifdef IP_FREEBIND
+ case IP_FREEBIND:
+#endif
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ len = tget32(optlen);
+ if (len < 0)
+ return -EINVAL;
+ lv = sizeof(int);
+ ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
+ len = 1;
+ tput32(optlen, len);
+ tput8(optval, val);
+ } else {
+ if (len > sizeof(int))
+ len = sizeof(int);
+ tput32(optlen, len);
+ tput32(optval, val);
+ }
+ break;
+ default:
+ goto unimplemented;
+ }
+ break;
+ default:
+ unimplemented:
+ gemu_log("getsockopt level=%d optname=%d not yet supported\n",
+ level, optname);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+static void lock_iovec(struct iovec *vec, target_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ target_ulong base;
+ int i;
+
+ target_vec = lock_user(target_addr, count * sizeof(struct target_iovec), 1);
+ for(i = 0;i < count; i++) {
+ base = tswapl(target_vec[i].iov_base);
+ vec[i].iov_len = tswapl(target_vec[i].iov_len);
+ vec[i].iov_base = lock_user(base, vec[i].iov_len, copy);
+ }
+ unlock_user (target_vec, target_addr, 0);
+}
+
+static void unlock_iovec(struct iovec *vec, target_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ target_ulong base;
+ int i;
+
+ target_vec = lock_user(target_addr, count * sizeof(struct target_iovec), 1);
+ for(i = 0;i < count; i++) {
+ base = tswapl(target_vec[i].iov_base);
+ unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
+ }
+ unlock_user (target_vec, target_addr, 0);
+}
+
+static long do_socket(int domain, int type, int protocol)
+{
+#if defined(TARGET_MIPS)
+ switch(type) {
+ case TARGET_SOCK_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ case TARGET_SOCK_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case TARGET_SOCK_RAW:
+ type = SOCK_RAW;
+ break;
+ case TARGET_SOCK_RDM:
+ type = SOCK_RDM;
+ break;
+ case TARGET_SOCK_SEQPACKET:
+ type = SOCK_SEQPACKET;
+ break;
+ case TARGET_SOCK_PACKET:
+ type = SOCK_PACKET;
+ break;
+ }
+#endif
+ return get_errno(socket(domain, type, protocol));
+}
+
+static long do_bind(int sockfd, target_ulong target_addr,
+ socklen_t addrlen)
+{
+ void *addr = alloca(addrlen);
+
+ target_to_host_sockaddr(addr, target_addr, addrlen);
+ return get_errno(bind(sockfd, addr, addrlen));
+}
+
+static long do_connect(int sockfd, target_ulong target_addr,
+ socklen_t addrlen)
+{
+ void *addr = alloca(addrlen);
+
+ target_to_host_sockaddr(addr, target_addr, addrlen);
+ return get_errno(connect(sockfd, addr, addrlen));
+}
+
+static long do_sendrecvmsg(int fd, target_ulong target_msg,
+ int flags, int send)
+{
+ long ret;
+ struct target_msghdr *msgp;
+ struct msghdr msg;
+ int count;
+ struct iovec *vec;
+ target_ulong target_vec;
+
+ lock_user_struct(msgp, target_msg, 1);
+ if (msgp->msg_name) {
+ msg.msg_namelen = tswap32(msgp->msg_namelen);
+ msg.msg_name = alloca(msg.msg_namelen);
+ target_to_host_sockaddr(msg.msg_name, tswapl(msgp->msg_name),
+ msg.msg_namelen);
+ } else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
+ msg.msg_controllen = 2 * tswapl(msgp->msg_controllen);
+ msg.msg_control = alloca(msg.msg_controllen);
+ msg.msg_flags = tswap32(msgp->msg_flags);
+
+ count = tswapl(msgp->msg_iovlen);
+ vec = alloca(count * sizeof(struct iovec));
+ target_vec = tswapl(msgp->msg_iov);
+ lock_iovec(vec, target_vec, count, send);
+ msg.msg_iovlen = count;
+ msg.msg_iov = vec;
+
+ if (send) {
+ target_to_host_cmsg(&msg, msgp);
+ ret = get_errno(sendmsg(fd, &msg, flags));
+ } else {
+ ret = get_errno(recvmsg(fd, &msg, flags));
+ if (!is_error(ret))
+ host_to_target_cmsg(msgp, &msg);
+ }
+ unlock_iovec(vec, target_vec, count, !send);
+ return ret;
+}
+
+static long do_socketcall(int num, target_ulong vptr)
+{
+ long ret;
+ const int n = sizeof(target_ulong);
+
+ switch(num) {
+ case SOCKOP_socket:
+ {
+ int domain = tgetl(vptr);
+ int type = tgetl(vptr + n);
+ int protocol = tgetl(vptr + 2 * n);
+ ret = do_socket(domain, type, protocol);
+ }
+ break;
+ case SOCKOP_bind:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong target_addr = tgetl(vptr + n);
+ socklen_t addrlen = tgetl(vptr + 2 * n);
+ ret = do_bind(sockfd, target_addr, addrlen);
+ }
+ break;
+ case SOCKOP_connect:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong target_addr = tgetl(vptr + n);
+ socklen_t addrlen = tgetl(vptr + 2 * n);
+ ret = do_connect(sockfd, target_addr, addrlen);
+ }
+ break;
+ case SOCKOP_listen:
+ {
+ int sockfd = tgetl(vptr);
+ int backlog = tgetl(vptr + n);
+ ret = get_errno(listen(sockfd, backlog));
+ }
+ break;
+ case SOCKOP_accept:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong target_addr = tgetl(vptr + n);
+ target_ulong target_addrlen = tgetl(vptr + 2 * n);
+ socklen_t addrlen = tget32(target_addrlen);
+ void *addr = alloca(addrlen);
+
+ ret = get_errno(accept(sockfd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ tput32(target_addrlen, addrlen);
+ }
+ }
+ break;
+ case SOCKOP_getsockname:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong target_addr = tgetl(vptr + n);
+ target_ulong target_addrlen = tgetl(vptr + 2 * n);
+ socklen_t addrlen = tget32(target_addrlen);
+ void *addr = alloca(addrlen);
+
+ ret = get_errno(getsockname(sockfd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ tput32(target_addrlen, addrlen);
+ }
+ }
+ break;
+ case SOCKOP_getpeername:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong target_addr = tgetl(vptr + n);
+ target_ulong target_addrlen = tgetl(vptr + 2 * n);
+ socklen_t addrlen = tget32(target_addrlen);
+ void *addr = alloca(addrlen);
+
+ ret = get_errno(getpeername(sockfd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ tput32(target_addrlen, addrlen);
+ }
+ }
+ break;
+ case SOCKOP_socketpair:
+ {
+ int domain = tgetl(vptr);
+ int type = tgetl(vptr + n);
+ int protocol = tgetl(vptr + 2 * n);
+ target_ulong target_tab = tgetl(vptr + 3 * n);
+ int tab[2];
+
+ ret = get_errno(socketpair(domain, type, protocol, tab));
+ if (!is_error(ret)) {
+ tput32(target_tab, tab[0]);
+ tput32(target_tab + 4, tab[1]);
+ }
+ }
+ break;
+ case SOCKOP_send:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong msg = tgetl(vptr + n);
+ size_t len = tgetl(vptr + 2 * n);
+ int flags = tgetl(vptr + 3 * n);
+ void *host_msg;
+
+ host_msg = lock_user(msg, len, 1);
+ ret = get_errno(send(sockfd, host_msg, len, flags));
+ unlock_user(host_msg, msg, 0);
+ }
+ break;
+ case SOCKOP_recv:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong msg = tgetl(vptr + n);
+ size_t len = tgetl(vptr + 2 * n);
+ int flags = tgetl(vptr + 3 * n);
+ void *host_msg;
+
+ host_msg = lock_user(msg, len, 0);
+ ret = get_errno(recv(sockfd, host_msg, len, flags));
+ unlock_user(host_msg, msg, ret);
+ }
+ break;
+ case SOCKOP_sendto:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong msg = tgetl(vptr + n);
+ size_t len = tgetl(vptr + 2 * n);
+ int flags = tgetl(vptr + 3 * n);
+ target_ulong target_addr = tgetl(vptr + 4 * n);
+ socklen_t addrlen = tgetl(vptr + 5 * n);
+ void *addr = alloca(addrlen);
+ void *host_msg;
+
+ host_msg = lock_user(msg, len, 1);
+ target_to_host_sockaddr(addr, target_addr, addrlen);
+ ret = get_errno(sendto(sockfd, host_msg, len, flags, addr, addrlen));
+ unlock_user(host_msg, msg, 0);
+ }
+ break;
+ case SOCKOP_recvfrom:
+ {
+ int sockfd = tgetl(vptr);
+ target_ulong msg = tgetl(vptr + n);
+ size_t len = tgetl(vptr + 2 * n);
+ int flags = tgetl(vptr + 3 * n);
+ target_ulong target_addr = tgetl(vptr + 4 * n);
+ target_ulong target_addrlen = tgetl(vptr + 5 * n);
+ socklen_t addrlen = tget32(target_addrlen);
+ void *addr = alloca(addrlen);
+ void *host_msg;
+
+ host_msg = lock_user(msg, len, 0);
+ ret = get_errno(recvfrom(sockfd, host_msg, len, flags, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ tput32(target_addrlen, addrlen);
+ unlock_user(host_msg, msg, len);
+ } else {
+ unlock_user(host_msg, msg, 0);
+ }
+ }
+ break;
+ case SOCKOP_shutdown:
+ {
+ int sockfd = tgetl(vptr);
+ int how = tgetl(vptr + n);
+
+ ret = get_errno(shutdown(sockfd, how));
+ }
+ break;
+ case SOCKOP_sendmsg:
+ case SOCKOP_recvmsg:
+ {
+ int fd;
+ target_ulong target_msg;
+ int flags;
+
+ fd = tgetl(vptr);
+ target_msg = tgetl(vptr + n);
+ flags = tgetl(vptr + 2 * n);
+
+ ret = do_sendrecvmsg(fd, target_msg, flags,
+ (num == SOCKOP_sendmsg));
+ }
+ break;
+ case SOCKOP_setsockopt:
+ {
+ int sockfd = tgetl(vptr);
+ int level = tgetl(vptr + n);
+ int optname = tgetl(vptr + 2 * n);
+ target_ulong optval = tgetl(vptr + 3 * n);
+ socklen_t optlen = tgetl(vptr + 4 * n);
+
+ ret = do_setsockopt(sockfd, level, optname, optval, optlen);
+ }
+ break;
+ case SOCKOP_getsockopt:
+ {
+ int sockfd = tgetl(vptr);
+ int level = tgetl(vptr + n);
+ int optname = tgetl(vptr + 2 * n);
+ target_ulong optval = tgetl(vptr + 3 * n);
+ target_ulong poptlen = tgetl(vptr + 4 * n);
+
+ ret = do_getsockopt(sockfd, level, optname, optval, poptlen);
+ }
+ break;
+ default:
+ gemu_log("Unsupported socketcall: %d\n", num);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+/* XXX: suppress this function and call directly the related socket
+ functions */
+static long do_socketcallwrapper(int num, long arg1, long arg2, long arg3,
+ long arg4, long arg5, long arg6)
+{
+ target_long args[6];
+
+ tputl(args, arg1);
+ tputl(args+1, arg2);
+ tputl(args+2, arg3);
+ tputl(args+3, arg4);
+ tputl(args+4, arg5);
+ tputl(args+5, arg6);
+
+ return do_socketcall(num, (target_ulong) args);
+}
+
+#define N_SHM_REGIONS 32
+
+static struct shm_region {
+ uint32_t start;
+ uint32_t size;
+} shm_regions[N_SHM_REGIONS];
+
+/* ??? This only works with linear mappings. */
+static long do_ipc(long call, long first, long second, long third,
+ long ptr, long fifth)
+{
+ int version;
+ long ret = 0;
+ unsigned long raddr;
+ struct shmid_ds shm_info;
+ int i;
+
+ version = call >> 16;
+ call &= 0xffff;
+
+ switch (call) {
+ case IPCOP_shmat:
+ /* SHM_* flags are the same on all linux platforms */
+ ret = get_errno((long) shmat(first, (void *) ptr, second));
+ if (is_error(ret))
+ break;
+ raddr = ret;
+ /* find out the length of the shared memory segment */
+
+ ret = get_errno(shmctl(first, IPC_STAT, &shm_info));
+ if (is_error(ret)) {
+ /* can't get length, bail out */
+ shmdt((void *) raddr);
+ break;
+ }
+ page_set_flags(raddr, raddr + shm_info.shm_segsz,
+ PAGE_VALID | PAGE_READ |
+ ((second & SHM_RDONLY)? 0: PAGE_WRITE));
+ for (i = 0; i < N_SHM_REGIONS; ++i) {
+ if (shm_regions[i].start == 0) {
+ shm_regions[i].start = raddr;
+ shm_regions[i].size = shm_info.shm_segsz;
+ break;
+ }
+ }
+ if (put_user(raddr, (uint32_t *)third))
+ return -EFAULT;
+ ret = 0;
+ break;
+ case IPCOP_shmdt:
+ for (i = 0; i < N_SHM_REGIONS; ++i) {
+ if (shm_regions[i].start == ptr) {
+ shm_regions[i].start = 0;
+ page_set_flags(ptr, shm_regions[i].size, 0);
+ break;
+ }
+ }
+ ret = get_errno(shmdt((void *) ptr));
+ break;
+
+ case IPCOP_shmget:
+ /* IPC_* flag values are the same on all linux platforms */
+ ret = get_errno(shmget(first, second, third));
+ break;
+
+ /* IPC_* and SHM_* command values are the same on all linux platforms */
+ case IPCOP_shmctl:
+ switch(second) {
+ case IPC_RMID:
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ ret = get_errno(shmctl(first, second, NULL));
+ break;
+ default:
+ goto unimplemented;
+ }
+ break;
+ default:
+ unimplemented:
+ gemu_log("Unsupported ipc call: %ld (version %d)\n", call, version);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+/* kernel structure types definitions */
+#define IFNAMSIZ 16
+
+#define STRUCT(name, list...) STRUCT_ ## name,
+#define STRUCT_SPECIAL(name) STRUCT_ ## name,
+enum {
+#include "syscall_types.h"
+};
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+#define STRUCT(name, list...) const argtype struct_ ## name ## _def[] = { list, TYPE_NULL };
+#define STRUCT_SPECIAL(name)
+#include "syscall_types.h"
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+typedef struct IOCTLEntry {
+ unsigned int target_cmd;
+ unsigned int host_cmd;
+ const char *name;
+ int access;
+ const argtype arg_type[5];
+} IOCTLEntry;
+
+#define IOC_R 0x0001
+#define IOC_W 0x0002
+#define IOC_RW (IOC_R | IOC_W)
+
+#define MAX_STRUCT_SIZE 4096
+
+IOCTLEntry ioctl_entries[] = {
+#define IOCTL(cmd, access, types...) \
+ { TARGET_ ## cmd, cmd, #cmd, access, { types } },
+#include "ioctls.h"
+ { 0, 0, },
+};
+
+/* ??? Implement proper locking for ioctls. */
+static long do_ioctl(long fd, long cmd, long arg)
+{
+ const IOCTLEntry *ie;
+ const argtype *arg_type;
+ long ret;
+ uint8_t buf_temp[MAX_STRUCT_SIZE];
+ int target_size;
+ void *argptr;
+
+ ie = ioctl_entries;
+ for(;;) {
+ if (ie->target_cmd == 0) {
+ gemu_log("Unsupported ioctl: cmd=0x%04lx\n", cmd);
+ return -ENOSYS;
+ }
+ if (ie->target_cmd == cmd)
+ break;
+ ie++;
+ }
+ arg_type = ie->arg_type;
+#if defined(DEBUG)
+ gemu_log("ioctl: cmd=0x%04lx (%s)\n", cmd, ie->name);
+#endif
+ switch(arg_type[0]) {
+ case TYPE_NULL:
+ /* no argument */
+ ret = get_errno(ioctl(fd, ie->host_cmd));
+ break;
+ case TYPE_PTRVOID:
+ case TYPE_INT:
+ /* int argment */
+ ret = get_errno(ioctl(fd, ie->host_cmd, arg));
+ break;
+ case TYPE_PTR:
+ arg_type++;
+ target_size = thunk_type_size(arg_type, 0);
+ switch(ie->access) {
+ case IOC_R:
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(arg, target_size, 0);
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ case IOC_W:
+ argptr = lock_user(arg, target_size, 1);
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ break;
+ default:
+ case IOC_RW:
+ argptr = lock_user(arg, target_size, 1);
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(arg, target_size, 0);
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ }
+ break;
+ default:
+ gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n", cmd, arg_type[0]);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+bitmask_transtbl iflag_tbl[] = {
+ { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK },
+ { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT },
+ { TARGET_IGNPAR, TARGET_IGNPAR, IGNPAR, IGNPAR },
+ { TARGET_PARMRK, TARGET_PARMRK, PARMRK, PARMRK },
+ { TARGET_INPCK, TARGET_INPCK, INPCK, INPCK },
+ { TARGET_ISTRIP, TARGET_ISTRIP, ISTRIP, ISTRIP },
+ { TARGET_INLCR, TARGET_INLCR, INLCR, INLCR },
+ { TARGET_IGNCR, TARGET_IGNCR, IGNCR, IGNCR },
+ { TARGET_ICRNL, TARGET_ICRNL, ICRNL, ICRNL },
+ { TARGET_IUCLC, TARGET_IUCLC, IUCLC, IUCLC },
+ { TARGET_IXON, TARGET_IXON, IXON, IXON },
+ { TARGET_IXANY, TARGET_IXANY, IXANY, IXANY },
+ { TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF },
+ { TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL },
+ { 0, 0, 0, 0 }
+};
+
+bitmask_transtbl oflag_tbl[] = {
+ { TARGET_OPOST, TARGET_OPOST, OPOST, OPOST },
+ { TARGET_OLCUC, TARGET_OLCUC, OLCUC, OLCUC },
+ { TARGET_ONLCR, TARGET_ONLCR, ONLCR, ONLCR },
+ { TARGET_OCRNL, TARGET_OCRNL, OCRNL, OCRNL },
+ { TARGET_ONOCR, TARGET_ONOCR, ONOCR, ONOCR },
+ { TARGET_ONLRET, TARGET_ONLRET, ONLRET, ONLRET },
+ { TARGET_OFILL, TARGET_OFILL, OFILL, OFILL },
+ { TARGET_OFDEL, TARGET_OFDEL, OFDEL, OFDEL },
+ { TARGET_NLDLY, TARGET_NL0, NLDLY, NL0 },
+ { TARGET_NLDLY, TARGET_NL1, NLDLY, NL1 },
+ { TARGET_CRDLY, TARGET_CR0, CRDLY, CR0 },
+ { TARGET_CRDLY, TARGET_CR1, CRDLY, CR1 },
+ { TARGET_CRDLY, TARGET_CR2, CRDLY, CR2 },
+ { TARGET_CRDLY, TARGET_CR3, CRDLY, CR3 },
+ { TARGET_TABDLY, TARGET_TAB0, TABDLY, TAB0 },
+ { TARGET_TABDLY, TARGET_TAB1, TABDLY, TAB1 },
+ { TARGET_TABDLY, TARGET_TAB2, TABDLY, TAB2 },
+ { TARGET_TABDLY, TARGET_TAB3, TABDLY, TAB3 },
+ { TARGET_BSDLY, TARGET_BS0, BSDLY, BS0 },
+ { TARGET_BSDLY, TARGET_BS1, BSDLY, BS1 },
+ { TARGET_VTDLY, TARGET_VT0, VTDLY, VT0 },
+ { TARGET_VTDLY, TARGET_VT1, VTDLY, VT1 },
+ { TARGET_FFDLY, TARGET_FF0, FFDLY, FF0 },
+ { TARGET_FFDLY, TARGET_FF1, FFDLY, FF1 },
+ { 0, 0, 0, 0 }
+};
+
+bitmask_transtbl cflag_tbl[] = {
+ { TARGET_CBAUD, TARGET_B0, CBAUD, B0 },
+ { TARGET_CBAUD, TARGET_B50, CBAUD, B50 },
+ { TARGET_CBAUD, TARGET_B75, CBAUD, B75 },
+ { TARGET_CBAUD, TARGET_B110, CBAUD, B110 },
+ { TARGET_CBAUD, TARGET_B134, CBAUD, B134 },
+ { TARGET_CBAUD, TARGET_B150, CBAUD, B150 },
+ { TARGET_CBAUD, TARGET_B200, CBAUD, B200 },
+ { TARGET_CBAUD, TARGET_B300, CBAUD, B300 },
+ { TARGET_CBAUD, TARGET_B600, CBAUD, B600 },
+ { TARGET_CBAUD, TARGET_B1200, CBAUD, B1200 },
+ { TARGET_CBAUD, TARGET_B1800, CBAUD, B1800 },
+ { TARGET_CBAUD, TARGET_B2400, CBAUD, B2400 },
+ { TARGET_CBAUD, TARGET_B4800, CBAUD, B4800 },
+ { TARGET_CBAUD, TARGET_B9600, CBAUD, B9600 },
+ { TARGET_CBAUD, TARGET_B19200, CBAUD, B19200 },
+ { TARGET_CBAUD, TARGET_B38400, CBAUD, B38400 },
+ { TARGET_CBAUD, TARGET_B57600, CBAUD, B57600 },
+ { TARGET_CBAUD, TARGET_B115200, CBAUD, B115200 },
+ { TARGET_CBAUD, TARGET_B230400, CBAUD, B230400 },
+ { TARGET_CBAUD, TARGET_B460800, CBAUD, B460800 },
+ { TARGET_CSIZE, TARGET_CS5, CSIZE, CS5 },
+ { TARGET_CSIZE, TARGET_CS6, CSIZE, CS6 },
+ { TARGET_CSIZE, TARGET_CS7, CSIZE, CS7 },
+ { TARGET_CSIZE, TARGET_CS8, CSIZE, CS8 },
+ { TARGET_CSTOPB, TARGET_CSTOPB, CSTOPB, CSTOPB },
+ { TARGET_CREAD, TARGET_CREAD, CREAD, CREAD },
+ { TARGET_PARENB, TARGET_PARENB, PARENB, PARENB },
+ { TARGET_PARODD, TARGET_PARODD, PARODD, PARODD },
+ { TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL },
+ { TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL },
+ { TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS },
+ { 0, 0, 0, 0 }
+};
+
+bitmask_transtbl lflag_tbl[] = {
+ { TARGET_ISIG, TARGET_ISIG, ISIG, ISIG },
+ { TARGET_ICANON, TARGET_ICANON, ICANON, ICANON },
+ { TARGET_XCASE, TARGET_XCASE, XCASE, XCASE },
+ { TARGET_ECHO, TARGET_ECHO, ECHO, ECHO },
+ { TARGET_ECHOE, TARGET_ECHOE, ECHOE, ECHOE },
+ { TARGET_ECHOK, TARGET_ECHOK, ECHOK, ECHOK },
+ { TARGET_ECHONL, TARGET_ECHONL, ECHONL, ECHONL },
+ { TARGET_NOFLSH, TARGET_NOFLSH, NOFLSH, NOFLSH },
+ { TARGET_TOSTOP, TARGET_TOSTOP, TOSTOP, TOSTOP },
+ { TARGET_ECHOCTL, TARGET_ECHOCTL, ECHOCTL, ECHOCTL },
+ { TARGET_ECHOPRT, TARGET_ECHOPRT, ECHOPRT, ECHOPRT },
+ { TARGET_ECHOKE, TARGET_ECHOKE, ECHOKE, ECHOKE },
+ { TARGET_FLUSHO, TARGET_FLUSHO, FLUSHO, FLUSHO },
+ { TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN },
+ { TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN },
+ { 0, 0, 0, 0 }
+};
+
+static void target_to_host_termios (void *dst, const void *src)
+{
+ struct host_termios *host = dst;
+ const struct target_termios *target = src;
+
+ host->c_iflag =
+ target_to_host_bitmask(tswap32(target->c_iflag), iflag_tbl);
+ host->c_oflag =
+ target_to_host_bitmask(tswap32(target->c_oflag), oflag_tbl);
+ host->c_cflag =
+ target_to_host_bitmask(tswap32(target->c_cflag), cflag_tbl);
+ host->c_lflag =
+ target_to_host_bitmask(tswap32(target->c_lflag), lflag_tbl);
+ host->c_line = target->c_line;
+
+ host->c_cc[VINTR] = target->c_cc[TARGET_VINTR];
+ host->c_cc[VQUIT] = target->c_cc[TARGET_VQUIT];
+ host->c_cc[VERASE] = target->c_cc[TARGET_VERASE];
+ host->c_cc[VKILL] = target->c_cc[TARGET_VKILL];
+ host->c_cc[VEOF] = target->c_cc[TARGET_VEOF];
+ host->c_cc[VTIME] = target->c_cc[TARGET_VTIME];
+ host->c_cc[VMIN] = target->c_cc[TARGET_VMIN];
+ host->c_cc[VSWTC] = target->c_cc[TARGET_VSWTC];
+ host->c_cc[VSTART] = target->c_cc[TARGET_VSTART];
+ host->c_cc[VSTOP] = target->c_cc[TARGET_VSTOP];
+ host->c_cc[VSUSP] = target->c_cc[TARGET_VSUSP];
+ host->c_cc[VEOL] = target->c_cc[TARGET_VEOL];
+ host->c_cc[VREPRINT] = target->c_cc[TARGET_VREPRINT];
+ host->c_cc[VDISCARD] = target->c_cc[TARGET_VDISCARD];
+ host->c_cc[VWERASE] = target->c_cc[TARGET_VWERASE];
+ host->c_cc[VLNEXT] = target->c_cc[TARGET_VLNEXT];
+ host->c_cc[VEOL2] = target->c_cc[TARGET_VEOL2];
+}
+
+static void host_to_target_termios (void *dst, const void *src)
+{
+ struct target_termios *target = dst;
+ const struct host_termios *host = src;
+
+ target->c_iflag =
+ tswap32(host_to_target_bitmask(host->c_iflag, iflag_tbl));
+ target->c_oflag =
+ tswap32(host_to_target_bitmask(host->c_oflag, oflag_tbl));
+ target->c_cflag =
+ tswap32(host_to_target_bitmask(host->c_cflag, cflag_tbl));
+ target->c_lflag =
+ tswap32(host_to_target_bitmask(host->c_lflag, lflag_tbl));
+ target->c_line = host->c_line;
+
+ target->c_cc[TARGET_VINTR] = host->c_cc[VINTR];
+ target->c_cc[TARGET_VQUIT] = host->c_cc[VQUIT];
+ target->c_cc[TARGET_VERASE] = host->c_cc[VERASE];
+ target->c_cc[TARGET_VKILL] = host->c_cc[VKILL];
+ target->c_cc[TARGET_VEOF] = host->c_cc[VEOF];
+ target->c_cc[TARGET_VTIME] = host->c_cc[VTIME];
+ target->c_cc[TARGET_VMIN] = host->c_cc[VMIN];
+ target->c_cc[TARGET_VSWTC] = host->c_cc[VSWTC];
+ target->c_cc[TARGET_VSTART] = host->c_cc[VSTART];
+ target->c_cc[TARGET_VSTOP] = host->c_cc[VSTOP];
+ target->c_cc[TARGET_VSUSP] = host->c_cc[VSUSP];
+ target->c_cc[TARGET_VEOL] = host->c_cc[VEOL];
+ target->c_cc[TARGET_VREPRINT] = host->c_cc[VREPRINT];
+ target->c_cc[TARGET_VDISCARD] = host->c_cc[VDISCARD];
+ target->c_cc[TARGET_VWERASE] = host->c_cc[VWERASE];
+ target->c_cc[TARGET_VLNEXT] = host->c_cc[VLNEXT];
+ target->c_cc[TARGET_VEOL2] = host->c_cc[VEOL2];
+}
+
+StructEntry struct_termios_def = {
+ .convert = { host_to_target_termios, target_to_host_termios },
+ .size = { sizeof(struct target_termios), sizeof(struct host_termios) },
+ .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) },
+};
+
+static bitmask_transtbl mmap_flags_tbl[] = {
+ { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED },
+ { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE },
+ { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED },
+ { TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS, MAP_ANONYMOUS, MAP_ANONYMOUS },
+ { TARGET_MAP_GROWSDOWN, TARGET_MAP_GROWSDOWN, MAP_GROWSDOWN, MAP_GROWSDOWN },
+ { TARGET_MAP_DENYWRITE, TARGET_MAP_DENYWRITE, MAP_DENYWRITE, MAP_DENYWRITE },
+ { TARGET_MAP_EXECUTABLE, TARGET_MAP_EXECUTABLE, MAP_EXECUTABLE, MAP_EXECUTABLE },
+ { TARGET_MAP_LOCKED, TARGET_MAP_LOCKED, MAP_LOCKED, MAP_LOCKED },
+ { 0, 0, 0, 0 }
+};
+
+static bitmask_transtbl fcntl_flags_tbl[] = {
+ { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, },
+ { TARGET_O_ACCMODE, TARGET_O_RDWR, O_ACCMODE, O_RDWR, },
+ { TARGET_O_CREAT, TARGET_O_CREAT, O_CREAT, O_CREAT, },
+ { TARGET_O_EXCL, TARGET_O_EXCL, O_EXCL, O_EXCL, },
+ { TARGET_O_NOCTTY, TARGET_O_NOCTTY, O_NOCTTY, O_NOCTTY, },
+ { TARGET_O_TRUNC, TARGET_O_TRUNC, O_TRUNC, O_TRUNC, },
+ { TARGET_O_APPEND, TARGET_O_APPEND, O_APPEND, O_APPEND, },
+ { TARGET_O_NONBLOCK, TARGET_O_NONBLOCK, O_NONBLOCK, O_NONBLOCK, },
+ { TARGET_O_SYNC, TARGET_O_SYNC, O_SYNC, O_SYNC, },
+ { TARGET_FASYNC, TARGET_FASYNC, FASYNC, FASYNC, },
+ { TARGET_O_DIRECTORY, TARGET_O_DIRECTORY, O_DIRECTORY, O_DIRECTORY, },
+ { TARGET_O_NOFOLLOW, TARGET_O_NOFOLLOW, O_NOFOLLOW, O_NOFOLLOW, },
+ { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, },
+#if defined(O_DIRECT)
+ { TARGET_O_DIRECT, TARGET_O_DIRECT, O_DIRECT, O_DIRECT, },
+#endif
+ { 0, 0, 0, 0 }
+};
+
+#if defined(TARGET_I386)
+
+/* NOTE: there is really one LDT for all the threads */
+uint8_t *ldt_table;
+
+static int read_ldt(target_ulong ptr, unsigned long bytecount)
+{
+ int size;
+ void *p;
+
+ if (!ldt_table)
+ return 0;
+ size = TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE;
+ if (size > bytecount)
+ size = bytecount;
+ p = lock_user(ptr, size, 0);
+ /* ??? Shoudl this by byteswapped? */
+ memcpy(p, ldt_table, size);
+ unlock_user(p, ptr, size);
+ return size;
+}
+
+/* XXX: add locking support */
+static int write_ldt(CPUX86State *env,
+ target_ulong ptr, unsigned long bytecount, int oldmode)
+{
+ struct target_modify_ldt_ldt_s ldt_info;
+ struct target_modify_ldt_ldt_s *target_ldt_info;
+ int seg_32bit, contents, read_exec_only, limit_in_pages;
+ int seg_not_present, useable;
+ uint32_t *lp, entry_1, entry_2;
+
+ if (bytecount != sizeof(ldt_info))
+ return -EINVAL;
+ lock_user_struct(target_ldt_info, ptr, 1);
+ ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
+ ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
+ ldt_info.limit = tswap32(target_ldt_info->limit);
+ ldt_info.flags = tswap32(target_ldt_info->flags);
+ unlock_user_struct(target_ldt_info, ptr, 0);
+
+ if (ldt_info.entry_number >= TARGET_LDT_ENTRIES)
+ return -EINVAL;
+ seg_32bit = ldt_info.flags & 1;
+ contents = (ldt_info.flags >> 1) & 3;
+ read_exec_only = (ldt_info.flags >> 3) & 1;
+ limit_in_pages = (ldt_info.flags >> 4) & 1;
+ seg_not_present = (ldt_info.flags >> 5) & 1;
+ useable = (ldt_info.flags >> 6) & 1;
+
+ if (contents == 3) {
+ if (oldmode)
+ return -EINVAL;
+ if (seg_not_present == 0)
+ return -EINVAL;
+ }
+ /* allocate the LDT */
+ if (!ldt_table) {
+ ldt_table = malloc(TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE);
+ if (!ldt_table)
+ return -ENOMEM;
+ memset(ldt_table, 0, TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE);
+ env->ldt.base = h2g(ldt_table);
+ env->ldt.limit = 0xffff;
+ }
+
+ /* NOTE: same code as Linux kernel */
+ /* Allow LDTs to be cleared by the user. */
+ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+ if (oldmode ||
+ (contents == 0 &&
+ read_exec_only == 1 &&
+ seg_32bit == 0 &&
+ limit_in_pages == 0 &&
+ seg_not_present == 1 &&
+ useable == 0 )) {
+ entry_1 = 0;
+ entry_2 = 0;
+ goto install;
+ }
+ }
+
+ entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+ (ldt_info.limit & 0x0ffff);
+ entry_2 = (ldt_info.base_addr & 0xff000000) |
+ ((ldt_info.base_addr & 0x00ff0000) >> 16) |
+ (ldt_info.limit & 0xf0000) |
+ ((read_exec_only ^ 1) << 9) |
+ (contents << 10) |
+ ((seg_not_present ^ 1) << 15) |
+ (seg_32bit << 22) |
+ (limit_in_pages << 23) |
+ 0x7000;
+ if (!oldmode)
+ entry_2 |= (useable << 20);
+
+ /* Install the new entry ... */
+install:
+ lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3));
+ lp[0] = tswap32(entry_1);
+ lp[1] = tswap32(entry_2);
+ return 0;
+}
+
+/* specific and weird i386 syscalls */
+int do_modify_ldt(CPUX86State *env, int func, target_ulong ptr, unsigned long bytecount)
+{
+ int ret = -ENOSYS;
+
+ switch (func) {
+ case 0:
+ ret = read_ldt(ptr, bytecount);
+ break;
+ case 1:
+ ret = write_ldt(env, ptr, bytecount, 1);
+ break;
+ case 0x11:
+ ret = write_ldt(env, ptr, bytecount, 0);
+ break;
+ }
+ return ret;
+}
+
+#endif /* defined(TARGET_I386) */
+
+/* this stack is the equivalent of the kernel stack associated with a
+ thread/process */
+#define NEW_STACK_SIZE 8192
+
+static int clone_func(void *arg)
+{
+ CPUState *env = arg;
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
+
+int do_fork(CPUState *env, unsigned int flags, unsigned long newsp)
+{
+ int ret;
+ TaskState *ts;
+ uint8_t *new_stack;
+ CPUState *new_env;
+
+ if (flags & CLONE_VM) {
+ ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
+ memset(ts, 0, sizeof(TaskState));
+ new_stack = ts->stack;
+ ts->used = 1;
+ /* add in task state list */
+ ts->next = first_task_state;
+ first_task_state = ts;
+ /* we create a new CPU instance. */
+ new_env = cpu_init();
+ memcpy(new_env, env, sizeof(CPUState));
+#if defined(TARGET_I386)
+ if (!newsp)
+ newsp = env->regs[R_ESP];
+ new_env->regs[R_ESP] = newsp;
+ new_env->regs[R_EAX] = 0;
+#elif defined(TARGET_ARM)
+ if (!newsp)
+ newsp = env->regs[13];
+ new_env->regs[13] = newsp;
+ new_env->regs[0] = 0;
+#elif defined(TARGET_SPARC)
+ if (!newsp)
+ newsp = env->regwptr[22];
+ new_env->regwptr[22] = newsp;
+ new_env->regwptr[0] = 0;
+ /* XXXXX */
+ printf ("HELPME: %s:%d\n", __FILE__, __LINE__);
+#elif defined(TARGET_MIPS)
+ printf ("HELPME: %s:%d\n", __FILE__, __LINE__);
+#elif defined(TARGET_PPC)
+ if (!newsp)
+ newsp = env->gpr[1];
+ new_env->gpr[1] = newsp;
+ {
+ int i;
+ for (i = 7; i < 32; i++)
+ new_env->gpr[i] = 0;
+ }
+#elif defined(TARGET_SH4)
+ if (!newsp)
+ newsp = env->gregs[15];
+ new_env->gregs[15] = newsp;
+ /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+ new_env->opaque = ts;
+#ifdef __ia64__
+ ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+#else
+ ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+#endif
+ } else {
+ /* if no CLONE_VM, we consider it is a fork */
+ if ((flags & ~CSIGNAL) != 0)
+ return -EINVAL;
+ ret = fork();
+ }
+ return ret;
+}
+
+static long do_fcntl(int fd, int cmd, target_ulong arg)
+{
+ struct flock fl;
+ struct target_flock *target_fl;
+ long ret;
+
+ switch(cmd) {
+ case TARGET_F_GETLK:
+ ret = fcntl(fd, cmd, &fl);
+ if (ret == 0) {
+ lock_user_struct(target_fl, arg, 0);
+ target_fl->l_type = tswap16(fl.l_type);
+ target_fl->l_whence = tswap16(fl.l_whence);
+ target_fl->l_start = tswapl(fl.l_start);
+ target_fl->l_len = tswapl(fl.l_len);
+ target_fl->l_pid = tswapl(fl.l_pid);
+ unlock_user_struct(target_fl, arg, 1);
+ }
+ break;
+
+ case TARGET_F_SETLK:
+ case TARGET_F_SETLKW:
+ lock_user_struct(target_fl, arg, 1);
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswapl(target_fl->l_start);
+ fl.l_len = tswapl(target_fl->l_len);
+ fl.l_pid = tswapl(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg, 0);
+ ret = fcntl(fd, cmd, &fl);
+ break;
+
+ case TARGET_F_GETLK64:
+ case TARGET_F_SETLK64:
+ case TARGET_F_SETLKW64:
+ ret = -1;
+ errno = EINVAL;
+ break;
+
+ case F_GETFL:
+ ret = fcntl(fd, cmd, arg);
+ ret = host_to_target_bitmask(ret, fcntl_flags_tbl);
+ break;
+
+ case F_SETFL:
+ ret = fcntl(fd, cmd, target_to_host_bitmask(arg, fcntl_flags_tbl));
+ break;
+
+ default:
+ ret = fcntl(fd, cmd, arg);
+ break;
+ }
+ return ret;
+}
+
+#ifdef USE_UID16
+
+static inline int high2lowuid(int uid)
+{
+ if (uid > 65535)
+ return 65534;
+ else
+ return uid;
+}
+
+static inline int high2lowgid(int gid)
+{
+ if (gid > 65535)
+ return 65534;
+ else
+ return gid;
+}
+
+static inline int low2highuid(int uid)
+{
+ if ((int16_t)uid == -1)
+ return -1;
+ else
+ return uid;
+}
+
+static inline int low2highgid(int gid)
+{
+ if ((int16_t)gid == -1)
+ return -1;
+ else
+ return gid;
+}
+
+#endif /* USE_UID16 */
+
+void syscall_init(void)
+{
+ IOCTLEntry *ie;
+ const argtype *arg_type;
+ int size;
+
+#define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
+#define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def);
+#include "syscall_types.h"
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+ /* we patch the ioctl size if necessary. We rely on the fact that
+ no ioctl has all the bits at '1' in the size field */
+ ie = ioctl_entries;
+ while (ie->target_cmd != 0) {
+ if (((ie->target_cmd >> TARGET_IOC_SIZESHIFT) & TARGET_IOC_SIZEMASK) ==
+ TARGET_IOC_SIZEMASK) {
+ arg_type = ie->arg_type;
+ if (arg_type[0] != TYPE_PTR) {
+ fprintf(stderr, "cannot patch size for ioctl 0x%x\n",
+ ie->target_cmd);
+ exit(1);
+ }
+ arg_type++;
+ size = thunk_type_size(arg_type, 0);
+ ie->target_cmd = (ie->target_cmd &
+ ~(TARGET_IOC_SIZEMASK << TARGET_IOC_SIZESHIFT)) |
+ (size << TARGET_IOC_SIZESHIFT);
+ }
+ /* automatic consistency check if same arch */
+#if defined(__i386__) && defined(TARGET_I386)
+ if (ie->target_cmd != ie->host_cmd) {
+ fprintf(stderr, "ERROR: ioctl: target=0x%x host=0x%x\n",
+ ie->target_cmd, ie->host_cmd);
+ }
+#endif
+ ie++;
+ }
+}
+
+static inline uint64_t target_offset64(uint32_t word0, uint32_t word1)
+{
+#ifdef TARGET_WORDS_BIG_ENDIAN
+ return ((uint64_t)word0 << 32) | word1;
+#else
+ return ((uint64_t)word1 << 32) | word0;
+#endif
+}
+
+#ifdef TARGET_NR_truncate64
+static inline long target_truncate64(void *cpu_env, const char *arg1,
+ long arg2, long arg3, long arg4)
+{
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ {
+ arg2 = arg3;
+ arg3 = arg4;
+ }
+#endif
+ return get_errno(truncate64(arg1, target_offset64(arg2, arg3)));
+}
+#endif
+
+#ifdef TARGET_NR_ftruncate64
+static inline long target_ftruncate64(void *cpu_env, long arg1, long arg2,
+ long arg3, long arg4)
+{
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ {
+ arg2 = arg3;
+ arg3 = arg4;
+ }
+#endif
+ return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3)));
+}
+#endif
+
+static inline void target_to_host_timespec(struct timespec *host_ts,
+ target_ulong target_addr)
+{
+ struct target_timespec *target_ts;
+
+ lock_user_struct(target_ts, target_addr, 1);
+ host_ts->tv_sec = tswapl(target_ts->tv_sec);
+ host_ts->tv_nsec = tswapl(target_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_addr, 0);
+}
+
+static inline void host_to_target_timespec(target_ulong target_addr,
+ struct timespec *host_ts)
+{
+ struct target_timespec *target_ts;
+
+ lock_user_struct(target_ts, target_addr, 0);
+ target_ts->tv_sec = tswapl(host_ts->tv_sec);
+ target_ts->tv_nsec = tswapl(host_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_addr, 1);
+}
+
+long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
+ long arg4, long arg5, long arg6)
+{
+ long ret;
+ struct stat st;
+ struct statfs stfs;
+ void *p;
+
+#ifdef DEBUG
+ gemu_log("syscall %d", num);
+#endif
+ switch(num) {
+ case TARGET_NR_exit:
+#ifdef HAVE_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ /* XXX: should free thread stack and CPU env */
+ _exit(arg1);
+ ret = 0; /* avoid warning */
+ break;
+ case TARGET_NR_read:
+ page_unprotect_range(arg2, arg3);
+ p = lock_user(arg2, arg3, 0);
+ ret = get_errno(read(arg1, p, arg3));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_NR_write:
+ p = lock_user(arg2, arg3, 1);
+ ret = get_errno(write(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+ case TARGET_NR_open:
+ p = lock_user_string(arg1);
+ ret = get_errno(open(path(p),
+ target_to_host_bitmask(arg2, fcntl_flags_tbl),
+ arg3));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_close:
+ ret = get_errno(close(arg1));
+ break;
+ case TARGET_NR_brk:
+ ret = do_brk(arg1);
+ break;
+ case TARGET_NR_fork:
+ ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
+ break;
+ case TARGET_NR_waitpid:
+ {
+ int status;
+ ret = get_errno(waitpid(arg1, &status, arg3));
+ if (!is_error(ret) && arg2)
+ tput32(arg2, status);
+ }
+ break;
+ case TARGET_NR_creat:
+ p = lock_user_string(arg1);
+ ret = get_errno(creat(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_link:
+ {
+ void * p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ ret = get_errno(link(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+ case TARGET_NR_unlink:
+ p = lock_user_string(arg1);
+ ret = get_errno(unlink(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_execve:
+ {
+ char **argp, **envp;
+ int argc, envc;
+ target_ulong gp;
+ target_ulong guest_argp;
+ target_ulong guest_envp;
+ target_ulong addr;
+ char **q;
+
+ argc = 0;
+ guest_argp = arg2;
+ for (gp = guest_argp; tgetl(gp); gp++)
+ argc++;
+ envc = 0;
+ guest_envp = arg3;
+ for (gp = guest_envp; tgetl(gp); gp++)
+ envc++;
+
+ argp = alloca((argc + 1) * sizeof(void *));
+ envp = alloca((envc + 1) * sizeof(void *));
+
+ for (gp = guest_argp, q = argp; ;
+ gp += sizeof(target_ulong), q++) {
+ addr = tgetl(gp);
+ if (!addr)
+ break;
+ *q = lock_user_string(addr);
+ }
+ *q = NULL;
+
+ for (gp = guest_envp, q = envp; ;
+ gp += sizeof(target_ulong), q++) {
+ addr = tgetl(gp);
+ if (!addr)
+ break;
+ *q = lock_user_string(addr);
+ }
+ *q = NULL;
+
+ p = lock_user_string(arg1);
+ ret = get_errno(execve(p, argp, envp));
+ unlock_user(p, arg1, 0);
+
+ for (gp = guest_argp, q = argp; *q;
+ gp += sizeof(target_ulong), q++) {
+ addr = tgetl(gp);
+ unlock_user(*q, addr, 0);
+ }
+ for (gp = guest_envp, q = envp; *q;
+ gp += sizeof(target_ulong), q++) {
+ addr = tgetl(gp);
+ unlock_user(*q, addr, 0);
+ }
+ }
+ break;
+ case TARGET_NR_chdir:
+ p = lock_user_string(arg1);
+ ret = get_errno(chdir(p));
+ unlock_user(p, arg1, 0);
+ break;
+#ifdef TARGET_NR_time
+ case TARGET_NR_time:
+ {
+ time_t host_time;
+ ret = get_errno(time(&host_time));
+ if (!is_error(ret) && arg1)
+ tputl(arg1, host_time);
+ }
+ break;
+#endif
+ case TARGET_NR_mknod:
+ p = lock_user_string(arg1);
+ ret = get_errno(mknod(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_chmod:
+ p = lock_user_string(arg1);
+ ret = get_errno(chmod(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#ifdef TARGET_NR_break
+ case TARGET_NR_break:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_oldstat
+ case TARGET_NR_oldstat:
+ goto unimplemented;
+#endif
+ case TARGET_NR_lseek:
+ ret = get_errno(lseek(arg1, arg2, arg3));
+ break;
+ case TARGET_NR_getpid:
+ ret = get_errno(getpid());
+ break;
+ case TARGET_NR_mount:
+ /* need to look at the data field */
+ goto unimplemented;
+ case TARGET_NR_umount:
+ p = lock_user_string(arg1);
+ ret = get_errno(umount(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_stime:
+ {
+ time_t host_time;
+ host_time = tgetl(arg1);
+ ret = get_errno(stime(&host_time));
+ }
+ break;
+ case TARGET_NR_ptrace:
+ goto unimplemented;
+ case TARGET_NR_alarm:
+ ret = alarm(arg1);
+ break;
+#ifdef TARGET_NR_oldfstat
+ case TARGET_NR_oldfstat:
+ goto unimplemented;
+#endif
+ case TARGET_NR_pause:
+ ret = get_errno(pause());
+ break;
+ case TARGET_NR_utime:
+ {
+ struct utimbuf tbuf, *host_tbuf;
+ struct target_utimbuf *target_tbuf;
+ if (arg2) {
+ lock_user_struct(target_tbuf, arg2, 1);
+ tbuf.actime = tswapl(target_tbuf->actime);
+ tbuf.modtime = tswapl(target_tbuf->modtime);
+ unlock_user_struct(target_tbuf, arg2, 0);
+ host_tbuf = &tbuf;
+ } else {
+ host_tbuf = NULL;
+ }
+ p = lock_user_string(arg1);
+ ret = get_errno(utime(p, host_tbuf));
+ unlock_user(p, arg1, 0);
+ }
+ break;
+ case TARGET_NR_utimes:
+ {
+ struct timeval *tvp, tv[2];
+ if (arg2) {
+ target_to_host_timeval(&tv[0], arg2);
+ target_to_host_timeval(&tv[1],
+ arg2 + sizeof (struct target_timeval));
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+ p = lock_user_string(arg1);
+ ret = get_errno(utimes(p, tvp));
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#ifdef TARGET_NR_stty
+ case TARGET_NR_stty:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_gtty
+ case TARGET_NR_gtty:
+ goto unimplemented;
+#endif
+ case TARGET_NR_access:
+ p = lock_user_string(arg1);
+ ret = get_errno(access(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_nice:
+ ret = get_errno(nice(arg1));
+ break;
+#ifdef TARGET_NR_ftime
+ case TARGET_NR_ftime:
+ goto unimplemented;
+#endif
+ case TARGET_NR_sync:
+ sync();
+ ret = 0;
+ break;
+ case TARGET_NR_kill:
+ ret = get_errno(kill(arg1, arg2));
+ break;
+ case TARGET_NR_rename:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ ret = get_errno(rename(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+ case TARGET_NR_mkdir:
+ p = lock_user_string(arg1);
+ ret = get_errno(mkdir(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_rmdir:
+ p = lock_user_string(arg1);
+ ret = get_errno(rmdir(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_dup:
+ ret = get_errno(dup(arg1));
+ break;
+ case TARGET_NR_pipe:
+ {
+ int host_pipe[2];
+ ret = get_errno(pipe(host_pipe));
+ if (!is_error(ret)) {
+ tput32(arg1, host_pipe[0]);
+ tput32(arg1 + 4, host_pipe[1]);
+ }
+ }
+ break;
+ case TARGET_NR_times:
+ {
+ struct target_tms *tmsp;
+ struct tms tms;
+ ret = get_errno(times(&tms));
+ if (arg1) {
+ tmsp = lock_user(arg1, sizeof(struct target_tms), 0);
+ tmsp->tms_utime = tswapl(host_to_target_clock_t(tms.tms_utime));
+ tmsp->tms_stime = tswapl(host_to_target_clock_t(tms.tms_stime));
+ tmsp->tms_cutime = tswapl(host_to_target_clock_t(tms.tms_cutime));
+ tmsp->tms_cstime = tswapl(host_to_target_clock_t(tms.tms_cstime));
+ }
+ if (!is_error(ret))
+ ret = host_to_target_clock_t(ret);
+ }
+ break;
+#ifdef TARGET_NR_prof
+ case TARGET_NR_prof:
+ goto unimplemented;
+#endif
+ case TARGET_NR_signal:
+ goto unimplemented;
+
+ case TARGET_NR_acct:
+ p = lock_user_string(arg1);
+ ret = get_errno(acct(path(p)));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_umount2:
+ p = lock_user_string(arg1);
+ ret = get_errno(umount2(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#ifdef TARGET_NR_lock
+ case TARGET_NR_lock:
+ goto unimplemented;
+#endif
+ case TARGET_NR_ioctl:
+ ret = do_ioctl(arg1, arg2, arg3);
+ break;
+ case TARGET_NR_fcntl:
+ ret = get_errno(do_fcntl(arg1, arg2, arg3));
+ break;
+#ifdef TARGET_NR_mpx
+ case TARGET_NR_mpx:
+ goto unimplemented;
+#endif
+ case TARGET_NR_setpgid:
+ ret = get_errno(setpgid(arg1, arg2));
+ break;
+#ifdef TARGET_NR_ulimit
+ case TARGET_NR_ulimit:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_oldolduname
+ case TARGET_NR_oldolduname:
+ goto unimplemented;
+#endif
+ case TARGET_NR_umask:
+ ret = get_errno(umask(arg1));
+ break;
+ case TARGET_NR_chroot:
+ p = lock_user_string(arg1);
+ ret = get_errno(chroot(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_ustat:
+ goto unimplemented;
+ case TARGET_NR_dup2:
+ ret = get_errno(dup2(arg1, arg2));
+ break;
+ case TARGET_NR_getppid:
+ ret = get_errno(getppid());
+ break;
+ case TARGET_NR_getpgrp:
+ ret = get_errno(getpgrp());
+ break;
+ case TARGET_NR_setsid:
+ ret = get_errno(setsid());
+ break;
+ case TARGET_NR_sigaction:
+ {
+ #if !defined(TARGET_MIPS)
+ struct target_old_sigaction *old_act;
+ struct target_sigaction act, oact, *pact;
+ if (arg2) {
+ lock_user_struct(old_act, arg2, 1);
+ act._sa_handler = old_act->_sa_handler;
+ target_siginitset(&act.sa_mask, old_act->sa_mask);
+ act.sa_flags = old_act->sa_flags;
+ act.sa_restorer = old_act->sa_restorer;
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ } else {
+ pact = NULL;
+ }
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+ if (!is_error(ret) && arg3) {
+ lock_user_struct(old_act, arg3, 0);
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_mask = oact.sa_mask.sig[0];
+ old_act->sa_flags = oact.sa_flags;
+ old_act->sa_restorer = oact.sa_restorer;
+ unlock_user_struct(old_act, arg3, 1);
+ }
+ #else
+ struct target_sigaction act, oact, *pact, *old_act;
+
+ if (arg2) {
+ lock_user_struct(old_act, arg2, 1);
+ act._sa_handler = old_act->_sa_handler;
+ target_siginitset(&act.sa_mask, old_act->sa_mask.sig[0]);
+ act.sa_flags = old_act->sa_flags;
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ } else {
+ pact = NULL;
+ }
+
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+
+ if (!is_error(ret) && arg3) {
+ lock_user_struct(old_act, arg3, 0);
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_flags = oact.sa_flags;
+ old_act->sa_mask.sig[0] = oact.sa_mask.sig[0];
+ old_act->sa_mask.sig[1] = 0;
+ old_act->sa_mask.sig[2] = 0;
+ old_act->sa_mask.sig[3] = 0;
+ unlock_user_struct(old_act, arg3, 1);
+ }
+ #endif
+ }
+ break;
+ case TARGET_NR_rt_sigaction:
+ {
+ struct target_sigaction *act;
+ struct target_sigaction *oact;
+
+ if (arg2)
+ lock_user_struct(act, arg2, 1);
+ else
+ act = NULL;
+ if (arg3)
+ lock_user_struct(oact, arg3, 0);
+ else
+ oact = NULL;
+ ret = get_errno(do_sigaction(arg1, act, oact));
+ if (arg2)
+ unlock_user_struct(act, arg2, 0);
+ if (arg3)
+ unlock_user_struct(oact, arg3, 1);
+ }
+ break;
+ case TARGET_NR_sgetmask:
+ {
+ sigset_t cur_set;
+ target_ulong target_set;
+ sigprocmask(0, NULL, &cur_set);
+ host_to_target_old_sigset(&target_set, &cur_set);
+ ret = target_set;
+ }
+ break;
+ case TARGET_NR_ssetmask:
+ {
+ sigset_t set, oset, cur_set;
+ target_ulong target_set = arg1;
+ sigprocmask(0, NULL, &cur_set);
+ target_to_host_old_sigset(&set, &target_set);
+ sigorset(&set, &set, &cur_set);
+ sigprocmask(SIG_SETMASK, &set, &oset);
+ host_to_target_old_sigset(&target_set, &oset);
+ ret = target_set;
+ }
+ break;
+ case TARGET_NR_sigprocmask:
+ {
+ int how = arg1;
+ sigset_t set, oldset, *set_ptr;
+
+ if (arg2) {
+ switch(how) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto fail;
+ }
+ p = lock_user(arg2, sizeof(target_sigset_t), 1);
+ target_to_host_old_sigset(&set, p);
+ unlock_user(p, arg2, 0);
+ set_ptr = &set;
+ } else {
+ how = 0;
+ set_ptr = NULL;
+ }
+ ret = get_errno(sigprocmask(arg1, set_ptr, &oldset));
+ if (!is_error(ret) && arg3) {
+ p = lock_user(arg3, sizeof(target_sigset_t), 0);
+ host_to_target_old_sigset(p, &oldset);
+ unlock_user(p, arg3, sizeof(target_sigset_t));
+ }
+ }
+ break;
+ case TARGET_NR_rt_sigprocmask:
+ {
+ int how = arg1;
+ sigset_t set, oldset, *set_ptr;
+
+ if (arg2) {
+ switch(how) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -EINVAL;
+ goto fail;
+ }
+ p = lock_user(arg2, sizeof(target_sigset_t), 1);
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg2, 0);
+ set_ptr = &set;
+ } else {
+ how = 0;
+ set_ptr = NULL;
+ }
+ ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+ if (!is_error(ret) && arg3) {
+ p = lock_user(arg3, sizeof(target_sigset_t), 0);
+ host_to_target_sigset(p, &oldset);
+ unlock_user(p, arg3, sizeof(target_sigset_t));
+ }
+ }
+ break;
+ case TARGET_NR_sigpending:
+ {
+ sigset_t set;
+ ret = get_errno(sigpending(&set));
+ if (!is_error(ret)) {
+ p = lock_user(arg1, sizeof(target_sigset_t), 0);
+ host_to_target_old_sigset(p, &set);
+ unlock_user(p, arg1, sizeof(target_sigset_t));
+ }
+ }
+ break;
+ case TARGET_NR_rt_sigpending:
+ {
+ sigset_t set;
+ ret = get_errno(sigpending(&set));
+ if (!is_error(ret)) {
+ p = lock_user(arg1, sizeof(target_sigset_t), 0);
+ host_to_target_sigset(p, &set);
+ unlock_user(p, arg1, sizeof(target_sigset_t));
+ }
+ }
+ break;
+ case TARGET_NR_sigsuspend:
+ {
+ sigset_t set;
+ p = lock_user(arg1, sizeof(target_sigset_t), 1);
+ target_to_host_old_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sigsuspend(&set));
+ }
+ break;
+ case TARGET_NR_rt_sigsuspend:
+ {
+ sigset_t set;
+ p = lock_user(arg1, sizeof(target_sigset_t), 1);
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sigsuspend(&set));
+ }
+ break;
+ case TARGET_NR_rt_sigtimedwait:
+ {
+ sigset_t set;
+ struct timespec uts, *puts;
+ siginfo_t uinfo;
+
+ p = lock_user(arg1, sizeof(target_sigset_t), 1);
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ if (arg3) {
+ puts = &uts;
+ target_to_host_timespec(puts, arg3);
+ } else {
+ puts = NULL;
+ }
+ ret = get_errno(sigtimedwait(&set, &uinfo, puts));
+ if (!is_error(ret) && arg2) {
+ p = lock_user(arg2, sizeof(target_sigset_t), 0);
+ host_to_target_siginfo(p, &uinfo);
+ unlock_user(p, arg2, sizeof(target_sigset_t));
+ }
+ }
+ break;
+ case TARGET_NR_rt_sigqueueinfo:
+ {
+ siginfo_t uinfo;
+ p = lock_user(arg3, sizeof(target_sigset_t), 1);
+ target_to_host_siginfo(&uinfo, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo));
+ }
+ break;
+ case TARGET_NR_sigreturn:
+ /* NOTE: ret is eax, so not transcoding must be done */
+ ret = do_sigreturn(cpu_env);
+ break;
+ case TARGET_NR_rt_sigreturn:
+ /* NOTE: ret is eax, so not transcoding must be done */
+ ret = do_rt_sigreturn(cpu_env);
+ break;
+ case TARGET_NR_sethostname:
+ p = lock_user_string(arg1);
+ ret = get_errno(sethostname(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_setrlimit:
+ {
+ /* XXX: convert resource ? */
+ int resource = arg1;
+ struct target_rlimit *target_rlim;
+ struct rlimit rlim;
+ lock_user_struct(target_rlim, arg2, 1);
+ rlim.rlim_cur = tswapl(target_rlim->rlim_cur);
+ rlim.rlim_max = tswapl(target_rlim->rlim_max);
+ unlock_user_struct(target_rlim, arg2, 0);
+ ret = get_errno(setrlimit(resource, &rlim));
+ }
+ break;
+ case TARGET_NR_getrlimit:
+ {
+ /* XXX: convert resource ? */
+ int resource = arg1;
+ struct target_rlimit *target_rlim;
+ struct rlimit rlim;
+
+ ret = get_errno(getrlimit(resource, &rlim));
+ if (!is_error(ret)) {
+ lock_user_struct(target_rlim, arg2, 0);
+ rlim.rlim_cur = tswapl(target_rlim->rlim_cur);
+ rlim.rlim_max = tswapl(target_rlim->rlim_max);
+ unlock_user_struct(target_rlim, arg2, 1);
+ }
+ }
+ break;
+ case TARGET_NR_getrusage:
+ {
+ struct rusage rusage;
+ ret = get_errno(getrusage(arg1, &rusage));
+ if (!is_error(ret)) {
+ host_to_target_rusage(arg2, &rusage);
+ }
+ }
+ break;
+ case TARGET_NR_gettimeofday:
+ {
+ struct timeval tv;
+ ret = get_errno(gettimeofday(&tv, NULL));
+ if (!is_error(ret)) {
+ host_to_target_timeval(arg1, &tv);
+ }
+ }
+ break;
+ case TARGET_NR_settimeofday:
+ {
+ struct timeval tv;
+ target_to_host_timeval(&tv, arg1);
+ ret = get_errno(settimeofday(&tv, NULL));
+ }
+ break;
+#ifdef TARGET_NR_select
+ case TARGET_NR_select:
+ {
+ struct target_sel_arg_struct *sel;
+ target_ulong inp, outp, exp, tvp;
+ long nsel;
+
+ lock_user_struct(sel, arg1, 1);
+ nsel = tswapl(sel->n);
+ inp = tswapl(sel->inp);
+ outp = tswapl(sel->outp);
+ exp = tswapl(sel->exp);
+ tvp = tswapl(sel->tvp);
+ unlock_user_struct(sel, arg1, 0);
+ ret = do_select(nsel, inp, outp, exp, tvp);
+ }
+ break;
+#endif
+ case TARGET_NR_symlink:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ ret = get_errno(symlink(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#ifdef TARGET_NR_oldlstat
+ case TARGET_NR_oldlstat:
+ goto unimplemented;
+#endif
+ case TARGET_NR_readlink:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user(arg2, arg3, 0);
+ ret = get_errno(readlink(path(p), p2, arg3));
+ unlock_user(p2, arg2, ret);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+ case TARGET_NR_uselib:
+ goto unimplemented;
+ case TARGET_NR_swapon:
+ p = lock_user_string(arg1);
+ ret = get_errno(swapon(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_reboot:
+ goto unimplemented;
+ case TARGET_NR_readdir:
+ goto unimplemented;
+ case TARGET_NR_mmap:
+#if defined(TARGET_I386) || defined(TARGET_ARM)
+ {
+ target_ulong *v;
+ target_ulong v1, v2, v3, v4, v5, v6;
+ v = lock_user(arg1, 6 * sizeof(target_ulong), 1);
+ v1 = tswapl(v[0]);
+ v2 = tswapl(v[1]);
+ v3 = tswapl(v[2]);
+ v4 = tswapl(v[3]);
+ v5 = tswapl(v[4]);
+ v6 = tswapl(v[5]);
+ unlock_user(v, arg1, 0);
+ ret = get_errno(target_mmap(v1, v2, v3,
+ target_to_host_bitmask(v4, mmap_flags_tbl),
+ v5, v6));
+ }
+#else
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6));
+#endif
+ break;
+#ifdef TARGET_NR_mmap2
+ case TARGET_NR_mmap2:
+#if defined(TARGET_SPARC)
+#define MMAP_SHIFT 12
+#else
+#define MMAP_SHIFT TARGET_PAGE_BITS
+#endif
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6 << MMAP_SHIFT));
+ break;
+#endif
+ case TARGET_NR_munmap:
+ ret = get_errno(target_munmap(arg1, arg2));
+ break;
+ case TARGET_NR_mprotect:
+ ret = get_errno(target_mprotect(arg1, arg2, arg3));
+ break;
+ case TARGET_NR_mremap:
+ ret = get_errno(target_mremap(arg1, arg2, arg3, arg4, arg5));
+ break;
+ /* ??? msync/mlock/munlock are broken for softmmu. */
+ case TARGET_NR_msync:
+ ret = get_errno(msync(g2h(arg1), arg2, arg3));
+ break;
+ case TARGET_NR_mlock:
+ ret = get_errno(mlock(g2h(arg1), arg2));
+ break;
+ case TARGET_NR_munlock:
+ ret = get_errno(munlock(g2h(arg1), arg2));
+ break;
+ case TARGET_NR_mlockall:
+ ret = get_errno(mlockall(arg1));
+ break;
+ case TARGET_NR_munlockall:
+ ret = get_errno(munlockall());
+ break;
+ case TARGET_NR_truncate:
+ p = lock_user_string(arg1);
+ ret = get_errno(truncate(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_ftruncate:
+ ret = get_errno(ftruncate(arg1, arg2));
+ break;
+ case TARGET_NR_fchmod:
+ ret = get_errno(fchmod(arg1, arg2));
+ break;
+ case TARGET_NR_getpriority:
+ ret = get_errno(getpriority(arg1, arg2));
+ break;
+ case TARGET_NR_setpriority:
+ ret = get_errno(setpriority(arg1, arg2, arg3));
+ break;
+#ifdef TARGET_NR_profil
+ case TARGET_NR_profil:
+ goto unimplemented;
+#endif
+ case TARGET_NR_statfs:
+ p = lock_user_string(arg1);
+ ret = get_errno(statfs(path(p), &stfs));
+ unlock_user(p, arg1, 0);
+ convert_statfs:
+ if (!is_error(ret)) {
+ struct target_statfs *target_stfs;
+
+ lock_user_struct(target_stfs, arg2, 0);
+ /* ??? put_user is probably wrong. */
+ put_user(stfs.f_type, &target_stfs->f_type);
+ put_user(stfs.f_bsize, &target_stfs->f_bsize);
+ put_user(stfs.f_blocks, &target_stfs->f_blocks);
+ put_user(stfs.f_bfree, &target_stfs->f_bfree);
+ put_user(stfs.f_bavail, &target_stfs->f_bavail);
+ put_user(stfs.f_files, &target_stfs->f_files);
+ put_user(stfs.f_ffree, &target_stfs->f_ffree);
+ put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid);
+ put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ unlock_user_struct(target_stfs, arg2, 1);
+ }
+ break;
+ case TARGET_NR_fstatfs:
+ ret = get_errno(fstatfs(arg1, &stfs));
+ goto convert_statfs;
+#ifdef TARGET_NR_statfs64
+ case TARGET_NR_statfs64:
+ p = lock_user_string(arg1);
+ ret = get_errno(statfs(path(p), &stfs));
+ unlock_user(p, arg1, 0);
+ convert_statfs64:
+ if (!is_error(ret)) {
+ struct target_statfs64 *target_stfs;
+
+ lock_user_struct(target_stfs, arg3, 0);
+ /* ??? put_user is probably wrong. */
+ put_user(stfs.f_type, &target_stfs->f_type);
+ put_user(stfs.f_bsize, &target_stfs->f_bsize);
+ put_user(stfs.f_blocks, &target_stfs->f_blocks);
+ put_user(stfs.f_bfree, &target_stfs->f_bfree);
+ put_user(stfs.f_bavail, &target_stfs->f_bavail);
+ put_user(stfs.f_files, &target_stfs->f_files);
+ put_user(stfs.f_ffree, &target_stfs->f_ffree);
+ put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid);
+ put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ unlock_user_struct(target_stfs, arg3, 0);
+ }
+ break;
+ case TARGET_NR_fstatfs64:
+ ret = get_errno(fstatfs(arg1, &stfs));
+ goto convert_statfs64;
+#endif
+#ifdef TARGET_NR_ioperm
+ case TARGET_NR_ioperm:
+ goto unimplemented;
+#endif
+ case TARGET_NR_socketcall:
+ ret = do_socketcall(arg1, arg2);
+ break;
+
+#ifdef TARGET_NR_accept
+ case TARGET_NR_accept:
+ ret = do_socketcallwrapper(SOCKOP_accept, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_bind
+ case TARGET_NR_bind:
+ ret = do_bind(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_connect
+ case TARGET_NR_connect:
+ ret = do_connect(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_getpeername
+ case TARGET_NR_getpeername:
+ ret = do_socketcallwrapper(SOCKOP_getpeername, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_getsockname
+ case TARGET_NR_getsockname:
+ ret = do_socketcallwrapper(SOCKOP_getsockname, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_getsockopt
+ case TARGET_NR_getsockopt:
+ ret = do_getsockopt(arg1, arg2, arg3, arg4, arg5);
+ break;
+#endif
+#ifdef TARGET_NR_listen
+ case TARGET_NR_listen:
+ ret = do_socketcallwrapper(SOCKOP_listen, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_recv
+ case TARGET_NR_recv:
+ ret = do_socketcallwrapper(SOCKOP_recv, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_recvfrom
+ case TARGET_NR_recvfrom:
+ ret = do_socketcallwrapper(SOCKOP_recvfrom, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_recvmsg
+ case TARGET_NR_recvmsg:
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 0);
+ break;
+#endif
+#ifdef TARGET_NR_send
+ case TARGET_NR_send:
+ ret = do_socketcallwrapper(SOCKOP_send, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_sendmsg
+ case TARGET_NR_sendmsg:
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 1);
+ break;
+#endif
+#ifdef TARGET_NR_sendto
+ case TARGET_NR_sendto:
+ ret = do_socketcallwrapper(SOCKOP_sendto, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_shutdown
+ case TARGET_NR_shutdown:
+ ret = do_socketcallwrapper(SOCKOP_shutdown, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_socket
+ case TARGET_NR_socket:
+ ret = do_socket(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_socketpair
+ case TARGET_NR_socketpair:
+ ret = do_socketcallwrapper(SOCKOP_socketpair, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_setsockopt
+ case TARGET_NR_setsockopt:
+ ret = do_setsockopt(arg1, arg2, arg3, arg4, (socklen_t) arg5);
+ break;
+#endif
+
+ case TARGET_NR_syslog:
+ goto unimplemented;
+ case TARGET_NR_setitimer:
+ {
+ struct itimerval value, ovalue, *pvalue;
+
+ if (arg2) {
+ pvalue = &value;
+ target_to_host_timeval(&pvalue->it_interval,
+ arg2);
+ target_to_host_timeval(&pvalue->it_value,
+ arg2 + sizeof(struct target_timeval));
+ } else {
+ pvalue = NULL;
+ }
+ ret = get_errno(setitimer(arg1, pvalue, &ovalue));
+ if (!is_error(ret) && arg3) {
+ host_to_target_timeval(arg3,
+ &ovalue.it_interval);
+ host_to_target_timeval(arg3 + sizeof(struct target_timeval),
+ &ovalue.it_value);
+ }
+ }
+ break;
+ case TARGET_NR_getitimer:
+ {
+ struct itimerval value;
+
+ ret = get_errno(getitimer(arg1, &value));
+ if (!is_error(ret) && arg2) {
+ host_to_target_timeval(arg2,
+ &value.it_interval);
+ host_to_target_timeval(arg2 + sizeof(struct target_timeval),
+ &value.it_value);
+ }
+ }
+ break;
+ case TARGET_NR_stat:
+ p = lock_user_string(arg1);
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat;
+ case TARGET_NR_lstat:
+ p = lock_user_string(arg1);
+ ret = get_errno(lstat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat;
+ case TARGET_NR_fstat:
+ {
+ ret = get_errno(fstat(arg1, &st));
+ do_stat:
+ if (!is_error(ret)) {
+ struct target_stat *target_st;
+
+ lock_user_struct(target_st, arg2, 0);
+ target_st->st_dev = tswap16(st.st_dev);
+ target_st->st_ino = tswapl(st.st_ino);
+#if defined(TARGET_PPC)
+ target_st->st_mode = tswapl(st.st_mode); /* XXX: check this */
+ target_st->st_uid = tswap32(st.st_uid);
+ target_st->st_gid = tswap32(st.st_gid);
+#else
+ target_st->st_mode = tswap16(st.st_mode);
+ target_st->st_uid = tswap16(st.st_uid);
+ target_st->st_gid = tswap16(st.st_gid);
+#endif
+ target_st->st_nlink = tswap16(st.st_nlink);
+ target_st->st_rdev = tswap16(st.st_rdev);
+ target_st->st_size = tswapl(st.st_size);
+ target_st->st_blksize = tswapl(st.st_blksize);
+ target_st->st_blocks = tswapl(st.st_blocks);
+ target_st->target_st_atime = tswapl(st.st_atime);
+ target_st->target_st_mtime = tswapl(st.st_mtime);
+ target_st->target_st_ctime = tswapl(st.st_ctime);
+ unlock_user_struct(target_st, arg2, 1);
+ }
+ }
+ break;
+#ifdef TARGET_NR_olduname
+ case TARGET_NR_olduname:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_iopl
+ case TARGET_NR_iopl:
+ goto unimplemented;
+#endif
+ case TARGET_NR_vhangup:
+ ret = get_errno(vhangup());
+ break;
+#ifdef TARGET_NR_idle
+ case TARGET_NR_idle:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_syscall
+ case TARGET_NR_syscall:
+ ret = do_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
+ break;
+#endif
+ case TARGET_NR_wait4:
+ {
+ int status;
+ target_long status_ptr = arg2;
+ struct rusage rusage, *rusage_ptr;
+ target_ulong target_rusage = arg4;
+ if (target_rusage)
+ rusage_ptr = &rusage;
+ else
+ rusage_ptr = NULL;
+ ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr));
+ if (!is_error(ret)) {
+ if (status_ptr)
+ tputl(status_ptr, status);
+ if (target_rusage) {
+ host_to_target_rusage(target_rusage, &rusage);
+ }
+ }
+ }
+ break;
+ case TARGET_NR_swapoff:
+ p = lock_user_string(arg1);
+ ret = get_errno(swapoff(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_sysinfo:
+ {
+ struct target_sysinfo *target_value;
+ struct sysinfo value;
+ ret = get_errno(sysinfo(&value));
+ if (!is_error(ret) && arg1)
+ {
+ /* ??? __put_user is probably wrong. */
+ lock_user_struct(target_value, arg1, 0);
+ __put_user(value.uptime, &target_value->uptime);
+ __put_user(value.loads[0], &target_value->loads[0]);
+ __put_user(value.loads[1], &target_value->loads[1]);
+ __put_user(value.loads[2], &target_value->loads[2]);
+ __put_user(value.totalram, &target_value->totalram);
+ __put_user(value.freeram, &target_value->freeram);
+ __put_user(value.sharedram, &target_value->sharedram);
+ __put_user(value.bufferram, &target_value->bufferram);
+ __put_user(value.totalswap, &target_value->totalswap);
+ __put_user(value.freeswap, &target_value->freeswap);
+ __put_user(value.procs, &target_value->procs);
+ __put_user(value.totalhigh, &target_value->totalhigh);
+ __put_user(value.freehigh, &target_value->freehigh);
+ __put_user(value.mem_unit, &target_value->mem_unit);
+ unlock_user_struct(target_value, arg1, 1);
+ }
+ }
+ break;
+ case TARGET_NR_ipc:
+ ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+ case TARGET_NR_fsync:
+ ret = get_errno(fsync(arg1));
+ break;
+ case TARGET_NR_clone:
+ ret = get_errno(do_fork(cpu_env, arg1, arg2));
+ break;
+#ifdef __NR_exit_group
+ /* new thread calls */
+ case TARGET_NR_exit_group:
+ gdb_exit(cpu_env, arg1);
+ ret = get_errno(exit_group(arg1));
+ break;
+#endif
+ case TARGET_NR_setdomainname:
+ p = lock_user_string(arg1);
+ ret = get_errno(setdomainname(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_uname:
+ /* no need to transcode because we use the linux syscall */
+ {
+ struct new_utsname * buf;
+
+ lock_user_struct(buf, arg1, 0);
+ ret = get_errno(sys_uname(buf));
+ if (!is_error(ret)) {
+ /* Overrite the native machine name with whatever is being
+ emulated. */
+ strcpy (buf->machine, UNAME_MACHINE);
+ /* Allow the user to override the reported release. */
+ if (qemu_uname_release && *qemu_uname_release)
+ strcpy (buf->release, qemu_uname_release);
+ }
+ unlock_user_struct(buf, arg1, 1);
+ }
+ break;
+#ifdef TARGET_I386
+ case TARGET_NR_modify_ldt:
+ ret = get_errno(do_modify_ldt(cpu_env, arg1, arg2, arg3));
+ break;
+ case TARGET_NR_vm86old:
+ goto unimplemented;
+ case TARGET_NR_vm86:
+ ret = do_vm86(cpu_env, arg1, arg2);
+ break;
+#endif
+ case TARGET_NR_adjtimex:
+ goto unimplemented;
+ case TARGET_NR_create_module:
+ case TARGET_NR_init_module:
+ case TARGET_NR_delete_module:
+ case TARGET_NR_get_kernel_syms:
+ goto unimplemented;
+ case TARGET_NR_quotactl:
+ goto unimplemented;
+ case TARGET_NR_getpgid:
+ ret = get_errno(getpgid(arg1));
+ break;
+ case TARGET_NR_fchdir:
+ ret = get_errno(fchdir(arg1));
+ break;
+ case TARGET_NR_bdflush:
+ goto unimplemented;
+ case TARGET_NR_sysfs:
+ goto unimplemented;
+ case TARGET_NR_personality:
+ ret = get_errno(personality(arg1));
+ break;
+ case TARGET_NR_afs_syscall:
+ goto unimplemented;
+ case TARGET_NR__llseek:
+ {
+#if defined (__x86_64__)
+ ret = get_errno(lseek(arg1, ((uint64_t )arg2 << 32) | arg3, arg5));
+ tput64(arg4, ret);
+#else
+ int64_t res;
+ ret = get_errno(_llseek(arg1, arg2, arg3, &res, arg5));
+ tput64(arg4, res);
+#endif
+ }
+ break;
+ case TARGET_NR_getdents:
+#if TARGET_LONG_SIZE != 4
+ goto unimplemented;
+#warning not supported
+#elif TARGET_LONG_SIZE == 4 && HOST_LONG_SIZE == 8
+ {
+ struct target_dirent *target_dirp;
+ struct dirent *dirp;
+ long count = arg3;
+
+ dirp = malloc(count);
+ if (!dirp)
+ return -ENOMEM;
+
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct dirent *de;
+ struct target_dirent *tde;
+ int len = ret;
+ int reclen, treclen;
+ int count1, tnamelen;
+
+ count1 = 0;
+ de = dirp;
+ target_dirp = lock_user(arg2, count, 0);
+ tde = target_dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ treclen = reclen - (2 * (sizeof(long) - sizeof(target_long)));
+ tde->d_reclen = tswap16(treclen);
+ tde->d_ino = tswapl(de->d_ino);
+ tde->d_off = tswapl(de->d_off);
+ tnamelen = treclen - (2 * sizeof(target_long) + 2);
+ if (tnamelen > 256)
+ tnamelen = 256;
+ /* XXX: may not be correct */
+ strncpy(tde->d_name, de->d_name, tnamelen);
+ de = (struct dirent *)((char *)de + reclen);
+ len -= reclen;
+ tde = (struct dirent *)((char *)tde + treclen);
+ count1 += treclen;
+ }
+ ret = count1;
+ }
+ unlock_user(target_dirp, arg2, ret);
+ free(dirp);
+ }
+#else
+ {
+ struct dirent *dirp;
+ long count = arg3;
+
+ dirp = lock_user(arg2, count, 0);
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct dirent *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len)
+ break;
+ de->d_reclen = tswap16(reclen);
+ tswapls(&de->d_ino);
+ tswapls(&de->d_off);
+ de = (struct dirent *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+ }
+#endif
+ break;
+#ifdef TARGET_NR_getdents64
+ case TARGET_NR_getdents64:
+ {
+ struct dirent64 *dirp;
+ long count = arg3;
+ dirp = lock_user(arg2, count, 0);
+ ret = get_errno(sys_getdents64(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct dirent64 *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len)
+ break;
+ de->d_reclen = tswap16(reclen);
+ tswap64s(&de->d_ino);
+ tswap64s(&de->d_off);
+ de = (struct dirent64 *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+ }
+ break;
+#endif /* TARGET_NR_getdents64 */
+ case TARGET_NR__newselect:
+ ret = do_select(arg1, arg2, arg3, arg4, arg5);
+ break;
+ case TARGET_NR_poll:
+ {
+ struct target_pollfd *target_pfd;
+ unsigned int nfds = arg2;
+ int timeout = arg3;
+ struct pollfd *pfd;
+ unsigned int i;
+
+ target_pfd = lock_user(arg1, sizeof(struct target_pollfd) * nfds, 1);
+ pfd = alloca(sizeof(struct pollfd) * nfds);
+ for(i = 0; i < nfds; i++) {
+ pfd[i].fd = tswap32(target_pfd[i].fd);
+ pfd[i].events = tswap16(target_pfd[i].events);
+ }
+ ret = get_errno(poll(pfd, nfds, timeout));
+ if (!is_error(ret)) {
+ for(i = 0; i < nfds; i++) {
+ target_pfd[i].revents = tswap16(pfd[i].revents);
+ }
+ ret += nfds * (sizeof(struct target_pollfd)
+ - sizeof(struct pollfd));
+ }
+ unlock_user(target_pfd, arg1, ret);
+ }
+ break;
+ case TARGET_NR_flock:
+ /* NOTE: the flock constant seems to be the same for every
+ Linux platform */
+ ret = get_errno(flock(arg1, arg2));
+ break;
+ case TARGET_NR_readv:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ lock_iovec(vec, arg2, count, 0);
+ ret = get_errno(readv(arg1, vec, count));
+ unlock_iovec(vec, arg2, count, 1);
+ }
+ break;
+ case TARGET_NR_writev:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ lock_iovec(vec, arg2, count, 1);
+ ret = get_errno(writev(arg1, vec, count));
+ unlock_iovec(vec, arg2, count, 0);
+ }
+ break;
+ case TARGET_NR_getsid:
+ ret = get_errno(getsid(arg1));
+ break;
+ case TARGET_NR_fdatasync:
+ ret = get_errno(fdatasync(arg1));
+ break;
+ case TARGET_NR__sysctl:
+ /* We don't implement this, but ENODIR is always a safe
+ return value. */
+ return -ENOTDIR;
+ case TARGET_NR_sched_setparam:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+
+ lock_user_struct(target_schp, arg2, 1);
+ schp.sched_priority = tswap32(target_schp->sched_priority);
+ unlock_user_struct(target_schp, arg2, 0);
+ ret = get_errno(sched_setparam(arg1, &schp));
+ }
+ break;
+ case TARGET_NR_sched_getparam:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+ ret = get_errno(sched_getparam(arg1, &schp));
+ if (!is_error(ret)) {
+ lock_user_struct(target_schp, arg2, 0);
+ target_schp->sched_priority = tswap32(schp.sched_priority);
+ unlock_user_struct(target_schp, arg2, 1);
+ }
+ }
+ break;
+ case TARGET_NR_sched_setscheduler:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+ lock_user_struct(target_schp, arg3, 1);
+ schp.sched_priority = tswap32(target_schp->sched_priority);
+ unlock_user_struct(target_schp, arg3, 0);
+ ret = get_errno(sched_setscheduler(arg1, arg2, &schp));
+ }
+ break;
+ case TARGET_NR_sched_getscheduler:
+ ret = get_errno(sched_getscheduler(arg1));
+ break;
+ case TARGET_NR_sched_yield:
+ ret = get_errno(sched_yield());
+ break;
+ case TARGET_NR_sched_get_priority_max:
+ ret = get_errno(sched_get_priority_max(arg1));
+ break;
+ case TARGET_NR_sched_get_priority_min:
+ ret = get_errno(sched_get_priority_min(arg1));
+ break;
+ case TARGET_NR_sched_rr_get_interval:
+ {
+ struct timespec ts;
+ ret = get_errno(sched_rr_get_interval(arg1, &ts));
+ if (!is_error(ret)) {
+ host_to_target_timespec(arg2, &ts);
+ }
+ }
+ break;
+ case TARGET_NR_nanosleep:
+ {
+ struct timespec req, rem;
+ target_to_host_timespec(&req, arg1);
+ ret = get_errno(nanosleep(&req, &rem));
+ if (is_error(ret) && arg2) {
+ host_to_target_timespec(arg2, &rem);
+ }
+ }
+ break;
+ case TARGET_NR_query_module:
+ goto unimplemented;
+ case TARGET_NR_nfsservctl:
+ goto unimplemented;
+ case TARGET_NR_prctl:
+ goto unimplemented;
+#ifdef TARGET_NR_pread
+ case TARGET_NR_pread:
+ page_unprotect_range(arg2, arg3);
+ p = lock_user(arg2, arg3, 0);
+ ret = get_errno(pread(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_NR_pwrite:
+ p = lock_user(arg2, arg3, 1);
+ ret = get_errno(pwrite(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_getcwd:
+ p = lock_user(arg1, arg2, 0);
+ ret = get_errno(sys_getcwd1(p, arg2));
+ unlock_user(p, arg1, ret);
+ break;
+ case TARGET_NR_capget:
+ goto unimplemented;
+ case TARGET_NR_capset:
+ goto unimplemented;
+ case TARGET_NR_sigaltstack:
+ goto unimplemented;
+ case TARGET_NR_sendfile:
+ goto unimplemented;
+#ifdef TARGET_NR_getpmsg
+ case TARGET_NR_getpmsg:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_putpmsg
+ case TARGET_NR_putpmsg:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_vfork
+ case TARGET_NR_vfork:
+ ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
+ break;
+#endif
+#ifdef TARGET_NR_ugetrlimit
+ case TARGET_NR_ugetrlimit:
+ {
+ struct rlimit rlim;
+ ret = get_errno(getrlimit(arg1, &rlim));
+ if (!is_error(ret)) {
+ struct target_rlimit *target_rlim;
+ lock_user_struct(target_rlim, arg2, 0);
+ target_rlim->rlim_cur = tswapl(rlim.rlim_cur);
+ target_rlim->rlim_max = tswapl(rlim.rlim_max);
+ unlock_user_struct(target_rlim, arg2, 1);
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_truncate64
+ case TARGET_NR_truncate64:
+ p = lock_user_string(arg1);
+ ret = target_truncate64(cpu_env, p, arg2, arg3, arg4);
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_ftruncate64
+ case TARGET_NR_ftruncate64:
+ ret = target_ftruncate64(cpu_env, arg1, arg2, arg3, arg4);
+ break;
+#endif
+#ifdef TARGET_NR_stat64
+ case TARGET_NR_stat64:
+ p = lock_user_string(arg1);
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat64;
+#endif
+#ifdef TARGET_NR_lstat64
+ case TARGET_NR_lstat64:
+ p = lock_user_string(arg1);
+ ret = get_errno(lstat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat64;
+#endif
+#ifdef TARGET_NR_fstat64
+ case TARGET_NR_fstat64:
+ {
+ ret = get_errno(fstat(arg1, &st));
+ do_stat64:
+ if (!is_error(ret)) {
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ struct target_eabi_stat64 *target_st;
+ lock_user_struct(target_st, arg2, 1);
+ memset(target_st, 0, sizeof(struct target_eabi_stat64));
+ /* put_user is probably wrong. */
+ put_user(st.st_dev, &target_st->st_dev);
+ put_user(st.st_ino, &target_st->st_ino);
+#ifdef TARGET_STAT64_HAS_BROKEN_ST_INO
+ put_user(st.st_ino, &target_st->__st_ino);
+#endif
+ put_user(st.st_mode, &target_st->st_mode);
+ put_user(st.st_nlink, &target_st->st_nlink);
+ put_user(st.st_uid, &target_st->st_uid);
+ put_user(st.st_gid, &target_st->st_gid);
+ put_user(st.st_rdev, &target_st->st_rdev);
+ /* XXX: better use of kernel struct */
+ put_user(st.st_size, &target_st->st_size);
+ put_user(st.st_blksize, &target_st->st_blksize);
+ put_user(st.st_blocks, &target_st->st_blocks);
+ put_user(st.st_atime, &target_st->target_st_atime);
+ put_user(st.st_mtime, &target_st->target_st_mtime);
+ put_user(st.st_ctime, &target_st->target_st_ctime);
+ unlock_user_struct(target_st, arg2, 0);
+ } else
+#endif
+ {
+ struct target_stat64 *target_st;
+ lock_user_struct(target_st, arg2, 1);
+ memset(target_st, 0, sizeof(struct target_stat64));
+ /* ??? put_user is probably wrong. */
+ put_user(st.st_dev, &target_st->st_dev);
+ put_user(st.st_ino, &target_st->st_ino);
+#ifdef TARGET_STAT64_HAS_BROKEN_ST_INO
+ put_user(st.st_ino, &target_st->__st_ino);
+#endif
+ put_user(st.st_mode, &target_st->st_mode);
+ put_user(st.st_nlink, &target_st->st_nlink);
+ put_user(st.st_uid, &target_st->st_uid);
+ put_user(st.st_gid, &target_st->st_gid);
+ put_user(st.st_rdev, &target_st->st_rdev);
+ /* XXX: better use of kernel struct */
+ put_user(st.st_size, &target_st->st_size);
+ put_user(st.st_blksize, &target_st->st_blksize);
+ put_user(st.st_blocks, &target_st->st_blocks);
+ put_user(st.st_atime, &target_st->target_st_atime);
+ put_user(st.st_mtime, &target_st->target_st_mtime);
+ put_user(st.st_ctime, &target_st->target_st_ctime);
+ unlock_user_struct(target_st, arg2, 0);
+ }
+ }
+ }
+ break;
+#endif
+#ifdef USE_UID16
+ case TARGET_NR_lchown:
+ p = lock_user_string(arg1);
+ ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3)));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_getuid:
+ ret = get_errno(high2lowuid(getuid()));
+ break;
+ case TARGET_NR_getgid:
+ ret = get_errno(high2lowgid(getgid()));
+ break;
+ case TARGET_NR_geteuid:
+ ret = get_errno(high2lowuid(geteuid()));
+ break;
+ case TARGET_NR_getegid:
+ ret = get_errno(high2lowgid(getegid()));
+ break;
+ case TARGET_NR_setreuid:
+ ret = get_errno(setreuid(low2highuid(arg1), low2highuid(arg2)));
+ break;
+ case TARGET_NR_setregid:
+ ret = get_errno(setregid(low2highgid(arg1), low2highgid(arg2)));
+ break;
+ case TARGET_NR_getgroups:
+ {
+ int gidsetsize = arg1;
+ uint16_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ ret = get_errno(getgroups(gidsetsize, grouplist));
+ if (!is_error(ret)) {
+ target_grouplist = lock_user(arg2, gidsetsize * 2, 0);
+ for(i = 0;i < gidsetsize; i++)
+ target_grouplist[i] = tswap16(grouplist[i]);
+ unlock_user(target_grouplist, arg2, gidsetsize * 2);
+ }
+ }
+ break;
+ case TARGET_NR_setgroups:
+ {
+ int gidsetsize = arg1;
+ uint16_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ target_grouplist = lock_user(arg2, gidsetsize * 2, 1);
+ for(i = 0;i < gidsetsize; i++)
+ grouplist[i] = tswap16(target_grouplist[i]);
+ unlock_user(target_grouplist, arg2, 0);
+ ret = get_errno(setgroups(gidsetsize, grouplist));
+ }
+ break;
+ case TARGET_NR_fchown:
+ ret = get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3)));
+ break;
+#ifdef TARGET_NR_setresuid
+ case TARGET_NR_setresuid:
+ ret = get_errno(setresuid(low2highuid(arg1),
+ low2highuid(arg2),
+ low2highuid(arg3)));
+ break;
+#endif
+#ifdef TARGET_NR_getresuid
+ case TARGET_NR_getresuid:
+ {
+ uid_t ruid, euid, suid;
+ ret = get_errno(getresuid(&ruid, &euid, &suid));
+ if (!is_error(ret)) {
+ tput16(arg1, tswap16(high2lowuid(ruid)));
+ tput16(arg2, tswap16(high2lowuid(euid)));
+ tput16(arg3, tswap16(high2lowuid(suid)));
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_getresgid
+ case TARGET_NR_setresgid:
+ ret = get_errno(setresgid(low2highgid(arg1),
+ low2highgid(arg2),
+ low2highgid(arg3)));
+ break;
+#endif
+#ifdef TARGET_NR_getresgid
+ case TARGET_NR_getresgid:
+ {
+ gid_t rgid, egid, sgid;
+ ret = get_errno(getresgid(&rgid, &egid, &sgid));
+ if (!is_error(ret)) {
+ tput16(arg1, tswap16(high2lowgid(rgid)));
+ tput16(arg2, tswap16(high2lowgid(egid)));
+ tput16(arg3, tswap16(high2lowgid(sgid)));
+ }
+ }
+ break;
+#endif
+ case TARGET_NR_chown:
+ p = lock_user_string(arg1);
+ ret = get_errno(chown(p, low2highuid(arg2), low2highgid(arg3)));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_setuid:
+ ret = get_errno(setuid(low2highuid(arg1)));
+ break;
+ case TARGET_NR_setgid:
+ ret = get_errno(setgid(low2highgid(arg1)));
+ break;
+ case TARGET_NR_setfsuid:
+ ret = get_errno(setfsuid(arg1));
+ break;
+ case TARGET_NR_setfsgid:
+ ret = get_errno(setfsgid(arg1));
+ break;
+#endif /* USE_UID16 */
+
+#ifdef TARGET_NR_lchown32
+ case TARGET_NR_lchown32:
+ p = lock_user_string(arg1);
+ ret = get_errno(lchown(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_getuid32
+ case TARGET_NR_getuid32:
+ ret = get_errno(getuid());
+ break;
+#endif
+#ifdef TARGET_NR_getgid32
+ case TARGET_NR_getgid32:
+ ret = get_errno(getgid());
+ break;
+#endif
+#ifdef TARGET_NR_geteuid32
+ case TARGET_NR_geteuid32:
+ ret = get_errno(geteuid());
+ break;
+#endif
+#ifdef TARGET_NR_getegid32
+ case TARGET_NR_getegid32:
+ ret = get_errno(getegid());
+ break;
+#endif
+#ifdef TARGET_NR_setreuid32
+ case TARGET_NR_setreuid32:
+ ret = get_errno(setreuid(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_setregid32
+ case TARGET_NR_setregid32:
+ ret = get_errno(setregid(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_getgroups32
+ case TARGET_NR_getgroups32:
+ {
+ int gidsetsize = arg1;
+ uint32_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ ret = get_errno(getgroups(gidsetsize, grouplist));
+ if (!is_error(ret)) {
+ target_grouplist = lock_user(arg2, gidsetsize * 4, 0);
+ for(i = 0;i < gidsetsize; i++)
+ target_grouplist[i] = tswap32(grouplist[i]);
+ unlock_user(target_grouplist, arg2, gidsetsize * 4);
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_setgroups32
+ case TARGET_NR_setgroups32:
+ {
+ int gidsetsize = arg1;
+ uint32_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ target_grouplist = lock_user(arg2, gidsetsize * 4, 1);
+ for(i = 0;i < gidsetsize; i++)
+ grouplist[i] = tswap32(target_grouplist[i]);
+ unlock_user(target_grouplist, arg2, 0);
+ ret = get_errno(setgroups(gidsetsize, grouplist));
+ }
+ break;
+#endif
+#ifdef TARGET_NR_fchown32
+ case TARGET_NR_fchown32:
+ ret = get_errno(fchown(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_setresuid32
+ case TARGET_NR_setresuid32:
+ ret = get_errno(setresuid(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_getresuid32
+ case TARGET_NR_getresuid32:
+ {
+ uid_t ruid, euid, suid;
+ ret = get_errno(getresuid(&ruid, &euid, &suid));
+ if (!is_error(ret)) {
+ tput32(arg1, tswap32(ruid));
+ tput32(arg2, tswap32(euid));
+ tput32(arg3, tswap32(suid));
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_setresgid32
+ case TARGET_NR_setresgid32:
+ ret = get_errno(setresgid(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_getresgid32
+ case TARGET_NR_getresgid32:
+ {
+ gid_t rgid, egid, sgid;
+ ret = get_errno(getresgid(&rgid, &egid, &sgid));
+ if (!is_error(ret)) {
+ tput32(arg1, tswap32(rgid));
+ tput32(arg2, tswap32(egid));
+ tput32(arg3, tswap32(sgid));
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_chown32
+ case TARGET_NR_chown32:
+ p = lock_user_string(arg1);
+ ret = get_errno(chown(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_setuid32
+ case TARGET_NR_setuid32:
+ ret = get_errno(setuid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setgid32
+ case TARGET_NR_setgid32:
+ ret = get_errno(setgid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setfsuid32
+ case TARGET_NR_setfsuid32:
+ ret = get_errno(setfsuid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setfsgid32
+ case TARGET_NR_setfsgid32:
+ ret = get_errno(setfsgid(arg1));
+ break;
+#endif
+
+ case TARGET_NR_pivot_root:
+ goto unimplemented;
+#ifdef TARGET_NR_mincore
+ case TARGET_NR_mincore:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_madvise
+ case TARGET_NR_madvise:
+ /* A straight passthrough may not be safe because qemu sometimes
+ turns private flie-backed mappings into anonymous mappings.
+ This will break MADV_DONTNEED.
+ This is a hint, so ignoring and returning success is ok. */
+ ret = get_errno(0);
+ break;
+#endif
+#if TARGET_LONG_BITS == 32
+ case TARGET_NR_fcntl64:
+ {
+ struct flock64 fl;
+ struct target_flock64 *target_fl;
+#ifdef TARGET_ARM
+ struct target_eabi_flock64 *target_efl;
+#endif
+
+ switch(arg2) {
+ case F_GETLK64:
+ ret = get_errno(fcntl(arg1, arg2, &fl));
+ if (ret == 0) {
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ lock_user_struct(target_efl, arg3, 0);
+ target_efl->l_type = tswap16(fl.l_type);
+ target_efl->l_whence = tswap16(fl.l_whence);
+ target_efl->l_start = tswap64(fl.l_start);
+ target_efl->l_len = tswap64(fl.l_len);
+ target_efl->l_pid = tswapl(fl.l_pid);
+ unlock_user_struct(target_efl, arg3, 1);
+ } else
+#endif
+ {
+ lock_user_struct(target_fl, arg3, 0);
+ target_fl->l_type = tswap16(fl.l_type);
+ target_fl->l_whence = tswap16(fl.l_whence);
+ target_fl->l_start = tswap64(fl.l_start);
+ target_fl->l_len = tswap64(fl.l_len);
+ target_fl->l_pid = tswapl(fl.l_pid);
+ unlock_user_struct(target_fl, arg3, 1);
+ }
+ }
+ break;
+
+ case F_SETLK64:
+ case F_SETLKW64:
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ lock_user_struct(target_efl, arg3, 1);
+ fl.l_type = tswap16(target_efl->l_type);
+ fl.l_whence = tswap16(target_efl->l_whence);
+ fl.l_start = tswap64(target_efl->l_start);
+ fl.l_len = tswap64(target_efl->l_len);
+ fl.l_pid = tswapl(target_efl->l_pid);
+ unlock_user_struct(target_efl, arg3, 0);
+ } else
+#endif
+ {
+ lock_user_struct(target_fl, arg3, 1);
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswap64(target_fl->l_start);
+ fl.l_len = tswap64(target_fl->l_len);
+ fl.l_pid = tswapl(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg3, 0);
+ }
+ ret = get_errno(fcntl(arg1, arg2, &fl));
+ break;
+ default:
+ ret = get_errno(do_fcntl(arg1, arg2, arg3));
+ break;
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_security
+ case TARGET_NR_security:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_getpagesize
+ case TARGET_NR_getpagesize:
+ ret = TARGET_PAGE_SIZE;
+ break;
+#endif
+ case TARGET_NR_gettid:
+ ret = get_errno(gettid());
+ break;
+ case TARGET_NR_readahead:
+ goto unimplemented;
+#ifdef TARGET_NR_setxattr
+ case TARGET_NR_setxattr:
+ case TARGET_NR_lsetxattr:
+ case TARGET_NR_fsetxattr:
+ case TARGET_NR_getxattr:
+ case TARGET_NR_lgetxattr:
+ case TARGET_NR_fgetxattr:
+ case TARGET_NR_listxattr:
+ case TARGET_NR_llistxattr:
+ case TARGET_NR_flistxattr:
+ case TARGET_NR_removexattr:
+ case TARGET_NR_lremovexattr:
+ case TARGET_NR_fremovexattr:
+ goto unimplemented_nowarn;
+#endif
+#ifdef TARGET_NR_set_thread_area
+ case TARGET_NR_set_thread_area:
+ case TARGET_NR_get_thread_area:
+ goto unimplemented_nowarn;
+#endif
+#ifdef TARGET_NR_getdomainname
+ case TARGET_NR_getdomainname:
+ goto unimplemented_nowarn;
+#endif
+ default:
+ unimplemented:
+ gemu_log("qemu: Unsupported syscall: %d\n", num);
+#if defined(TARGET_NR_setxattr) || defined(TARGET_NR_set_thread_area) || defined(TARGET_NR_getdomainname)
+ unimplemented_nowarn:
+#endif
+ ret = -ENOSYS;
+ break;
+ }
+ fail:
+#ifdef DEBUG
+ gemu_log(" = %ld\n", ret);
+#endif
+ return ret;
+}
+
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
new file mode 100644
index 0000000..73a5c42
--- /dev/null
+++ b/linux-user/syscall_defs.h
@@ -0,0 +1,1516 @@
+/* common syscall defines for all architectures */
+
+/* Note: although the syscall numbers change between architectures,
+ most of them stay the same, so we handle it by puting ifdefs if
+ necessary */
+
+#include "syscall_nr.h"
+
+#define SOCKOP_socket 1
+#define SOCKOP_bind 2
+#define SOCKOP_connect 3
+#define SOCKOP_listen 4
+#define SOCKOP_accept 5
+#define SOCKOP_getsockname 6
+#define SOCKOP_getpeername 7
+#define SOCKOP_socketpair 8
+#define SOCKOP_send 9
+#define SOCKOP_recv 10
+#define SOCKOP_sendto 11
+#define SOCKOP_recvfrom 12
+#define SOCKOP_shutdown 13
+#define SOCKOP_setsockopt 14
+#define SOCKOP_getsockopt 15
+#define SOCKOP_sendmsg 16
+#define SOCKOP_recvmsg 17
+
+#define IPCOP_semop 1
+#define IPCOP_semget 2
+#define IPCOP_semctl 3
+#define IPCOP_semtimedop 4
+#define IPCOP_msgsnd 11
+#define IPCOP_msgrcv 12
+#define IPCOP_msgget 13
+#define IPCOP_msgctl 14
+#define IPCOP_shmat 21
+#define IPCOP_shmdt 22
+#define IPCOP_shmget 23
+#define IPCOP_shmctl 24
+
+/*
+ * The following is for compatibility across the various Linux
+ * platforms. The i386 ioctl numbering scheme doesn't really enforce
+ * a type field. De facto, however, the top 8 bits of the lower 16
+ * bits are indeed used as a type field, so we might just as well make
+ * this explicit here. Please be sure to use the decoding macros
+ * below from now on.
+ */
+#define TARGET_IOC_NRBITS 8
+#define TARGET_IOC_TYPEBITS 8
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4)
+
+#define TARGET_IOC_SIZEBITS 14
+#define TARGET_IOC_DIRBITS 2
+
+#define TARGET_IOC_NONE 0U
+#define TARGET_IOC_WRITE 1U
+#define TARGET_IOC_READ 2U
+
+#elif defined(TARGET_PPC) || defined(TARGET_ALPHA) || \
+ defined(TARGET_SPARC) || defined(TARGET_MIPS)
+
+#define TARGET_IOC_SIZEBITS 13
+#define TARGET_IOC_DIRBITS 3
+
+#define TARGET_IOC_NONE 1U
+#define TARGET_IOC_READ 2U
+#define TARGET_IOC_WRITE 4U
+
+#else
+#error unsupported CPU
+#endif
+
+#define TARGET_IOC_NRMASK ((1 << TARGET_IOC_NRBITS)-1)
+#define TARGET_IOC_TYPEMASK ((1 << TARGET_IOC_TYPEBITS)-1)
+#define TARGET_IOC_SIZEMASK ((1 << TARGET_IOC_SIZEBITS)-1)
+#define TARGET_IOC_DIRMASK ((1 << TARGET_IOC_DIRBITS)-1)
+
+#define TARGET_IOC_NRSHIFT 0
+#define TARGET_IOC_TYPESHIFT (TARGET_IOC_NRSHIFT+TARGET_IOC_NRBITS)
+#define TARGET_IOC_SIZESHIFT (TARGET_IOC_TYPESHIFT+TARGET_IOC_TYPEBITS)
+#define TARGET_IOC_DIRSHIFT (TARGET_IOC_SIZESHIFT+TARGET_IOC_SIZEBITS)
+
+#define TARGET_IOC(dir,type,nr,size) \
+ (((dir) << TARGET_IOC_DIRSHIFT) | \
+ ((type) << TARGET_IOC_TYPESHIFT) | \
+ ((nr) << TARGET_IOC_NRSHIFT) | \
+ ((size) << TARGET_IOC_SIZESHIFT))
+
+/* used to create numbers */
+#define TARGET_IO(type,nr) TARGET_IOC(TARGET_IOC_NONE,(type),(nr),0)
+#define TARGET_IOR(type,nr,size) TARGET_IOC(TARGET_IOC_READ,(type),(nr),sizeof(size))
+#define TARGET_IOW(type,nr,size) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),sizeof(size))
+#define TARGET_IOWR(type,nr,size) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),sizeof(size))
+
+/* the size is automatically computed for these defines */
+#define TARGET_IORU(type,nr) TARGET_IOC(TARGET_IOC_READ,(type),(nr),TARGET_IOC_SIZEMASK)
+#define TARGET_IOWU(type,nr) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK)
+#define TARGET_IOWRU(type,nr) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK)
+
+struct target_sockaddr {
+ uint16_t sa_family;
+ uint8_t sa_data[14];
+};
+
+struct target_timeval {
+ target_long tv_sec;
+ target_long tv_usec;
+};
+
+struct target_timespec {
+ target_long tv_sec;
+ target_long tv_nsec;
+};
+
+struct target_itimerval {
+ struct target_timeval it_interval;
+ struct target_timeval it_value;
+};
+
+typedef target_long target_clock_t;
+
+#define TARGET_HZ 100
+
+struct target_tms {
+ target_clock_t tms_utime;
+ target_clock_t tms_stime;
+ target_clock_t tms_cutime;
+ target_clock_t tms_cstime;
+};
+
+struct target_utimbuf {
+ target_long actime;
+ target_long modtime;
+};
+
+struct target_sel_arg_struct {
+ target_long n;
+ target_long inp, outp, exp;
+ target_long tvp;
+};
+
+struct target_iovec {
+ target_long iov_base; /* Starting address */
+ target_long iov_len; /* Number of bytes */
+};
+
+struct target_msghdr {
+ target_long msg_name; /* Socket name */
+ int msg_namelen; /* Length of name */
+ target_long msg_iov; /* Data blocks */
+ target_long msg_iovlen; /* Number of blocks */
+ target_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
+ target_long msg_controllen; /* Length of cmsg list */
+ unsigned int msg_flags;
+};
+
+struct target_cmsghdr {
+ target_long cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+#define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1))
+#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg)
+#define TARGET_CMSG_FIRSTHDR(mhdr) \
+ ((size_t) tswapl((mhdr)->msg_controllen) >= sizeof (struct target_cmsghdr) \
+ ? (struct target_cmsghdr *) tswapl((mhdr)->msg_control) : (struct target_cmsghdr *) NULL)
+#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (target_long) - 1) \
+ & (size_t) ~(sizeof (target_long) - 1))
+#define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \
+ + TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)))
+#define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len))
+
+static __inline__ struct target_cmsghdr *
+__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg)
+{
+ if (tswapl(__cmsg->cmsg_len) < sizeof (struct target_cmsghdr))
+ /* The kernel header does this so there may be a reason. */
+ return 0;
+
+ __cmsg = (struct target_cmsghdr *) ((unsigned char *) __cmsg
+ + TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len)));
+ if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) tswapl(__mhdr->msg_control)
+ + tswapl(__mhdr->msg_controllen))
+ || ((unsigned char *) __cmsg + TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len))
+ > ((unsigned char *) tswapl(__mhdr->msg_control)
+ + tswapl(__mhdr->msg_controllen))))
+ /* No more entries. */
+ return 0;
+ return __cmsg;
+}
+
+
+struct target_rusage {
+ struct target_timeval ru_utime; /* user time used */
+ struct target_timeval ru_stime; /* system time used */
+ target_long ru_maxrss; /* maximum resident set size */
+ target_long ru_ixrss; /* integral shared memory size */
+ target_long ru_idrss; /* integral unshared data size */
+ target_long ru_isrss; /* integral unshared stack size */
+ target_long ru_minflt; /* page reclaims */
+ target_long ru_majflt; /* page faults */
+ target_long ru_nswap; /* swaps */
+ target_long ru_inblock; /* block input operations */
+ target_long ru_oublock; /* block output operations */
+ target_long ru_msgsnd; /* messages sent */
+ target_long ru_msgrcv; /* messages received */
+ target_long ru_nsignals; /* signals received */
+ target_long ru_nvcsw; /* voluntary context switches */
+ target_long ru_nivcsw; /* involuntary " */
+};
+
+typedef struct {
+ int val[2];
+} kernel_fsid_t;
+
+struct kernel_statfs {
+ int f_type;
+ int f_bsize;
+ int f_blocks;
+ int f_bfree;
+ int f_bavail;
+ int f_files;
+ int f_ffree;
+ kernel_fsid_t f_fsid;
+ int f_namelen;
+ int f_spare[6];
+};
+
+struct target_dirent {
+ target_long d_ino;
+ target_long d_off;
+ unsigned short d_reclen;
+ char d_name[256]; /* We must not include limits.h! */
+};
+
+struct target_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+};
+
+
+/* mostly generic signal stuff */
+#define TARGET_SIG_DFL ((target_long)0) /* default signal handling */
+#define TARGET_SIG_IGN ((target_long)1) /* ignore signal */
+#define TARGET_SIG_ERR ((target_long)-1) /* error return from signal */
+
+#ifdef TARGET_MIPS
+#define TARGET_NSIG 128
+#else
+#define TARGET_NSIG 64
+#endif
+#define TARGET_NSIG_BPW TARGET_LONG_BITS
+#define TARGET_NSIG_WORDS (TARGET_NSIG / TARGET_NSIG_BPW)
+
+typedef struct {
+ target_ulong sig[TARGET_NSIG_WORDS];
+} target_sigset_t;
+
+#ifdef BSWAP_NEEDED
+static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
+{
+ int i;
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = tswapl(s->sig[i]);
+}
+#else
+static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
+{
+ *d = *s;
+}
+#endif
+
+static inline void target_siginitset(target_sigset_t *d, target_ulong set)
+{
+ int i;
+ d->sig[0] = set;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = 0;
+}
+
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s);
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s);
+void host_to_target_old_sigset(target_ulong *old_sigset,
+ const sigset_t *sigset);
+void target_to_host_old_sigset(sigset_t *sigset,
+ const target_ulong *old_sigset);
+struct target_sigaction;
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact);
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined (TARGET_SH4)
+
+#if defined(TARGET_SPARC)
+#define TARGET_SA_NOCLDSTOP 8u
+#define TARGET_SA_NOCLDWAIT 0x100u
+#define TARGET_SA_SIGINFO 0x200u
+#define TARGET_SA_ONSTACK 1u
+#define TARGET_SA_RESTART 2u
+#define TARGET_SA_NODEFER 0x20u
+#define TARGET_SA_RESETHAND 4u
+#elif defined(TARGET_MIPS)
+#define TARGET_SA_NOCLDSTOP 0x00000001
+#define TARGET_SA_NOCLDWAIT 0x00010000
+#define TARGET_SA_SIGINFO 0x00000008
+#define TARGET_SA_ONSTACK 0x08000000
+#define TARGET_SA_NODEFER 0x40000000
+#define TARGET_SA_RESTART 0x10000000
+#define TARGET_SA_RESETHAND 0x80000000
+#define TARGET_SA_RESTORER 0x04000000 /* Only for o32 */
+#else
+#define TARGET_SA_NOCLDSTOP 0x00000001
+#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */
+#define TARGET_SA_SIGINFO 0x00000004
+#define TARGET_SA_ONSTACK 0x08000000
+#define TARGET_SA_RESTART 0x10000000
+#define TARGET_SA_NODEFER 0x40000000
+#define TARGET_SA_RESETHAND 0x80000000
+#define TARGET_SA_RESTORER 0x04000000
+#endif
+
+#if defined(TARGET_SPARC)
+
+#define TARGET_SIGHUP 1
+#define TARGET_SIGINT 2
+#define TARGET_SIGQUIT 3
+#define TARGET_SIGILL 4
+#define TARGET_SIGTRAP 5
+#define TARGET_SIGABRT 6
+#define TARGET_SIGIOT 6
+#define TARGET_SIGSTKFLT 7 /* actually EMT */
+#define TARGET_SIGFPE 8
+#define TARGET_SIGKILL 9
+#define TARGET_SIGBUS 10
+#define TARGET_SIGSEGV 11
+#define TARGET_SIGSYS 12
+#define TARGET_SIGPIPE 13
+#define TARGET_SIGALRM 14
+#define TARGET_SIGTERM 15
+#define TARGET_SIGURG 16
+#define TARGET_SIGSTOP 17
+#define TARGET_SIGTSTP 18
+#define TARGET_SIGCONT 19
+#define TARGET_SIGCHLD 20
+#define TARGET_SIGTTIN 21
+#define TARGET_SIGTTOU 22
+#define TARGET_SIGIO 23
+#define TARGET_SIGXCPU 24
+#define TARGET_SIGXFSZ 25
+#define TARGET_SIGVTALRM 26
+#define TARGET_SIGPROF 27
+#define TARGET_SIGWINCH 28
+#define TARGET_SIGPWR 29
+#define TARGET_SIGUSR1 30
+#define TARGET_SIGUSR2 31
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */
+
+#elif defined(TARGET_MIPS)
+
+#define TARGET_SIGHUP 1 /* Hangup (POSIX). */
+#define TARGET_SIGINT 2 /* Interrupt (ANSI). */
+#define TARGET_SIGQUIT 3 /* Quit (POSIX). */
+#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */
+#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */
+#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */
+#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */
+#define TARGET_SIGEMT 7
+#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */
+#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */
+#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */
+#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */
+#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */
+#define TARGET_SIGSYS 12
+#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */
+#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */
+#define TARGET_SIGTERM 15 /* Termination (ANSI). */
+#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */
+#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */
+#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */
+#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */
+#define TARGET_SIGPWR 19 /* Power failure restart (System V). */
+#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */
+#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */
+#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */
+#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */
+#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */
+#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */
+#define TARGET_SIGCONT 25 /* Continue (POSIX). */
+#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */
+#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */
+#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */
+#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */
+#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */
+#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 1 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */
+
+#else
+
+#define TARGET_SIGHUP 1
+#define TARGET_SIGINT 2
+#define TARGET_SIGQUIT 3
+#define TARGET_SIGILL 4
+#define TARGET_SIGTRAP 5
+#define TARGET_SIGABRT 6
+#define TARGET_SIGIOT 6
+#define TARGET_SIGBUS 7
+#define TARGET_SIGFPE 8
+#define TARGET_SIGKILL 9
+#define TARGET_SIGUSR1 10
+#define TARGET_SIGSEGV 11
+#define TARGET_SIGUSR2 12
+#define TARGET_SIGPIPE 13
+#define TARGET_SIGALRM 14
+#define TARGET_SIGTERM 15
+#define TARGET_SIGSTKFLT 16
+#define TARGET_SIGCHLD 17
+#define TARGET_SIGCONT 18
+#define TARGET_SIGSTOP 19
+#define TARGET_SIGTSTP 20
+#define TARGET_SIGTTIN 21
+#define TARGET_SIGTTOU 22
+#define TARGET_SIGURG 23
+#define TARGET_SIGXCPU 24
+#define TARGET_SIGXFSZ 25
+#define TARGET_SIGVTALRM 26
+#define TARGET_SIGPROF 27
+#define TARGET_SIGWINCH 28
+#define TARGET_SIGIO 29
+#define TARGET_SIGPWR 30
+#define TARGET_SIGSYS 31
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 0 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */
+
+#endif
+
+#if defined(TARGET_MIPS)
+
+struct target_sigaction {
+ target_ulong sa_flags;
+ target_ulong _sa_handler;
+ target_sigset_t sa_mask;
+};
+
+#else
+struct target_old_sigaction {
+ target_ulong _sa_handler;
+ target_ulong sa_mask;
+ target_ulong sa_flags;
+ target_ulong sa_restorer;
+};
+
+struct target_sigaction {
+ target_ulong _sa_handler;
+ target_ulong sa_flags;
+ target_ulong sa_restorer;
+ target_sigset_t sa_mask;
+};
+#endif
+
+typedef union target_sigval {
+ int sival_int;
+ target_ulong sival_ptr;
+} target_sigval_t;
+#if 0
+#if defined (TARGET_SPARC)
+typedef struct {
+ struct {
+ target_ulong psr;
+ target_ulong pc;
+ target_ulong npc;
+ target_ulong y;
+ target_ulong u_regs[16]; /* globals and ins */
+ } si_regs;
+ int si_mask;
+} __siginfo_t;
+
+typedef struct {
+ unsigned long si_float_regs [32];
+ unsigned long si_fsr;
+ unsigned long si_fpqdepth;
+ struct {
+ unsigned long *insn_addr;
+ unsigned long insn;
+ } si_fpqueue [16];
+} __siginfo_fpu_t;
+#endif
+#endif
+
+#define TARGET_SI_MAX_SIZE 128
+#define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct target_siginfo {
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ union {
+ int _pad[TARGET_SI_PAD_SIZE];
+
+ /* kill() */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct {
+ unsigned int _timer1;
+ unsigned int _timer2;
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ target_sigval_t _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct {
+ pid_t _pid; /* which child */
+ uid_t _uid; /* sender's uid */
+ int _status; /* exit code */
+ target_clock_t _utime;
+ target_clock_t _stime;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ struct {
+ target_ulong _addr; /* faulting insn/memory ref. */
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct {
+ int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} target_siginfo_t;
+
+/*
+ * si_code values
+ * Digital reserves positive values for kernel-generated signals.
+ */
+#define TARGET_SI_USER 0 /* sent by kill, sigsend, raise */
+#define TARGET_SI_KERNEL 0x80 /* sent by the kernel from somewhere */
+#define TARGET_SI_QUEUE -1 /* sent by sigqueue */
+#define TARGET_SI_TIMER -2 /* sent by timer expiration */
+#define TARGET_SI_MESGQ -3 /* sent by real time mesq state change */
+#define TARGET_SI_ASYNCIO -4 /* sent by AIO completion */
+#define TARGET_SI_SIGIO -5 /* sent by queued SIGIO */
+
+/*
+ * SIGILL si_codes
+ */
+#define TARGET_ILL_ILLOPC (1) /* illegal opcode */
+#define TARGET_ILL_ILLOPN (2) /* illegal operand */
+#define TARGET_ILL_ILLADR (3) /* illegal addressing mode */
+#define TARGET_ILL_ILLTRP (4) /* illegal trap */
+#define TARGET_ILL_PRVOPC (5) /* privileged opcode */
+#define TARGET_ILL_PRVREG (6) /* privileged register */
+#define TARGET_ILL_COPROC (7) /* coprocessor error */
+#define TARGET_ILL_BADSTK (8) /* internal stack error */
+
+/*
+ * SIGFPE si_codes
+ */
+#define TARGET_FPE_INTDIV (1) /* integer divide by zero */
+#define TARGET_FPE_INTOVF (2) /* integer overflow */
+#define TARGET_FPE_FLTDIV (3) /* floating point divide by zero */
+#define TARGET_FPE_FLTOVF (4) /* floating point overflow */
+#define TARGET_FPE_FLTUND (5) /* floating point underflow */
+#define TARGET_FPE_FLTRES (6) /* floating point inexact result */
+#define TARGET_FPE_FLTINV (7) /* floating point invalid operation */
+#define TARGET_FPE_FLTSUB (8) /* subscript out of range */
+#define TARGET_NSIGFPE 8
+
+/*
+ * SIGSEGV si_codes
+ */
+#define TARGET_SEGV_MAPERR (1) /* address not mapped to object */
+#define TARGET_SEGV_ACCERR (2) /* invalid permissions for mapped object */
+
+/*
+ * SIGBUS si_codes
+ */
+#define TARGET_BUS_ADRALN (1) /* invalid address alignment */
+#define TARGET_BUS_ADRERR (2) /* non-existant physical address */
+#define TARGET_BUS_OBJERR (3) /* object specific hardware error */
+
+/*
+ * SIGTRAP si_codes
+ */
+#define TARGET_TRAP_BRKPT (1) /* process breakpoint */
+#define TARGET_TRAP_TRACE (2) /* process trace trap */
+
+#endif /* defined(TARGET_I386) || defined(TARGET_ARM) */
+
+struct target_rlimit {
+ target_ulong rlim_cur;
+ target_ulong rlim_max;
+};
+
+struct target_pollfd {
+ int fd; /* file descriptor */
+ short events; /* requested events */
+ short revents; /* returned events */
+};
+
+/* virtual terminal ioctls */
+#define TARGET_KIOCSOUND 0x4B2F /* start sound generation (0 for off) */
+#define TARGET_KDMKTONE 0x4B30 /* generate tone */
+#define TARGET_KDGKBTYPE 0x4b33
+#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */
+#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */
+
+#define TARGET_SIOCATMARK 0x8905
+
+/* Networking ioctls */
+#define TARGET_SIOCADDRT 0x890B /* add routing table entry */
+#define TARGET_SIOCDELRT 0x890C /* delete routing table entry */
+#define TARGET_SIOCGIFNAME 0x8910 /* get iface name */
+#define TARGET_SIOCSIFLINK 0x8911 /* set iface channel */
+#define TARGET_SIOCGIFCONF 0x8912 /* get iface list */
+#define TARGET_SIOCGIFFLAGS 0x8913 /* get flags */
+#define TARGET_SIOCSIFFLAGS 0x8914 /* set flags */
+#define TARGET_SIOCGIFADDR 0x8915 /* get PA address */
+#define TARGET_SIOCSIFADDR 0x8916 /* set PA address */
+#define TARGET_SIOCGIFDSTADDR 0x8917 /* get remote PA address */
+#define TARGET_SIOCSIFDSTADDR 0x8918 /* set remote PA address */
+#define TARGET_SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
+#define TARGET_SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
+#define TARGET_SIOCGIFNETMASK 0x891b /* get network PA mask */
+#define TARGET_SIOCSIFNETMASK 0x891c /* set network PA mask */
+#define TARGET_SIOCGIFMETRIC 0x891d /* get metric */
+#define TARGET_SIOCSIFMETRIC 0x891e /* set metric */
+#define TARGET_SIOCGIFMEM 0x891f /* get memory address (BSD) */
+#define TARGET_SIOCSIFMEM 0x8920 /* set memory address (BSD) */
+#define TARGET_SIOCGIFMTU 0x8921 /* get MTU size */
+#define TARGET_SIOCSIFMTU 0x8922 /* set MTU size */
+#define TARGET_SIOCSIFHWADDR 0x8924 /* set hardware address (NI) */
+#define TARGET_SIOCGIFENCAP 0x8925 /* get/set slip encapsulation */
+#define TARGET_SIOCSIFENCAP 0x8926
+#define TARGET_SIOCGIFHWADDR 0x8927 /* Get hardware address */
+#define TARGET_SIOCGIFSLAVE 0x8929 /* Driver slaving support */
+#define TARGET_SIOCSIFSLAVE 0x8930
+#define TARGET_SIOCADDMULTI 0x8931 /* Multicast address lists */
+#define TARGET_SIOCDELMULTI 0x8932
+
+/* Bridging control calls */
+#define TARGET_SIOCGIFBR 0x8940 /* Bridging support */
+#define TARGET_SIOCSIFBR 0x8941 /* Set bridging options */
+
+#define TARGET_SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
+#define TARGET_SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
+
+/* ARP cache control calls. */
+#define TARGET_OLD_SIOCDARP 0x8950 /* old delete ARP table entry */
+#define TARGET_OLD_SIOCGARP 0x8951 /* old get ARP table entry */
+#define TARGET_OLD_SIOCSARP 0x8952 /* old set ARP table entry */
+#define TARGET_SIOCDARP 0x8953 /* delete ARP table entry */
+#define TARGET_SIOCGARP 0x8954 /* get ARP table entry */
+#define TARGET_SIOCSARP 0x8955 /* set ARP table entry */
+
+/* RARP cache control calls. */
+#define TARGET_SIOCDRARP 0x8960 /* delete RARP table entry */
+#define TARGET_SIOCGRARP 0x8961 /* get RARP table entry */
+#define TARGET_SIOCSRARP 0x8962 /* set RARP table entry */
+
+/* Driver configuration calls */
+#define TARGET_SIOCGIFMAP 0x8970 /* Get device parameters */
+#define TARGET_SIOCSIFMAP 0x8971 /* Set device parameters */
+
+/* DLCI configuration calls */
+#define TARGET_SIOCADDDLCI 0x8980 /* Create new DLCI device */
+#define TARGET_SIOCDELDLCI 0x8981 /* Delete DLCI device */
+
+
+/* From <linux/fs.h> */
+
+#define TARGET_BLKROSET TARGET_IO(0x12,93) /* set device read-only (0 = read-write) */
+#define TARGET_BLKROGET TARGET_IO(0x12,94) /* get read-only status (0 = read_write) */
+#define TARGET_BLKRRPART TARGET_IO(0x12,95) /* re-read partition table */
+#define TARGET_BLKGETSIZE TARGET_IO(0x12,96) /* return device size /512 (long *arg) */
+#define TARGET_BLKFLSBUF TARGET_IO(0x12,97) /* flush buffer cache */
+#define TARGET_BLKRASET TARGET_IO(0x12,98) /* Set read ahead for block device */
+#define TARGET_BLKRAGET TARGET_IO(0x12,99) /* get current read ahead setting */
+#define TARGET_BLKFRASET TARGET_IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
+#define TARGET_BLKFRAGET TARGET_IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
+#define TARGET_BLKSECTSET TARGET_IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
+#define TARGET_BLKSECTGET TARGET_IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
+#define TARGET_BLKSSZGET TARGET_IO(0x12,104)/* get block device sector size */
+/* A jump here: 108-111 have been used for various private purposes. */
+#define TARGET_BLKBSZGET TARGET_IOR(0x12,112,sizeof(int))
+#define TARGET_BLKBSZSET TARGET_IOW(0x12,113,sizeof(int))
+#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */
+#define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */
+#define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */
+
+/* cdrom commands */
+#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
+#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */
+#define TARGET_CDROMPLAYMSF 0x5303 /* Play Audio MSF (struct cdrom_msf) */
+#define TARGET_CDROMPLAYTRKIND 0x5304 /* Play Audio Track/index
+ (struct cdrom_ti) */
+#define TARGET_CDROMREADTOCHDR 0x5305 /* Read TOC header
+ (struct cdrom_tochdr) */
+#define TARGET_CDROMREADTOCENTRY 0x5306 /* Read TOC entry
+ (struct cdrom_tocentry) */
+#define TARGET_CDROMSTOP 0x5307 /* Stop the cdrom drive */
+#define TARGET_CDROMSTART 0x5308 /* Start the cdrom drive */
+#define TARGET_CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define TARGET_CDROMVOLCTRL 0x530a /* Control output volume
+ (struct cdrom_volctrl) */
+#define TARGET_CDROMSUBCHNL 0x530b /* Read subchannel data
+ (struct cdrom_subchnl) */
+#define TARGET_CDROMREADMODE2 0x530c /* Read TARGET_CDROM mode 2 data (2336 Bytes)
+ (struct cdrom_read) */
+#define TARGET_CDROMREADMODE1 0x530d /* Read TARGET_CDROM mode 1 data (2048 Bytes)
+ (struct cdrom_read) */
+#define TARGET_CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
+#define TARGET_CDROMEJECT_SW 0x530f /* enable(1)/disable(0) auto-ejecting */
+#define TARGET_CDROMMULTISESSION 0x5310 /* Obtain the start-of-last-session
+ address of multi session disks
+ (struct cdrom_multisession) */
+#define TARGET_CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code"
+ if available (struct cdrom_mcn) */
+#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is depricated,
+ but here anyway for compatability */
+#define TARGET_CDROMRESET 0x5312 /* hard-reset the drive */
+#define TARGET_CDROMVOLREAD 0x5313 /* Get the drive's volume setting
+ (struct cdrom_volctrl) */
+#define TARGET_CDROMREADRAW 0x5314 /* read data in raw mode (2352 Bytes)
+ (struct cdrom_read) */
+/*
+ * These ioctls are used only used in aztcd.c and optcd.c
+ */
+#define TARGET_CDROMREADCOOKED 0x5315 /* read data in cooked mode */
+#define TARGET_CDROMSEEK 0x5316 /* seek msf address */
+
+/*
+ * This ioctl is only used by the scsi-cd driver.
+ It is for playing audio in logical block addressing mode.
+ */
+#define TARGET_CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */
+
+/*
+ * These ioctls are only used in optcd.c
+ */
+#define TARGET_CDROMREADALL 0x5318 /* read all 2646 bytes */
+
+/*
+ * These ioctls are (now) only in ide-cd.c for controlling
+ * drive spindown time. They should be implemented in the
+ * Uniform driver, via generic packet commands, GPCMD_MODE_SELECT_10,
+ * GPCMD_MODE_SENSE_10 and the GPMODE_POWER_PAGE...
+ * -Erik
+ */
+#define TARGET_CDROMGETSPINDOWN 0x531d
+#define TARGET_CDROMSETSPINDOWN 0x531e
+
+/*
+ * These ioctls are implemented through the uniform CD-ROM driver
+ * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
+ * drivers are eventually ported to the uniform CD-ROM driver interface.
+ */
+#define TARGET_CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+#define TARGET_CDROM_SET_OPTIONS 0x5320 /* Set behavior options */
+#define TARGET_CDROM_CLEAR_OPTIONS 0x5321 /* Clear behavior options */
+#define TARGET_CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */
+#define TARGET_CDROM_SELECT_DISC 0x5323 /* Select disc (for juke-boxes) */
+#define TARGET_CDROM_MEDIA_CHANGED 0x5325 /* Check is media changed */
+#define TARGET_CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+#define TARGET_CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */
+#define TARGET_CDROM_CHANGER_NSLOTS 0x5328 /* Get number of slots */
+#define TARGET_CDROM_LOCKDOOR 0x5329 /* lock or unlock door */
+#define TARGET_CDROM_DEBUG 0x5330 /* Turn debug messages on/off */
+#define TARGET_CDROM_GET_CAPABILITY 0x5331 /* get capabilities */
+
+/* Note that scsi/scsi_ioctl.h also uses 0x5382 - 0x5386.
+ * Future CDROM ioctls should be kept below 0x537F
+ */
+
+/* This ioctl is only used by sbpcd at the moment */
+#define TARGET_CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */
+ /* conflict with SCSI_IOCTL_GET_IDLUN */
+
+/* DVD-ROM Specific ioctls */
+#define TARGET_DVD_READ_STRUCT 0x5390 /* Read structure */
+#define TARGET_DVD_WRITE_STRUCT 0x5391 /* Write structure */
+#define TARGET_DVD_AUTH 0x5392 /* Authentication */
+
+#define TARGET_CDROM_SEND_PACKET 0x5393 /* send a packet to the drive */
+#define TARGET_CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */
+#define TARGET_CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */
+
+/* HD commands */
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define TARGET_HDIO_GETGEO 0x0301 /* get device geometry */
+#define TARGET_HDIO_GET_UNMASKINTR 0x0302 /* get current unmask setting */
+#define TARGET_HDIO_GET_MULTCOUNT 0x0304 /* get current IDE blockmode setting */
+#define TARGET_HDIO_GET_KEEPSETTINGS 0x0308 /* get keep-settings-on-reset flag */
+#define TARGET_HDIO_GET_32BIT 0x0309 /* get current io_32bit setting */
+#define TARGET_HDIO_GET_NOWERR 0x030a /* get ignore-write-error flag */
+#define TARGET_HDIO_GET_DMA 0x030b /* get use-dma flag */
+#define TARGET_HDIO_GET_IDENTITY 0x030d /* get IDE identification info */
+#define TARGET_HDIO_DRIVE_CMD 0x031f /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define TARGET_HDIO_SET_MULTCOUNT 0x0321 /* change IDE blockmode */
+#define TARGET_HDIO_SET_UNMASKINTR 0x0322 /* permit other irqs during I/O */
+#define TARGET_HDIO_SET_KEEPSETTINGS 0x0323 /* keep ioctl settings on reset */
+#define TARGET_HDIO_SET_32BIT 0x0324 /* change io_32bit flags */
+#define TARGET_HDIO_SET_NOWERR 0x0325 /* change ignore-write-error flag */
+#define TARGET_HDIO_SET_DMA 0x0326 /* change use-dma flag */
+#define TARGET_HDIO_SET_PIO_MODE 0x0327 /* reconfig interface to new speed */
+
+
+/* from asm/termbits.h */
+
+#define TARGET_NCC 8
+struct target_termio {
+ unsigned short c_iflag; /* input mode flags */
+ unsigned short c_oflag; /* output mode flags */
+ unsigned short c_cflag; /* control mode flags */
+ unsigned short c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCC]; /* control characters */
+};
+
+struct target_winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+};
+
+#include "termbits.h"
+
+#define TARGET_MAP_SHARED 0x01 /* Share changes */
+#define TARGET_MAP_PRIVATE 0x02 /* Changes are private */
+#define TARGET_MAP_TYPE 0x0f /* Mask for type of mapping */
+#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
+#if defined(TARGET_MIPS)
+#define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x4000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x8000 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */
+#else
+#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x2000 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */
+#endif
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4)
+struct target_stat {
+ unsigned short st_dev;
+ unsigned short __pad1;
+ target_ulong st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ unsigned short __pad2;
+ target_ulong st_size;
+ target_ulong st_blksize;
+ target_ulong st_blocks;
+ target_ulong target_st_atime;
+ target_ulong __unused1;
+ target_ulong target_st_mtime;
+ target_ulong __unused2;
+ target_ulong target_st_ctime;
+ target_ulong __unused3;
+ target_ulong __unused4;
+ target_ulong __unused5;
+};
+
+/* This matches struct stat64 in glibc2.1, hence the absolutely
+ * insane amounts of padding around dev_t's.
+ */
+struct target_stat64 {
+ unsigned short st_dev;
+ unsigned char __pad0[10];
+
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ target_ulong __st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ target_ulong st_uid;
+ target_ulong st_gid;
+
+ unsigned short st_rdev;
+ unsigned char __pad3[10];
+
+ long long st_size;
+ target_ulong st_blksize;
+
+ target_ulong st_blocks; /* Number 512-byte blocks allocated. */
+ target_ulong __pad4; /* future possible st_blocks high bits */
+
+ target_ulong target_st_atime;
+ target_ulong __pad5;
+
+ target_ulong target_st_mtime;
+ target_ulong __pad6;
+
+ target_ulong target_st_ctime;
+ target_ulong __pad7; /* will be high 32 bits of ctime someday */
+
+ unsigned long long st_ino;
+} __attribute__((packed));
+
+#ifdef TARGET_ARM
+struct target_eabi_stat64 {
+ unsigned long long st_dev;
+ unsigned int __pad1;
+ unsigned long __st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ unsigned long st_uid;
+ unsigned long st_gid;
+
+ unsigned long long st_rdev;
+ unsigned int __pad2[2];
+
+ long long st_size;
+ unsigned long st_blksize;
+ unsigned int __pad3;
+ unsigned long long st_blocks;
+
+ unsigned long target_st_atime;
+ unsigned long target_st_atime_nsec;
+
+ unsigned long target_st_mtime;
+ unsigned long target_st_mtime_nsec;
+
+ unsigned long target_st_ctime;
+ unsigned long target_st_ctime_nsec;
+
+ unsigned long long st_ino;
+} __attribute__ ((packed));
+#endif
+
+#elif defined(TARGET_SPARC)
+
+struct target_stat {
+ unsigned short st_dev;
+ target_ulong st_ino;
+ unsigned short st_mode;
+ short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ target_long st_size;
+ target_long target_st_atime;
+ target_ulong __unused1;
+ target_long target_st_mtime;
+ target_ulong __unused2;
+ target_long target_st_ctime;
+ target_ulong __unused3;
+ target_long st_blksize;
+ target_long st_blocks;
+ target_ulong __unused4[2];
+};
+
+struct target_stat64 {
+ unsigned char __pad0[6];
+ unsigned short st_dev;
+
+ uint64_t st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ unsigned int st_uid;
+ unsigned int st_gid;
+
+ unsigned char __pad2[6];
+ unsigned short st_rdev;
+
+ unsigned char __pad3[8];
+
+ int64_t st_size;
+ unsigned int st_blksize;
+
+ unsigned char __pad4[8];
+ unsigned int st_blocks;
+
+ unsigned int target_st_atime;
+ unsigned int __unused1;
+
+ unsigned int target_st_mtime;
+ unsigned int __unused2;
+
+ unsigned int target_st_ctime;
+ unsigned int __unused3;
+
+ unsigned int __unused4;
+ unsigned int __unused5;
+};
+
+#elif defined(TARGET_PPC)
+
+struct target_stat {
+ unsigned short st_dev;
+ target_ulong st_ino;
+ unsigned int st_mode;
+ unsigned short st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned short st_rdev;
+ target_ulong st_size;
+ target_ulong st_blksize;
+ target_ulong st_blocks;
+ target_ulong target_st_atime;
+ target_ulong __unused1;
+ target_ulong target_st_mtime;
+ target_ulong __unused2;
+ target_ulong target_st_ctime;
+ target_ulong __unused3;
+ target_ulong __unused4;
+ target_ulong __unused5;
+};
+
+struct target_stat64 {
+ unsigned long long st_dev;
+ unsigned long long st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned long long st_rdev;
+ long long pad0;
+ long long st_size;
+ target_ulong st_blksize;
+ target_ulong pad1;
+ long long st_blocks; /* Number 512-byte blocks allocated. */
+ target_ulong target_st_atime;
+ target_ulong target_st_atime_nsec;
+ target_ulong target_st_mtime;
+ target_ulong target_st_mtime_nsec;
+ target_ulong target_st_ctime;
+ target_ulong target_st_ctime_nsec;
+ target_ulong __unused4;
+ target_ulong __unused5;
+};
+
+#elif defined(TARGET_MIPS)
+
+struct target_stat {
+ unsigned st_dev;
+ target_long st_pad1[3]; /* Reserved for network id */
+ target_ulong st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ int st_uid;
+ int st_gid;
+ unsigned st_rdev;
+ target_long st_pad2[2];
+ target_long st_size;
+ target_long st_pad3;
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ target_long target_st_atime;
+ target_long target_st_atime_nsec;
+ target_long target_st_mtime;
+ target_long target_st_mtime_nsec;
+ target_long target_st_ctime;
+ target_long target_st_ctime_nsec;
+ target_long st_blksize;
+ target_long st_blocks;
+ target_long st_pad4[14];
+};
+
+/*
+ * This matches struct stat64 in glibc2.1, hence the absolutely insane
+ * amounts of padding around dev_t's. The memory layout is the same as of
+ * struct stat of the 64-bit kernel.
+ */
+
+struct target_stat64 {
+ target_ulong st_dev;
+ target_ulong st_pad0[3]; /* Reserved for st_dev expansion */
+
+ uint64_t st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ int st_uid;
+ int st_gid;
+
+ target_ulong st_rdev;
+ target_ulong st_pad1[3]; /* Reserved for st_rdev expansion */
+
+ int64_t st_size;
+
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ target_long target_st_atime;
+ target_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */
+
+ target_long target_st_mtime;
+ target_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */
+
+ target_long target_st_ctime;
+ target_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */
+
+ target_ulong st_blksize;
+ target_ulong st_pad2;
+
+ int64_t st_blocks;
+};
+#else
+#error unsupported CPU
+#endif
+
+#ifdef TARGET_MIPS
+struct target_statfs {
+ target_long f_type;
+ target_long f_bsize;
+ target_long f_frsize; /* Fragment size - unsupported */
+ target_long f_blocks;
+ target_long f_bfree;
+ target_long f_files;
+ target_long f_ffree;
+ target_long f_bavail;
+
+ /* Linux specials */
+ int f_fsid;
+ target_long f_namelen;
+ target_long f_spare[6];
+};
+
+struct target_statfs64 {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint32_t f_frsize; /* Fragment size - unsupported */
+ uint32_t __pad;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ uint64_t f_bavail;
+ int f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_spare[6];
+};
+#else
+struct target_statfs {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint32_t f_blocks;
+ uint32_t f_bfree;
+ uint32_t f_bavail;
+ uint32_t f_files;
+ uint32_t f_ffree;
+ int f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_frsize;
+ uint32_t f_spare[5];
+};
+
+struct target_statfs64 {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ int f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_frsize;
+ uint32_t f_spare[5];
+};
+#endif
+
+
+#define TARGET_F_DUPFD 0 /* dup */
+#define TARGET_F_GETFD 1 /* get close_on_exec */
+#define TARGET_F_SETFD 2 /* set/clear close_on_exec */
+#define TARGET_F_GETFL 3 /* get file->f_flags */
+#define TARGET_F_SETFL 4 /* set file->f_flags */
+
+#if defined(TARGET_ALPHA)
+#define TARGET_F_GETLK 7
+#define TARGET_F_SETLK 8
+#define TARGET_F_SETLKW 9
+#define TARGET_F_SETOWN 5 /* for sockets. */
+#define TARGET_F_GETOWN 6 /* for sockets. */
+#else
+#define TARGET_F_GETLK 5
+#define TARGET_F_SETLK 6
+#define TARGET_F_SETLKW 7
+#define TARGET_F_SETOWN 8 /* for sockets. */
+#define TARGET_F_GETOWN 9 /* for sockets. */
+#endif
+
+#define TARGET_F_SETSIG 10 /* for sockets. */
+#define TARGET_F_GETSIG 11 /* for sockets. */
+
+#define TARGET_F_GETLK64 12 /* using 'struct flock64' */
+#define TARGET_F_SETLK64 13
+#define TARGET_F_SETLKW64 14
+
+#if defined (TARGET_ARM)
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECTORY 040000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */
+#define TARGET_O_DIRECT 0200000 /* direct disk access hint */
+#define TARGET_O_LARGEFILE 0400000
+#elif defined (TARGET_PPC)
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECTORY 040000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */
+#define TARGET_O_LARGEFILE 0200000
+#define TARGET_O_DIRECT 0400000 /* direct disk access hint */
+#elif defined (TARGET_SPARC)
+#define TARGET_O_RDONLY 0x0000
+#define TARGET_O_WRONLY 0x0001
+#define TARGET_O_RDWR 0x0002
+#define TARGET_O_ACCMODE 0x0003
+#define TARGET_O_APPEND 0x0008
+#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */
+#define TARGET_O_CREAT 0x0200 /* not fcntl */
+#define TARGET_O_TRUNC 0x0400 /* not fcntl */
+#define TARGET_O_EXCL 0x0800 /* not fcntl */
+#define TARGET_O_SYNC 0x2000
+#define TARGET_O_NONBLOCK 0x4000
+#define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK)
+#define TARGET_O_NOCTTY 0x8000 /* not fcntl */
+#define TARGET_O_DIRECTORY 0x10000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0x20000 /* don't follow links */
+#define TARGET_O_LARGEFILE 0x40000
+#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */
+#elif defined(TARGET_MIPS)
+#define TARGET_O_ACCMODE 0x0003
+#define TARGET_O_RDONLY 0x0000
+#define TARGET_O_WRONLY 0x0001
+#define TARGET_O_RDWR 0x0002
+#define TARGET_O_APPEND 0x0008
+#define TARGET_O_SYNC 0x0010
+#define TARGET_O_NONBLOCK 0x0080
+#define TARGET_O_CREAT 0x0100 /* not fcntl */
+#define TARGET_O_TRUNC 0x0200 /* not fcntl */
+#define TARGET_O_EXCL 0x0400 /* not fcntl */
+#define TARGET_O_NOCTTY 0x0800 /* not fcntl */
+#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */
+#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */
+#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */
+#define TARGET_O_DIRECTORY 0x10000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0x20000 /* don't follow links */
+#define TARGET_O_NOATIME 0x40000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#else
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECT 040000 /* direct disk access hint */
+#define TARGET_O_LARGEFILE 0100000
+#define TARGET_O_DIRECTORY 0200000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */
+#endif
+
+struct target_flock {
+ short l_type;
+ short l_whence;
+ target_ulong l_start;
+ target_ulong l_len;
+ int l_pid;
+};
+
+struct target_flock64 {
+ short l_type;
+ short l_whence;
+ unsigned long long l_start;
+ unsigned long long l_len;
+ int l_pid;
+}__attribute__((packed));
+
+#ifdef TARGET_ARM
+struct target_eabi_flock64 {
+ short l_type;
+ short l_whence;
+ int __pad;
+ unsigned long long l_start;
+ unsigned long long l_len;
+ int l_pid;
+}__attribute__((packed));
+#endif
+
+/* soundcard defines */
+/* XXX: convert them all to arch indepedent entries */
+#define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, int);
+#define TARGET_SNDCTL_COPR_LOAD 0xcfb04301
+#define TARGET_SNDCTL_COPR_RCODE 0xc0144303
+#define TARGET_SNDCTL_COPR_RCVMSG 0x8fa44309
+#define TARGET_SNDCTL_COPR_RDATA 0xc0144302
+#define TARGET_SNDCTL_COPR_RESET 0x00004300
+#define TARGET_SNDCTL_COPR_RUN 0xc0144306
+#define TARGET_SNDCTL_COPR_SENDMSG 0xcfa44308
+#define TARGET_SNDCTL_COPR_WCODE 0x40144305
+#define TARGET_SNDCTL_COPR_WDATA 0x40144304
+#define TARGET_SNDCTL_DSP_RESET TARGET_IO('P', 0)
+#define TARGET_SNDCTL_DSP_SYNC TARGET_IO('P', 1)
+#define TARGET_SNDCTL_DSP_SPEED TARGET_IOWR('P', 2, int)
+#define TARGET_SNDCTL_DSP_STEREO TARGET_IOWR('P', 3, int)
+#define TARGET_SNDCTL_DSP_GETBLKSIZE TARGET_IOWR('P', 4, int)
+#define TARGET_SNDCTL_DSP_SETFMT TARGET_IOWR('P', 5, int)
+#define TARGET_SNDCTL_DSP_CHANNELS TARGET_IOWR('P', 6, int)
+#define TARGET_SOUND_PCM_WRITE_FILTER TARGET_IOWR('P', 7, int)
+#define TARGET_SNDCTL_DSP_POST TARGET_IO('P', 8)
+#define TARGET_SNDCTL_DSP_SUBDIVIDE TARGET_IOWR('P', 9, int)
+#define TARGET_SNDCTL_DSP_SETFRAGMENT TARGET_IOWR('P',10, int)
+#define TARGET_SNDCTL_DSP_GETFMTS TARGET_IOR('P', 11, int)
+#define TARGET_SNDCTL_DSP_GETOSPACE TARGET_IORU('P',12)
+#define TARGET_SNDCTL_DSP_GETISPACE TARGET_IORU('P',13)
+#define TARGET_SNDCTL_DSP_GETCAPS TARGET_IOR('P', 15, int)
+#define TARGET_SNDCTL_DSP_GETTRIGGER TARGET_IOR('P',16, int)
+#define TARGET_SNDCTL_DSP_GETIPTR TARGET_IORU('P',17)
+#define TARGET_SNDCTL_DSP_GETOPTR TARGET_IORU('P',18)
+#define TARGET_SNDCTL_DSP_MAPINBUF 0x80085013
+#define TARGET_SNDCTL_DSP_MAPOUTBUF 0x80085014
+#define TARGET_SNDCTL_DSP_NONBLOCK 0x0000500e
+#define TARGET_SNDCTL_DSP_SAMPLESIZE 0xc0045005
+#define TARGET_SNDCTL_DSP_SETDUPLEX 0x00005016
+#define TARGET_SNDCTL_DSP_SETSYNCRO 0x00005015
+#define TARGET_SNDCTL_DSP_SETTRIGGER 0x40045010
+#define TARGET_SNDCTL_FM_4OP_ENABLE 0x4004510f
+#define TARGET_SNDCTL_FM_LOAD_INSTR 0x40285107
+#define TARGET_SNDCTL_MIDI_INFO 0xc074510c
+#define TARGET_SNDCTL_MIDI_MPUCMD 0xc0216d02
+#define TARGET_SNDCTL_MIDI_MPUMODE 0xc0046d01
+#define TARGET_SNDCTL_MIDI_PRETIME 0xc0046d00
+#define TARGET_SNDCTL_PMGR_ACCESS 0xcfb85110
+#define TARGET_SNDCTL_PMGR_IFACE 0xcfb85001
+#define TARGET_SNDCTL_SEQ_CTRLRATE 0xc0045103
+#define TARGET_SNDCTL_SEQ_GETINCOUNT 0x80045105
+#define TARGET_SNDCTL_SEQ_GETOUTCOUNT 0x80045104
+#define TARGET_SNDCTL_SEQ_NRMIDIS 0x8004510b
+#define TARGET_SNDCTL_SEQ_NRSYNTHS 0x8004510a
+#define TARGET_SNDCTL_SEQ_OUTOFBAND 0x40085112
+#define TARGET_SNDCTL_SEQ_PANIC 0x00005111
+#define TARGET_SNDCTL_SEQ_PERCMODE 0x40045106
+#define TARGET_SNDCTL_SEQ_RESET 0x00005100
+#define TARGET_SNDCTL_SEQ_RESETSAMPLES 0x40045109
+#define TARGET_SNDCTL_SEQ_SYNC 0x00005101
+#define TARGET_SNDCTL_SEQ_TESTMIDI 0x40045108
+#define TARGET_SNDCTL_SEQ_THRESHOLD 0x4004510d
+#define TARGET_SNDCTL_SEQ_TRESHOLD 0x4004510d
+#define TARGET_SNDCTL_SYNTH_INFO 0xc08c5102
+#define TARGET_SNDCTL_SYNTH_MEMAVL 0xc004510e
+#define TARGET_SNDCTL_TMR_CONTINUE 0x00005404
+#define TARGET_SNDCTL_TMR_METRONOME 0x40045407
+#define TARGET_SNDCTL_TMR_SELECT 0x40045408
+#define TARGET_SNDCTL_TMR_SOURCE 0xc0045406
+#define TARGET_SNDCTL_TMR_START 0x00005402
+#define TARGET_SNDCTL_TMR_STOP 0x00005403
+#define TARGET_SNDCTL_TMR_TEMPO 0xc0045405
+#define TARGET_SNDCTL_TMR_TIMEBASE 0xc0045401
+#define TARGET_SOUND_PCM_READ_RATE 0x80045002
+#define TARGET_SOUND_PCM_READ_CHANNELS 0x80045006
+#define TARGET_SOUND_PCM_READ_BITS 0x80045005
+#define TARGET_SOUND_PCM_READ_FILTER 0x80045007
+#define TARGET_SOUND_MIXER_INFO TARGET_IOR ('M', 101, mixer_info)
+#define TARGET_SOUND_MIXER_ACCESS 0xc0804d66
+#define TARGET_SOUND_MIXER_PRIVATE1 TARGET_IOWR('M', 111, int)
+#define TARGET_SOUND_MIXER_PRIVATE2 TARGET_IOWR('M', 112, int)
+#define TARGET_SOUND_MIXER_PRIVATE3 TARGET_IOWR('M', 113, int)
+#define TARGET_SOUND_MIXER_PRIVATE4 TARGET_IOWR('M', 114, int)
+#define TARGET_SOUND_MIXER_PRIVATE5 TARGET_IOWR('M', 115, int)
+
+#define TARGET_MIXER_READ(dev) TARGET_IOR('M', dev, int)
+
+#define TARGET_SOUND_MIXER_READ_VOLUME TARGET_MIXER_READ(SOUND_MIXER_VOLUME)
+#define TARGET_SOUND_MIXER_READ_BASS TARGET_MIXER_READ(SOUND_MIXER_BASS)
+#define TARGET_SOUND_MIXER_READ_TREBLE TARGET_MIXER_READ(SOUND_MIXER_TREBLE)
+#define TARGET_SOUND_MIXER_READ_SYNTH TARGET_MIXER_READ(SOUND_MIXER_SYNTH)
+#define TARGET_SOUND_MIXER_READ_PCM TARGET_MIXER_READ(SOUND_MIXER_PCM)
+#define TARGET_SOUND_MIXER_READ_SPEAKER TARGET_MIXER_READ(SOUND_MIXER_SPEAKER)
+#define TARGET_SOUND_MIXER_READ_LINE TARGET_MIXER_READ(SOUND_MIXER_LINE)
+#define TARGET_SOUND_MIXER_READ_MIC TARGET_MIXER_READ(SOUND_MIXER_MIC)
+#define TARGET_SOUND_MIXER_READ_CD TARGET_MIXER_READ(SOUND_MIXER_CD)
+#define TARGET_SOUND_MIXER_READ_IMIX TARGET_MIXER_READ(SOUND_MIXER_IMIX)
+#define TARGET_SOUND_MIXER_READ_ALTPCM TARGET_MIXER_READ(SOUND_MIXER_ALTPCM)
+#define TARGET_SOUND_MIXER_READ_RECLEV TARGET_MIXER_READ(SOUND_MIXER_RECLEV)
+#define TARGET_SOUND_MIXER_READ_IGAIN TARGET_MIXER_READ(SOUND_MIXER_IGAIN)
+#define TARGET_SOUND_MIXER_READ_OGAIN TARGET_MIXER_READ(SOUND_MIXER_OGAIN)
+#define TARGET_SOUND_MIXER_READ_LINE1 TARGET_MIXER_READ(SOUND_MIXER_LINE1)
+#define TARGET_SOUND_MIXER_READ_LINE2 TARGET_MIXER_READ(SOUND_MIXER_LINE2)
+#define TARGET_SOUND_MIXER_READ_LINE3 TARGET_MIXER_READ(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define TARGET_SOUND_MIXER_READ_MUTE TARGET_MIXER_READ(SOUND_MIXER_MUTE)
+#define TARGET_SOUND_MIXER_READ_ENHANCE TARGET_MIXER_READ(SOUND_MIXER_ENHANCE)
+#define TARGET_SOUND_MIXER_READ_LOUD TARGET_MIXER_READ(SOUND_MIXER_LOUD)
+
+#define TARGET_SOUND_MIXER_READ_RECSRC TARGET_MIXER_READ(SOUND_MIXER_RECSRC)
+#define TARGET_SOUND_MIXER_READ_DEVMASK TARGET_MIXER_READ(SOUND_MIXER_DEVMASK)
+#define TARGET_SOUND_MIXER_READ_RECMASK TARGET_MIXER_READ(SOUND_MIXER_RECMASK)
+#define TARGET_SOUND_MIXER_READ_STEREODEVS TARGET_MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define TARGET_SOUND_MIXER_READ_CAPS TARGET_MIXER_READ(SOUND_MIXER_CAPS)
+
+#define TARGET_MIXER_WRITE(dev) TARGET_IOWR('M', dev, int)
+
+#define TARGET_SOUND_MIXER_WRITE_VOLUME TARGET_MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define TARGET_SOUND_MIXER_WRITE_BASS TARGET_MIXER_WRITE(SOUND_MIXER_BASS)
+#define TARGET_SOUND_MIXER_WRITE_TREBLE TARGET_MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define TARGET_SOUND_MIXER_WRITE_SYNTH TARGET_MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define TARGET_SOUND_MIXER_WRITE_PCM TARGET_MIXER_WRITE(SOUND_MIXER_PCM)
+#define TARGET_SOUND_MIXER_WRITE_SPEAKER TARGET_MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define TARGET_SOUND_MIXER_WRITE_LINE TARGET_MIXER_WRITE(SOUND_MIXER_LINE)
+#define TARGET_SOUND_MIXER_WRITE_MIC TARGET_MIXER_WRITE(SOUND_MIXER_MIC)
+#define TARGET_SOUND_MIXER_WRITE_CD TARGET_MIXER_WRITE(SOUND_MIXER_CD)
+#define TARGET_SOUND_MIXER_WRITE_IMIX TARGET_MIXER_WRITE(SOUND_MIXER_IMIX)
+#define TARGET_SOUND_MIXER_WRITE_ALTPCM TARGET_MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define TARGET_SOUND_MIXER_WRITE_RECLEV TARGET_MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define TARGET_SOUND_MIXER_WRITE_IGAIN TARGET_MIXER_WRITE(SOUND_MIXER_IGAIN)
+#define TARGET_SOUND_MIXER_WRITE_OGAIN TARGET_MIXER_WRITE(SOUND_MIXER_OGAIN)
+#define TARGET_SOUND_MIXER_WRITE_LINE1 TARGET_MIXER_WRITE(SOUND_MIXER_LINE1)
+#define TARGET_SOUND_MIXER_WRITE_LINE2 TARGET_MIXER_WRITE(SOUND_MIXER_LINE2)
+#define TARGET_SOUND_MIXER_WRITE_LINE3 TARGET_MIXER_WRITE(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define TARGET_SOUND_MIXER_WRITE_MUTE TARGET_MIXER_WRITE(SOUND_MIXER_MUTE)
+#define TARGET_SOUND_MIXER_WRITE_ENHANCE TARGET_MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define TARGET_SOUND_MIXER_WRITE_LOUD TARGET_MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define TARGET_SOUND_MIXER_WRITE_RECSRC TARGET_MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/* vfat ioctls */
+#define TARGET_VFAT_IOCTL_READDIR_BOTH TARGET_IORU('r', 1)
+#define TARGET_VFAT_IOCTL_READDIR_SHORT TARGET_IORU('r', 2)
+
+struct target_sysinfo {
+ target_long uptime; /* Seconds since boot */
+ target_ulong loads[3]; /* 1, 5, and 15 minute load averages */
+ target_ulong totalram; /* Total usable main memory size */
+ target_ulong freeram; /* Available memory size */
+ target_ulong sharedram; /* Amount of shared memory */
+ target_ulong bufferram; /* Memory used by buffers */
+ target_ulong totalswap; /* Total swap space size */
+ target_ulong freeswap; /* swap space still available */
+ unsigned short procs; /* Number of current processes */
+ unsigned short pad; /* explicit padding for m68k */
+ target_ulong totalhigh; /* Total high memory size */
+ target_ulong freehigh; /* Available high memory size */
+ unsigned int mem_unit; /* Memory unit size in bytes */
+ char _f[20-2*sizeof(target_long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+};
+
+#include "socket.h"
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
new file mode 100644
index 0000000..308da48
--- /dev/null
+++ b/linux-user/syscall_types.h
@@ -0,0 +1,81 @@
+STRUCT_SPECIAL(termios)
+
+STRUCT(winsize,
+ TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT)
+
+STRUCT(serial_multiport_struct,
+ TYPE_INT, TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_CHAR, TYPE_CHAR,
+ TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT,
+ MK_ARRAY(TYPE_INT, 32))
+
+STRUCT(serial_icounter_struct,
+ TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, MK_ARRAY(TYPE_INT, 16))
+
+STRUCT(sockaddr,
+ TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 14))
+
+STRUCT(rtentry,
+ TYPE_ULONG, MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr),
+ TYPE_SHORT, TYPE_SHORT, TYPE_ULONG, TYPE_PTRVOID, TYPE_SHORT, TYPE_PTRVOID,
+ TYPE_ULONG, TYPE_ULONG, TYPE_SHORT)
+
+STRUCT(ifmap,
+ TYPE_ULONG, TYPE_ULONG, TYPE_SHORT, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR,
+ /* Spare 3 bytes */
+ TYPE_CHAR, TYPE_CHAR, TYPE_CHAR)
+
+/* The *_ifreq_list arrays deal with the fact that struct ifreq has unions */
+
+STRUCT(sockaddr_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), MK_STRUCT(STRUCT_sockaddr))
+
+STRUCT(short_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_SHORT)
+
+STRUCT(int_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_INT)
+
+STRUCT(ifmap_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), MK_STRUCT(STRUCT_ifmap))
+
+STRUCT(char_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ),
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ))
+
+STRUCT(ptr_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_PTRVOID)
+
+STRUCT(ifconf,
+ TYPE_INT, TYPE_PTRVOID)
+
+STRUCT(arpreq,
+ MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), TYPE_INT, MK_STRUCT(STRUCT_sockaddr),
+ MK_ARRAY(TYPE_CHAR, 16))
+
+STRUCT(arpreq_old,
+ MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), TYPE_INT, MK_STRUCT(STRUCT_sockaddr))
+
+STRUCT(cdrom_read_audio,
+ TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_PTRVOID,
+ TYPE_NULL)
+
+STRUCT(hd_geometry,
+ TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG)
+
+STRUCT(dirent,
+ TYPE_LONG, TYPE_LONG, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 256))
+
+STRUCT(kbentry,
+ TYPE_CHAR, TYPE_CHAR, TYPE_SHORT)
+
+STRUCT(kbsentry,
+ TYPE_CHAR, MK_ARRAY(TYPE_CHAR, 512))
+
+STRUCT(audio_buf_info,
+ TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT)
+
+STRUCT(count_info,
+ TYPE_INT, TYPE_INT, TYPE_INT)
+
+STRUCT(mixer_info,
+ MK_ARRAY(TYPE_CHAR, 16), MK_ARRAY(TYPE_CHAR, 32), TYPE_INT, MK_ARRAY(TYPE_INT, 10))
diff --git a/linux-user/vm86.c b/linux-user/vm86.c
new file mode 100644
index 0000000..b28eea6
--- /dev/null
+++ b/linux-user/vm86.c
@@ -0,0 +1,482 @@
+/*
+ * vm86 linux syscall support
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu.h"
+
+//#define DEBUG_VM86
+
+#define set_flags(X,new,mask) \
+((X) = ((X) & ~(mask)) | ((new) & (mask)))
+
+#define SAFE_MASK (0xDD5)
+#define RETURN_MASK (0xDFF)
+
+static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
+{
+ return (((uint8_t *)bitmap)[nr >> 3] >> (nr & 7)) & 1;
+}
+
+static inline void vm_putw(uint8_t *segptr, unsigned int reg16, unsigned int val)
+{
+ stw(segptr + (reg16 & 0xffff), val);
+}
+
+static inline void vm_putl(uint8_t *segptr, unsigned int reg16, unsigned int val)
+{
+ stl(segptr + (reg16 & 0xffff), val);
+}
+
+static inline unsigned int vm_getw(uint8_t *segptr, unsigned int reg16)
+{
+ return lduw(segptr + (reg16 & 0xffff));
+}
+
+static inline unsigned int vm_getl(uint8_t *segptr, unsigned int reg16)
+{
+ return ldl(segptr + (reg16 & 0xffff));
+}
+
+void save_v86_state(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ struct target_vm86plus_struct * target_v86;
+
+ lock_user_struct(target_v86, ts->target_v86, 0);
+ /* put the VM86 registers in the userspace register structure */
+ target_v86->regs.eax = tswap32(env->regs[R_EAX]);
+ target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
+ target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
+ target_v86->regs.edx = tswap32(env->regs[R_EDX]);
+ target_v86->regs.esi = tswap32(env->regs[R_ESI]);
+ target_v86->regs.edi = tswap32(env->regs[R_EDI]);
+ target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
+ target_v86->regs.esp = tswap32(env->regs[R_ESP]);
+ target_v86->regs.eip = tswap32(env->eip);
+ target_v86->regs.cs = tswap16(env->segs[R_CS].selector);
+ target_v86->regs.ss = tswap16(env->segs[R_SS].selector);
+ target_v86->regs.ds = tswap16(env->segs[R_DS].selector);
+ target_v86->regs.es = tswap16(env->segs[R_ES].selector);
+ target_v86->regs.fs = tswap16(env->segs[R_FS].selector);
+ target_v86->regs.gs = tswap16(env->segs[R_GS].selector);
+ set_flags(env->eflags, ts->v86flags, VIF_MASK | ts->v86mask);
+ target_v86->regs.eflags = tswap32(env->eflags);
+ unlock_user_struct(target_v86, ts->target_v86, 1);
+#ifdef DEBUG_VM86
+ fprintf(logfile, "save_v86_state: eflags=%08x cs:ip=%04x:%04x\n",
+ env->eflags, env->segs[R_CS].selector, env->eip);
+#endif
+
+ /* restore 32 bit registers */
+ env->regs[R_EAX] = ts->vm86_saved_regs.eax;
+ env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
+ env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
+ env->regs[R_EDX] = ts->vm86_saved_regs.edx;
+ env->regs[R_ESI] = ts->vm86_saved_regs.esi;
+ env->regs[R_EDI] = ts->vm86_saved_regs.edi;
+ env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
+ env->regs[R_ESP] = ts->vm86_saved_regs.esp;
+ env->eflags = ts->vm86_saved_regs.eflags;
+ env->eip = ts->vm86_saved_regs.eip;
+
+ cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
+ cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
+ cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
+ cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
+ cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
+ cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
+}
+
+/* return from vm86 mode to 32 bit. The vm86() syscall will return
+ 'retval' */
+static inline void return_to_32bit(CPUX86State *env, int retval)
+{
+#ifdef DEBUG_VM86
+ fprintf(logfile, "return_to_32bit: ret=0x%x\n", retval);
+#endif
+ save_v86_state(env);
+ env->regs[R_EAX] = retval;
+}
+
+static inline int set_IF(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ ts->v86flags |= VIF_MASK;
+ if (ts->v86flags & VIP_MASK) {
+ return_to_32bit(env, TARGET_VM86_STI);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void clear_IF(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ ts->v86flags &= ~VIF_MASK;
+}
+
+static inline void clear_TF(CPUX86State *env)
+{
+ env->eflags &= ~TF_MASK;
+}
+
+static inline void clear_AC(CPUX86State *env)
+{
+ env->eflags &= ~AC_MASK;
+}
+
+static inline int set_vflags_long(unsigned long eflags, CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ set_flags(ts->v86flags, eflags, ts->v86mask);
+ set_flags(env->eflags, eflags, SAFE_MASK);
+ if (eflags & IF_MASK)
+ return set_IF(env);
+ else
+ clear_IF(env);
+ return 0;
+}
+
+static inline int set_vflags_short(unsigned short flags, CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ set_flags(ts->v86flags, flags, ts->v86mask & 0xffff);
+ set_flags(env->eflags, flags, SAFE_MASK);
+ if (flags & IF_MASK)
+ return set_IF(env);
+ else
+ clear_IF(env);
+ return 0;
+}
+
+static inline unsigned int get_vflags(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ unsigned int flags;
+
+ flags = env->eflags & RETURN_MASK;
+ if (ts->v86flags & VIF_MASK)
+ flags |= IF_MASK;
+ flags |= IOPL_MASK;
+ return flags | (ts->v86flags & ts->v86mask);
+}
+
+#define ADD16(reg, val) reg = (reg & ~0xffff) | ((reg + (val)) & 0xffff)
+
+/* handle VM86 interrupt (NOTE: the CPU core currently does not
+ support TSS interrupt revectoring, so this code is always executed) */
+static void do_int(CPUX86State *env, int intno)
+{
+ TaskState *ts = env->opaque;
+ uint32_t *int_ptr, segoffs;
+ uint8_t *ssp;
+ unsigned int sp;
+
+ if (env->segs[R_CS].selector == TARGET_BIOSSEG)
+ goto cannot_handle;
+ if (is_revectored(intno, &ts->vm86plus.int_revectored))
+ goto cannot_handle;
+ if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff,
+ &ts->vm86plus.int21_revectored))
+ goto cannot_handle;
+ int_ptr = (uint32_t *)(intno << 2);
+ segoffs = tswap32(*int_ptr);
+ if ((segoffs >> 16) == TARGET_BIOSSEG)
+ goto cannot_handle;
+#if defined(DEBUG_VM86)
+ fprintf(logfile, "VM86: emulating int 0x%x. CS:IP=%04x:%04x\n",
+ intno, segoffs >> 16, segoffs & 0xffff);
+#endif
+ /* save old state */
+ ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
+ sp = env->regs[R_ESP] & 0xffff;
+ vm_putw(ssp, sp - 2, get_vflags(env));
+ vm_putw(ssp, sp - 4, env->segs[R_CS].selector);
+ vm_putw(ssp, sp - 6, env->eip);
+ ADD16(env->regs[R_ESP], -6);
+ /* goto interrupt handler */
+ env->eip = segoffs & 0xffff;
+ cpu_x86_load_seg(env, R_CS, segoffs >> 16);
+ clear_TF(env);
+ clear_IF(env);
+ clear_AC(env);
+ return;
+ cannot_handle:
+#if defined(DEBUG_VM86)
+ fprintf(logfile, "VM86: return to 32 bits int 0x%x\n", intno);
+#endif
+ return_to_32bit(env, TARGET_VM86_INTx | (intno << 8));
+}
+
+void handle_vm86_trap(CPUX86State *env, int trapno)
+{
+ if (trapno == 1 || trapno == 3) {
+ return_to_32bit(env, TARGET_VM86_TRAP + (trapno << 8));
+ } else {
+ do_int(env, trapno);
+ }
+}
+
+#define CHECK_IF_IN_TRAP() \
+ if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \
+ (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \
+ newflags |= TF_MASK
+
+#define VM86_FAULT_RETURN \
+ if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \
+ (ts->v86flags & (IF_MASK | VIF_MASK))) \
+ return_to_32bit(env, TARGET_VM86_PICRETURN); \
+ return
+
+void handle_vm86_fault(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ uint8_t *csp, *pc, *ssp;
+ unsigned int ip, sp, newflags, newip, newcs, opcode, intno;
+ int data32, pref_done;
+
+ csp = (uint8_t *)(env->segs[R_CS].selector << 4);
+ ip = env->eip & 0xffff;
+ pc = csp + ip;
+
+ ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
+ sp = env->regs[R_ESP] & 0xffff;
+
+#if defined(DEBUG_VM86)
+ fprintf(logfile, "VM86 exception %04x:%08x %02x %02x\n",
+ env->segs[R_CS].selector, env->eip, pc[0], pc[1]);
+#endif
+
+ data32 = 0;
+ pref_done = 0;
+ do {
+ opcode = csp[ip];
+ ADD16(ip, 1);
+ switch (opcode) {
+ case 0x66: /* 32-bit data */ data32=1; break;
+ case 0x67: /* 32-bit address */ break;
+ case 0x2e: /* CS */ break;
+ case 0x3e: /* DS */ break;
+ case 0x26: /* ES */ break;
+ case 0x36: /* SS */ break;
+ case 0x65: /* GS */ break;
+ case 0x64: /* FS */ break;
+ case 0xf2: /* repnz */ break;
+ case 0xf3: /* rep */ break;
+ default: pref_done = 1;
+ }
+ } while (!pref_done);
+
+ /* VM86 mode */
+ switch(opcode) {
+ case 0x9c: /* pushf */
+ if (data32) {
+ vm_putl(ssp, sp - 4, get_vflags(env));
+ ADD16(env->regs[R_ESP], -4);
+ } else {
+ vm_putw(ssp, sp - 2, get_vflags(env));
+ ADD16(env->regs[R_ESP], -2);
+ }
+ env->eip = ip;
+ VM86_FAULT_RETURN;
+
+ case 0x9d: /* popf */
+ if (data32) {
+ newflags = vm_getl(ssp, sp);
+ ADD16(env->regs[R_ESP], 4);
+ } else {
+ newflags = vm_getw(ssp, sp);
+ ADD16(env->regs[R_ESP], 2);
+ }
+ env->eip = ip;
+ CHECK_IF_IN_TRAP();
+ if (data32) {
+ if (set_vflags_long(newflags, env))
+ return;
+ } else {
+ if (set_vflags_short(newflags, env))
+ return;
+ }
+ VM86_FAULT_RETURN;
+
+ case 0xcd: /* int */
+ intno = csp[ip];
+ ADD16(ip, 1);
+ env->eip = ip;
+ if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) {
+ if ( (ts->vm86plus.vm86plus.vm86dbg_intxxtab[intno >> 3] >>
+ (intno &7)) & 1) {
+ return_to_32bit(env, TARGET_VM86_INTx + (intno << 8));
+ return;
+ }
+ }
+ do_int(env, intno);
+ break;
+
+ case 0xcf: /* iret */
+ if (data32) {
+ newip = vm_getl(ssp, sp) & 0xffff;
+ newcs = vm_getl(ssp, sp + 4) & 0xffff;
+ newflags = vm_getl(ssp, sp + 8);
+ ADD16(env->regs[R_ESP], 12);
+ } else {
+ newip = vm_getw(ssp, sp);
+ newcs = vm_getw(ssp, sp + 2);
+ newflags = vm_getw(ssp, sp + 4);
+ ADD16(env->regs[R_ESP], 6);
+ }
+ env->eip = newip;
+ cpu_x86_load_seg(env, R_CS, newcs);
+ CHECK_IF_IN_TRAP();
+ if (data32) {
+ if (set_vflags_long(newflags, env))
+ return;
+ } else {
+ if (set_vflags_short(newflags, env))
+ return;
+ }
+ VM86_FAULT_RETURN;
+
+ case 0xfa: /* cli */
+ env->eip = ip;
+ clear_IF(env);
+ VM86_FAULT_RETURN;
+
+ case 0xfb: /* sti */
+ env->eip = ip;
+ if (set_IF(env))
+ return;
+ VM86_FAULT_RETURN;
+
+ default:
+ /* real VM86 GPF exception */
+ return_to_32bit(env, TARGET_VM86_UNKNOWN);
+ break;
+ }
+}
+
+int do_vm86(CPUX86State *env, long subfunction, target_ulong vm86_addr)
+{
+ TaskState *ts = env->opaque;
+ struct target_vm86plus_struct * target_v86;
+ int ret;
+
+ switch (subfunction) {
+ case TARGET_VM86_REQUEST_IRQ:
+ case TARGET_VM86_FREE_IRQ:
+ case TARGET_VM86_GET_IRQ_BITS:
+ case TARGET_VM86_GET_AND_RESET_IRQ:
+ gemu_log("qemu: unsupported vm86 subfunction (%ld)\n", subfunction);
+ ret = -EINVAL;
+ goto out;
+ case TARGET_VM86_PLUS_INSTALL_CHECK:
+ /* NOTE: on old vm86 stuff this will return the error
+ from verify_area(), because the subfunction is
+ interpreted as (invalid) address to vm86_struct.
+ So the installation check works.
+ */
+ ret = 0;
+ goto out;
+ }
+
+ /* save current CPU regs */
+ ts->vm86_saved_regs.eax = 0; /* default vm86 syscall return code */
+ ts->vm86_saved_regs.ebx = env->regs[R_EBX];
+ ts->vm86_saved_regs.ecx = env->regs[R_ECX];
+ ts->vm86_saved_regs.edx = env->regs[R_EDX];
+ ts->vm86_saved_regs.esi = env->regs[R_ESI];
+ ts->vm86_saved_regs.edi = env->regs[R_EDI];
+ ts->vm86_saved_regs.ebp = env->regs[R_EBP];
+ ts->vm86_saved_regs.esp = env->regs[R_ESP];
+ ts->vm86_saved_regs.eflags = env->eflags;
+ ts->vm86_saved_regs.eip = env->eip;
+ ts->vm86_saved_regs.cs = env->segs[R_CS].selector;
+ ts->vm86_saved_regs.ss = env->segs[R_SS].selector;
+ ts->vm86_saved_regs.ds = env->segs[R_DS].selector;
+ ts->vm86_saved_regs.es = env->segs[R_ES].selector;
+ ts->vm86_saved_regs.fs = env->segs[R_FS].selector;
+ ts->vm86_saved_regs.gs = env->segs[R_GS].selector;
+
+ ts->target_v86 = vm86_addr;
+ lock_user_struct(target_v86, vm86_addr, 1);
+ /* build vm86 CPU state */
+ ts->v86flags = tswap32(target_v86->regs.eflags);
+ env->eflags = (env->eflags & ~SAFE_MASK) |
+ (tswap32(target_v86->regs.eflags) & SAFE_MASK) | VM_MASK;
+
+ ts->vm86plus.cpu_type = tswapl(target_v86->cpu_type);
+ switch (ts->vm86plus.cpu_type) {
+ case TARGET_CPU_286:
+ ts->v86mask = 0;
+ break;
+ case TARGET_CPU_386:
+ ts->v86mask = NT_MASK | IOPL_MASK;
+ break;
+ case TARGET_CPU_486:
+ ts->v86mask = AC_MASK | NT_MASK | IOPL_MASK;
+ break;
+ default:
+ ts->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
+ break;
+ }
+
+ env->regs[R_EBX] = tswap32(target_v86->regs.ebx);
+ env->regs[R_ECX] = tswap32(target_v86->regs.ecx);
+ env->regs[R_EDX] = tswap32(target_v86->regs.edx);
+ env->regs[R_ESI] = tswap32(target_v86->regs.esi);
+ env->regs[R_EDI] = tswap32(target_v86->regs.edi);
+ env->regs[R_EBP] = tswap32(target_v86->regs.ebp);
+ env->regs[R_ESP] = tswap32(target_v86->regs.esp);
+ env->eip = tswap32(target_v86->regs.eip);
+ cpu_x86_load_seg(env, R_CS, tswap16(target_v86->regs.cs));
+ cpu_x86_load_seg(env, R_SS, tswap16(target_v86->regs.ss));
+ cpu_x86_load_seg(env, R_DS, tswap16(target_v86->regs.ds));
+ cpu_x86_load_seg(env, R_ES, tswap16(target_v86->regs.es));
+ cpu_x86_load_seg(env, R_FS, tswap16(target_v86->regs.fs));
+ cpu_x86_load_seg(env, R_GS, tswap16(target_v86->regs.gs));
+ ret = tswap32(target_v86->regs.eax); /* eax will be restored at
+ the end of the syscall */
+ memcpy(&ts->vm86plus.int_revectored,
+ &target_v86->int_revectored, 32);
+ memcpy(&ts->vm86plus.int21_revectored,
+ &target_v86->int21_revectored, 32);
+ ts->vm86plus.vm86plus.flags = tswapl(target_v86->vm86plus.flags);
+ memcpy(&ts->vm86plus.vm86plus.vm86dbg_intxxtab,
+ target_v86->vm86plus.vm86dbg_intxxtab, 32);
+ unlock_user_struct(target_v86, vm86_addr, 0);
+
+#ifdef DEBUG_VM86
+ fprintf(logfile, "do_vm86: cs:ip=%04x:%04x\n",
+ env->segs[R_CS].selector, env->eip);
+#endif
+ /* now the virtual CPU is ready for vm86 execution ! */
+ out:
+ return ret;
+}
+
diff --git a/loader.c b/loader.c
new file mode 100644
index 0000000..3fa681d
--- /dev/null
+++ b/loader.c
@@ -0,0 +1,235 @@
+/*
+ * QEMU Executable loader
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "disas.h"
+
+/* return the size or -1 if error */
+int get_image_size(const char *filename)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ close(fd);
+ return size;
+}
+
+/* return the size or -1 if error */
+int load_image(const char *filename, uint8_t *addr)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, addr, size) != size) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return size;
+}
+
+/* A.OUT loader */
+
+struct exec
+{
+ uint32_t a_info; /* Use macros N_MAGIC, etc for access */
+ uint32_t a_text; /* length of text, in bytes */
+ uint32_t a_data; /* length of data, in bytes */
+ uint32_t a_bss; /* length of uninitialized data area, in bytes */
+ uint32_t a_syms; /* length of symbol table data in file, in bytes */
+ uint32_t a_entry; /* start address */
+ uint32_t a_trsize; /* length of relocation info for text, in bytes */
+ uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#ifdef BSWAP_NEEDED
+static void bswap_ahdr(struct exec *e)
+{
+ bswap32s(&e->a_info);
+ bswap32s(&e->a_text);
+ bswap32s(&e->a_data);
+ bswap32s(&e->a_bss);
+ bswap32s(&e->a_syms);
+ bswap32s(&e->a_entry);
+ bswap32s(&e->a_trsize);
+ bswap32s(&e->a_drsize);
+}
+#else
+#define bswap_ahdr(x) do { } while (0)
+#endif
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
+ (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0)
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+ : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+
+
+int load_aout(const char *filename, uint8_t *addr)
+{
+ int fd, size, ret;
+ struct exec e;
+ uint32_t magic;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ size = read(fd, &e, sizeof(e));
+ if (size < 0)
+ goto fail;
+
+ bswap_ahdr(&e);
+
+ magic = N_MAGIC(e);
+ switch (magic) {
+ case ZMAGIC:
+ case QMAGIC:
+ case OMAGIC:
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read(fd, addr, e.a_text + e.a_data);
+ if (size < 0)
+ goto fail;
+ break;
+ case NMAGIC:
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read(fd, addr, e.a_text);
+ if (size < 0)
+ goto fail;
+ ret = read(fd, addr + N_DATADDR(e), e.a_data);
+ if (ret < 0)
+ goto fail;
+ size += ret;
+ break;
+ default:
+ goto fail;
+ }
+ close(fd);
+ return size;
+ fail:
+ close(fd);
+ return -1;
+}
+
+/* ELF loader */
+
+static void *load_at(int fd, int offset, int size)
+{
+ void *ptr;
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ return NULL;
+ ptr = qemu_malloc(size);
+ if (!ptr)
+ return NULL;
+ if (read(fd, ptr, size) != size) {
+ qemu_free(ptr);
+ return NULL;
+ }
+ return ptr;
+}
+
+
+#define ELF_CLASS ELFCLASS32
+#include "elf.h"
+
+#define SZ 32
+#define elf_word uint32_t
+#define bswapSZs bswap32s
+#include "elf_ops.h"
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_shdr
+#undef elf_sym
+#undef elf_note
+#undef elf_word
+#undef bswapSZs
+#undef SZ
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
+#define elf_word uint64_t
+#define bswapSZs bswap64s
+#define SZ 64
+#include "elf_ops.h"
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, int64_t virt_to_phys_addend,
+ uint64_t *pentry)
+{
+ int fd, data_order, must_swab, ret;
+ uint8_t e_ident[EI_NIDENT];
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ perror(filename);
+ return -1;
+ }
+ if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
+ goto fail;
+ if (e_ident[0] != ELFMAG0 ||
+ e_ident[1] != ELFMAG1 ||
+ e_ident[2] != ELFMAG2 ||
+ e_ident[3] != ELFMAG3)
+ goto fail;
+#ifdef WORDS_BIGENDIAN
+ data_order = ELFDATA2MSB;
+#else
+ data_order = ELFDATA2LSB;
+#endif
+ must_swab = data_order != e_ident[EI_DATA];
+
+ lseek(fd, 0, SEEK_SET);
+ if (e_ident[EI_CLASS] == ELFCLASS64) {
+ ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry);
+ } else {
+ ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry);
+ }
+
+ close(fd);
+ return ret;
+
+ fail:
+ close(fd);
+ return -1;
+}
diff --git a/m68k-dis.c b/m68k-dis.c
new file mode 100644
index 0000000..dd19558
--- /dev/null
+++ b/m68k-dis.c
@@ -0,0 +1,5051 @@
+/* This file is composed of several different files from the upstream
+ sourceware.org CVS. Original file boundaries marked with **** */
+
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "dis-asm.h"
+
+/* **** foatformat.h from sourceware.org CVS 2005-08-14. */
+/* IEEE floating point support declarations, for GDB, the GNU Debugger.
+ Copyright 1991, 1994, 1995, 1997, 2000, 2003 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#if !defined (FLOATFORMAT_H)
+#define FLOATFORMAT_H 1
+
+/*#include "ansidecl.h" */
+
+/* A floatformat consists of a sign bit, an exponent and a mantissa. Once the
+ bytes are concatenated according to the byteorder flag, then each of those
+ fields is contiguous. We number the bits with 0 being the most significant
+ (i.e. BITS_BIG_ENDIAN type numbering), and specify which bits each field
+ contains with the *_start and *_len fields. */
+
+/* What is the order of the bytes. */
+
+enum floatformat_byteorders {
+
+ /* Standard little endian byte order.
+ EX: 1.2345678e10 => 00 00 80 c5 e0 fe 06 42 */
+
+ floatformat_little,
+
+ /* Standard big endian byte order.
+ EX: 1.2345678e10 => 42 06 fe e0 c5 80 00 00 */
+
+ floatformat_big,
+
+ /* Little endian byte order but big endian word order.
+ EX: 1.2345678e10 => e0 fe 06 42 00 00 80 c5 */
+
+ floatformat_littlebyte_bigword
+
+};
+
+enum floatformat_intbit { floatformat_intbit_yes, floatformat_intbit_no };
+
+struct floatformat
+{
+ enum floatformat_byteorders byteorder;
+ unsigned int totalsize; /* Total size of number in bits */
+
+ /* Sign bit is always one bit long. 1 means negative, 0 means positive. */
+ unsigned int sign_start;
+
+ unsigned int exp_start;
+ unsigned int exp_len;
+ /* Bias added to a "true" exponent to form the biased exponent. It
+ is intentionally signed as, otherwize, -exp_bias can turn into a
+ very large number (e.g., given the exp_bias of 0x3fff and a 64
+ bit long, the equation (long)(1 - exp_bias) evaluates to
+ 4294950914) instead of -16382). */
+ int exp_bias;
+ /* Exponent value which indicates NaN. This is the actual value stored in
+ the float, not adjusted by the exp_bias. This usually consists of all
+ one bits. */
+ unsigned int exp_nan;
+
+ unsigned int man_start;
+ unsigned int man_len;
+
+ /* Is the integer bit explicit or implicit? */
+ enum floatformat_intbit intbit;
+
+ /* Internal name for debugging. */
+ const char *name;
+
+ /* Validator method. */
+ int (*is_valid) (const struct floatformat *fmt, const char *from);
+};
+
+/* floatformats for IEEE single and double, big and little endian. */
+
+extern const struct floatformat floatformat_ieee_single_big;
+extern const struct floatformat floatformat_ieee_single_little;
+extern const struct floatformat floatformat_ieee_double_big;
+extern const struct floatformat floatformat_ieee_double_little;
+
+/* floatformat for ARM IEEE double, little endian bytes and big endian words */
+
+extern const struct floatformat floatformat_ieee_double_littlebyte_bigword;
+
+/* floatformats for various extendeds. */
+
+extern const struct floatformat floatformat_i387_ext;
+extern const struct floatformat floatformat_m68881_ext;
+extern const struct floatformat floatformat_i960_ext;
+extern const struct floatformat floatformat_m88110_ext;
+extern const struct floatformat floatformat_m88110_harris_ext;
+extern const struct floatformat floatformat_arm_ext_big;
+extern const struct floatformat floatformat_arm_ext_littlebyte_bigword;
+/* IA-64 Floating Point register spilt into memory. */
+extern const struct floatformat floatformat_ia64_spill_big;
+extern const struct floatformat floatformat_ia64_spill_little;
+extern const struct floatformat floatformat_ia64_quad_big;
+extern const struct floatformat floatformat_ia64_quad_little;
+
+/* Convert from FMT to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+extern void
+floatformat_to_double (const struct floatformat *, const char *, double *);
+
+/* The converse: convert the double *FROM to FMT
+ and store where TO points. */
+
+extern void
+floatformat_from_double (const struct floatformat *, const double *, char *);
+
+/* Return non-zero iff the data at FROM is a valid number in format FMT. */
+
+extern int
+floatformat_is_valid (const struct floatformat *fmt, const char *from);
+
+#endif /* defined (FLOATFORMAT_H) */
+/* **** End of floatformat.h */
+/* **** m68k-dis.h from sourceware.org CVS 2005-08-14. */
+/* Opcode table header for m680[01234]0/m6888[12]/m68851.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2001,
+ 2003, 2004 Free Software Foundation, Inc.
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 1, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ 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 file; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* These are used as bit flags for the arch field in the m68k_opcode
+ structure. */
+#define _m68k_undef 0
+#define m68000 0x001
+#define m68008 m68000 /* Synonym for -m68000. otherwise unused. */
+#define m68010 0x002
+#define m68020 0x004
+#define m68030 0x008
+#define m68ec030 m68030 /* Similar enough to -m68030 to ignore differences;
+ gas will deal with the few differences. */
+#define m68040 0x010
+/* There is no 68050. */
+#define m68060 0x020
+#define m68881 0x040
+#define m68882 m68881 /* Synonym for -m68881. otherwise unused. */
+#define m68851 0x080
+#define cpu32 0x100 /* e.g., 68332 */
+
+#define mcfmac 0x200 /* ColdFire MAC. */
+#define mcfemac 0x400 /* ColdFire EMAC. */
+#define cfloat 0x800 /* ColdFire FPU. */
+#define mcfhwdiv 0x1000 /* ColdFire hardware divide. */
+
+#define mcfisa_a 0x2000 /* ColdFire ISA_A. */
+#define mcfisa_aa 0x4000 /* ColdFire ISA_A+. */
+#define mcfisa_b 0x8000 /* ColdFire ISA_B. */
+#define mcfusp 0x10000 /* ColdFire USP instructions. */
+
+#define mcf5200 0x20000
+#define mcf5206e 0x40000
+#define mcf521x 0x80000
+#define mcf5249 0x100000
+#define mcf528x 0x200000
+#define mcf5307 0x400000
+#define mcf5407 0x800000
+#define mcf5470 0x1000000
+#define mcf5480 0x2000000
+
+ /* Handy aliases. */
+#define m68040up (m68040 | m68060)
+#define m68030up (m68030 | m68040up)
+#define m68020up (m68020 | m68030up)
+#define m68010up (m68010 | cpu32 | m68020up)
+#define m68000up (m68000 | m68010up)
+
+#define mfloat (m68881 | m68882 | m68040 | m68060)
+#define mmmu (m68851 | m68030 | m68040 | m68060)
+
+/* The structure used to hold information for an opcode. */
+
+struct m68k_opcode
+{
+ /* The opcode name. */
+ const char *name;
+ /* The pseudo-size of the instruction(in bytes). Used to determine
+ number of bytes necessary to disassemble the instruction. */
+ unsigned int size;
+ /* The opcode itself. */
+ unsigned long opcode;
+ /* The mask used by the disassembler. */
+ unsigned long match;
+ /* The arguments. */
+ const char *args;
+ /* The architectures which support this opcode. */
+ unsigned int arch;
+};
+
+/* The structure used to hold information for an opcode alias. */
+
+struct m68k_opcode_alias
+{
+ /* The alias name. */
+ const char *alias;
+ /* The instruction for which this is an alias. */
+ const char *primary;
+};
+
+/* We store four bytes of opcode for all opcodes because that is the
+ most any of them need. The actual length of an instruction is
+ always at least 2 bytes, and is as much longer as necessary to hold
+ the operands it has.
+
+ The match field is a mask saying which bits must match particular
+ opcode in order for an instruction to be an instance of that
+ opcode.
+
+ The args field is a string containing two characters for each
+ operand of the instruction. The first specifies the kind of
+ operand; the second, the place it is stored. */
+
+/* Kinds of operands:
+ Characters used: AaBbCcDdEeFfGgHIiJkLlMmnOopQqRrSsTtU VvWwXxYyZz01234|*~%;@!&$?/<>#^+-
+
+ D data register only. Stored as 3 bits.
+ A address register only. Stored as 3 bits.
+ a address register indirect only. Stored as 3 bits.
+ R either kind of register. Stored as 4 bits.
+ r either kind of register indirect only. Stored as 4 bits.
+ At the moment, used only for cas2 instruction.
+ F floating point coprocessor register only. Stored as 3 bits.
+ O an offset (or width): immediate data 0-31 or data register.
+ Stored as 6 bits in special format for BF... insns.
+ + autoincrement only. Stored as 3 bits (number of the address register).
+ - autodecrement only. Stored as 3 bits (number of the address register).
+ Q quick immediate data. Stored as 3 bits.
+ This matches an immediate operand only when value is in range 1 .. 8.
+ M moveq immediate data. Stored as 8 bits.
+ This matches an immediate operand only when value is in range -128..127
+ T trap vector immediate data. Stored as 4 bits.
+
+ k K-factor for fmove.p instruction. Stored as a 7-bit constant or
+ a three bit register offset, depending on the field type.
+
+ # immediate data. Stored in special places (b, w or l)
+ which say how many bits to store.
+ ^ immediate data for floating point instructions. Special places
+ are offset by 2 bytes from '#'...
+ B pc-relative address, converted to an offset
+ that is treated as immediate data.
+ d displacement and register. Stores the register as 3 bits
+ and stores the displacement in the entire second word.
+
+ C the CCR. No need to store it; this is just for filtering validity.
+ S the SR. No need to store, just as with CCR.
+ U the USP. No need to store, just as with CCR.
+ E the MAC ACC. No need to store, just as with CCR.
+ e the EMAC ACC[0123].
+ G the MAC/EMAC MACSR. No need to store, just as with CCR.
+ g the EMAC ACCEXT{01,23}.
+ H the MASK. No need to store, just as with CCR.
+ i the MAC/EMAC scale factor.
+
+ I Coprocessor ID. Not printed if 1. The Coprocessor ID is always
+ extracted from the 'd' field of word one, which means that an extended
+ coprocessor opcode can be skipped using the 'i' place, if needed.
+
+ s System Control register for the floating point coprocessor.
+
+ J Misc register for movec instruction, stored in 'j' format.
+ Possible values:
+ 0x000 SFC Source Function Code reg [60, 40, 30, 20, 10]
+ 0x001 DFC Data Function Code reg [60, 40, 30, 20, 10]
+ 0x002 CACR Cache Control Register [60, 40, 30, 20, mcf]
+ 0x003 TC MMU Translation Control [60, 40]
+ 0x004 ITT0 Instruction Transparent
+ Translation reg 0 [60, 40]
+ 0x005 ITT1 Instruction Transparent
+ Translation reg 1 [60, 40]
+ 0x006 DTT0 Data Transparent
+ Translation reg 0 [60, 40]
+ 0x007 DTT1 Data Transparent
+ Translation reg 1 [60, 40]
+ 0x008 BUSCR Bus Control Register [60]
+ 0x800 USP User Stack Pointer [60, 40, 30, 20, 10]
+ 0x801 VBR Vector Base reg [60, 40, 30, 20, 10, mcf]
+ 0x802 CAAR Cache Address Register [ 30, 20]
+ 0x803 MSP Master Stack Pointer [ 40, 30, 20]
+ 0x804 ISP Interrupt Stack Pointer [ 40, 30, 20]
+ 0x805 MMUSR MMU Status reg [ 40]
+ 0x806 URP User Root Pointer [60, 40]
+ 0x807 SRP Supervisor Root Pointer [60, 40]
+ 0x808 PCR Processor Configuration reg [60]
+ 0xC00 ROMBAR ROM Base Address Register [520X]
+ 0xC04 RAMBAR0 RAM Base Address Register 0 [520X]
+ 0xC05 RAMBAR1 RAM Base Address Register 0 [520X]
+ 0xC0F MBAR0 RAM Base Address Register 0 [520X]
+ 0xC04 FLASHBAR FLASH Base Address Register [mcf528x]
+ 0xC05 RAMBAR Static RAM Base Address Register [mcf528x]
+
+ L Register list of the type d0-d7/a0-a7 etc.
+ (New! Improved! Can also hold fp0-fp7, as well!)
+ The assembler tries to see if the registers match the insn by
+ looking at where the insn wants them stored.
+
+ l Register list like L, but with all the bits reversed.
+ Used for going the other way. . .
+
+ c cache identifier which may be "nc" for no cache, "ic"
+ for instruction cache, "dc" for data cache, or "bc"
+ for both caches. Used in cinv and cpush. Always
+ stored in position "d".
+
+ u Any register, with ``upper'' or ``lower'' specification. Used
+ in the mac instructions with size word.
+
+ The remainder are all stored as 6 bits using an address mode and a
+ register number; they differ in which addressing modes they match.
+
+ * all (modes 0-6,7.0-4)
+ ~ alterable memory (modes 2-6,7.0,7.1)
+ (not 0,1,7.2-4)
+ % alterable (modes 0-6,7.0,7.1)
+ (not 7.2-4)
+ ; data (modes 0,2-6,7.0-4)
+ (not 1)
+ @ data, but not immediate (modes 0,2-6,7.0-3)
+ (not 1,7.4)
+ ! control (modes 2,5,6,7.0-3)
+ (not 0,1,3,4,7.4)
+ & alterable control (modes 2,5,6,7.0,7.1)
+ (not 0,1,3,4,7.2-4)
+ $ alterable data (modes 0,2-6,7.0,7.1)
+ (not 1,7.2-4)
+ ? alterable control, or data register (modes 0,2,5,6,7.0,7.1)
+ (not 1,3,4,7.2-4)
+ / control, or data register (modes 0,2,5,6,7.0-3)
+ (not 1,3,4,7.4)
+ > *save operands (modes 2,4,5,6,7.0,7.1)
+ (not 0,1,3,7.2-4)
+ < *restore operands (modes 2,3,5,6,7.0-3)
+ (not 0,1,4,7.4)
+
+ coldfire move operands:
+ m (modes 0-4)
+ n (modes 5,7.2)
+ o (modes 6,7.0,7.1,7.3,7.4)
+ p (modes 0-5)
+
+ coldfire bset/bclr/btst/mulsl/mulul operands:
+ q (modes 0,2-5)
+ v (modes 0,2-5,7.0,7.1)
+ b (modes 0,2-5,7.2)
+ w (modes 2-5,7.2)
+ y (modes 2,5)
+ z (modes 2,5,7.2)
+ x mov3q immediate operand.
+ 4 (modes 2,3,4,5)
+ */
+
+/* For the 68851: */
+/* I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ 0 32 bit pmmu register
+ Possible values:
+ 000 TC Translation Control Register (68030, 68851)
+
+ 1 16 bit pmmu register
+ 111 AC Access Control (68851)
+
+ 2 8 bit pmmu register
+ 100 CAL Current Access Level (68851)
+ 101 VAL Validate Access Level (68851)
+ 110 SCC Stack Change Control (68851)
+
+ 3 68030-only pmmu registers (32 bit)
+ 010 TT0 Transparent Translation reg 0
+ (aka Access Control reg 0 -- AC0 -- on 68ec030)
+ 011 TT1 Transparent Translation reg 1
+ (aka Access Control reg 1 -- AC1 -- on 68ec030)
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer (68851)
+ 010 SRP Supervisor Root Pointer (68030, 68851)
+ 011 CRP Cpu Root Pointer (68030, 68851)
+
+ f function code register (68030, 68851)
+ 0 SFC
+ 1 DFC
+
+ V VAL register only (68851)
+
+ X BADx, BACx (16 bit)
+ 100 BAD Breakpoint Acknowledge Data (68851)
+ 101 BAC Breakpoint Acknowledge Control (68851)
+
+ Y PSR (68851) (MMUSR on 68030) (ACUSR on 68ec030)
+ Z PCSR (68851)
+
+ | memory (modes 2-6, 7.*)
+
+ t address test level (68030 only)
+ Stored as 3 bits, range 0-7.
+ Also used for breakpoint instruction now.
+
+*/
+
+/* Places to put an operand, for non-general operands:
+ Characters used: BbCcDdFfGgHhIijkLlMmNnostWw123456789/
+
+ s source, low bits of first word.
+ d dest, shifted 9 in first word
+ 1 second word, shifted 12
+ 2 second word, shifted 6
+ 3 second word, shifted 0
+ 4 third word, shifted 12
+ 5 third word, shifted 6
+ 6 third word, shifted 0
+ 7 second word, shifted 7
+ 8 second word, shifted 10
+ 9 second word, shifted 5
+ D store in both place 1 and place 3; for divul and divsl.
+ B first word, low byte, for branch displacements
+ W second word (entire), for branch displacements
+ L second and third words (entire), for branch displacements
+ (also overloaded for move16)
+ b second word, low byte
+ w second word (entire) [variable word/long branch offset for dbra]
+ W second word (entire) (must be signed 16 bit value)
+ l second and third word (entire)
+ g variable branch offset for bra and similar instructions.
+ The place to store depends on the magnitude of offset.
+ t store in both place 7 and place 8; for floating point operations
+ c branch offset for cpBcc operations.
+ The place to store is word two if bit six of word one is zero,
+ and words two and three if bit six of word one is one.
+ i Increment by two, to skip over coprocessor extended operands. Only
+ works with the 'I' format.
+ k Dynamic K-factor field. Bits 6-4 of word 2, used as a register number.
+ Also used for dynamic fmovem instruction.
+ C floating point coprocessor constant - 7 bits. Also used for static
+ K-factors...
+ j Movec register #, stored in 12 low bits of second word.
+ m For M[S]ACx; 4 bits split with MSB shifted 6 bits in first word
+ and remaining 3 bits of register shifted 9 bits in first word.
+ Indicate upper/lower in 1 bit shifted 7 bits in second word.
+ Use with `R' or `u' format.
+ n `m' withouth upper/lower indication. (For M[S]ACx; 4 bits split
+ with MSB shifted 6 bits in first word and remaining 3 bits of
+ register shifted 9 bits in first word. No upper/lower
+ indication is done.) Use with `R' or `u' format.
+ o For M[S]ACw; 4 bits shifted 12 in second word (like `1').
+ Indicate upper/lower in 1 bit shifted 7 bits in second word.
+ Use with `R' or `u' format.
+ M For M[S]ACw; 4 bits in low bits of first word. Indicate
+ upper/lower in 1 bit shifted 6 bits in second word. Use with
+ `R' or `u' format.
+ N For M[S]ACw; 4 bits in low bits of second word. Indicate
+ upper/lower in 1 bit shifted 6 bits in second word. Use with
+ `R' or `u' format.
+ h shift indicator (scale factor), 1 bit shifted 10 in second word
+
+ Places to put operand, for general operands:
+ d destination, shifted 6 bits in first word
+ b source, at low bit of first word, and immediate uses one byte
+ w source, at low bit of first word, and immediate uses two bytes
+ l source, at low bit of first word, and immediate uses four bytes
+ s source, at low bit of first word.
+ Used sometimes in contexts where immediate is not allowed anyway.
+ f single precision float, low bit of 1st word, immediate uses 4 bytes
+ F double precision float, low bit of 1st word, immediate uses 8 bytes
+ x extended precision float, low bit of 1st word, immediate uses 12 bytes
+ p packed float, low bit of 1st word, immediate uses 12 bytes
+ G EMAC accumulator, load (bit 4 2nd word, !bit8 first word)
+ H EMAC accumulator, non load (bit 4 2nd word, bit 8 first word)
+ F EMAC ACCx
+ f EMAC ACCy
+ I MAC/EMAC scale factor
+ / Like 's', but set 2nd word, bit 5 if trailing_ampersand set
+ ] first word, bit 10
+*/
+
+extern const struct m68k_opcode m68k_opcodes[];
+extern const struct m68k_opcode_alias m68k_opcode_aliases[];
+
+extern const int m68k_numopcodes, m68k_numaliases;
+
+/* **** End of m68k-opcode.h */
+/* **** m68k-dis.c from sourceware.org CVS 2005-08-14. */
+/* Print Motorola 68k instructions.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ This file 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., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* Local function prototypes. */
+
+const char * const fpcr_names[] =
+{
+ "", "%fpiar", "%fpsr", "%fpiar/%fpsr", "%fpcr",
+ "%fpiar/%fpcr", "%fpsr/%fpcr", "%fpiar/%fpsr/%fpcr"
+};
+
+static char *const reg_names[] =
+{
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%fp", "%sp",
+ "%ps", "%pc"
+};
+
+/* Name of register halves for MAC/EMAC.
+ Seperate from reg_names since 'spu', 'fpl' look weird. */
+static char *const reg_half_names[] =
+{
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+ "%ps", "%pc"
+};
+
+/* Sign-extend an (unsigned char). */
+#if __STDC__ == 1
+#define COERCE_SIGNED_CHAR(ch) ((signed char) (ch))
+#else
+#define COERCE_SIGNED_CHAR(ch) ((int) (((ch) ^ 0x80) & 0xFF) - 128)
+#endif
+
+/* Get a 1 byte signed integer. */
+#define NEXTBYTE(p) (p += 2, FETCH_DATA (info, p), COERCE_SIGNED_CHAR(p[-1]))
+
+/* Get a 2 byte signed integer. */
+#define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000))
+#define NEXTWORD(p) \
+ (p += 2, FETCH_DATA (info, p), \
+ COERCE16 ((p[-2] << 8) + p[-1]))
+
+/* Get a 4 byte signed integer. */
+#define COERCE32(x) ((bfd_signed_vma) ((x) ^ 0x80000000) - 0x80000000)
+#define NEXTLONG(p) \
+ (p += 4, FETCH_DATA (info, p), \
+ (COERCE32 ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])))
+
+/* Get a 4 byte unsigned integer. */
+#define NEXTULONG(p) \
+ (p += 4, FETCH_DATA (info, p), \
+ (unsigned int) ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]))
+
+/* Get a single precision float. */
+#define NEXTSINGLE(val, p) \
+ (p += 4, FETCH_DATA (info, p), \
+ floatformat_to_double (&floatformat_ieee_single_big, (char *) p - 4, &val))
+
+/* Get a double precision float. */
+#define NEXTDOUBLE(val, p) \
+ (p += 8, FETCH_DATA (info, p), \
+ floatformat_to_double (&floatformat_ieee_double_big, (char *) p - 8, &val))
+
+/* Get an extended precision float. */
+#define NEXTEXTEND(val, p) \
+ (p += 12, FETCH_DATA (info, p), \
+ floatformat_to_double (&floatformat_m68881_ext, (char *) p - 12, &val))
+
+/* Need a function to convert from packed to double
+ precision. Actually, it's easier to print a
+ packed number than a double anyway, so maybe
+ there should be a special case to handle this... */
+#define NEXTPACKED(p) \
+ (p += 12, FETCH_DATA (info, p), 0.0)
+
+/* Maximum length of an instruction. */
+#define MAXLEN 22
+
+#include <setjmp.h>
+
+struct private
+{
+ /* Points to first byte not fetched. */
+ bfd_byte *max_fetched;
+ bfd_byte the_buffer[MAXLEN];
+ bfd_vma insn_start;
+ jmp_buf bailout;
+};
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+ to ADDR (exclusive) are valid. Returns 1 for success, longjmps
+ on error. */
+#define FETCH_DATA(info, addr) \
+ ((addr) <= ((struct private *) (info->private_data))->max_fetched \
+ ? 1 : fetch_data ((info), (addr)))
+
+static int
+fetch_data (struct disassemble_info *info, bfd_byte *addr)
+{
+ int status;
+ struct private *priv = (struct private *)info->private_data;
+ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+ status = (*info->read_memory_func) (start,
+ priv->max_fetched,
+ addr - priv->max_fetched,
+ info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, start, info);
+ longjmp (priv->bailout, 1);
+ }
+ else
+ priv->max_fetched = addr;
+ return 1;
+}
+
+/* This function is used to print to the bit-bucket. */
+static int
+dummy_printer (FILE *file ATTRIBUTE_UNUSED,
+ const char *format ATTRIBUTE_UNUSED,
+ ...)
+{
+ return 0;
+}
+
+static void
+dummy_print_address (bfd_vma vma ATTRIBUTE_UNUSED,
+ struct disassemble_info *info ATTRIBUTE_UNUSED)
+{
+}
+
+/* Fetch BITS bits from a position in the instruction specified by CODE.
+ CODE is a "place to put an argument", or 'x' for a destination
+ that is a general address (mode and register).
+ BUFFER contains the instruction. */
+
+static int
+fetch_arg (unsigned char *buffer,
+ int code,
+ int bits,
+ disassemble_info *info)
+{
+ int val = 0;
+
+ switch (code)
+ {
+ case '/': /* MAC/EMAC mask bit. */
+ val = buffer[3] >> 5;
+ break;
+
+ case 'G': /* EMAC ACC load. */
+ val = ((buffer[3] >> 3) & 0x2) | ((~buffer[1] >> 7) & 0x1);
+ break;
+
+ case 'H': /* EMAC ACC !load. */
+ val = ((buffer[3] >> 3) & 0x2) | ((buffer[1] >> 7) & 0x1);
+ break;
+
+ case ']': /* EMAC ACCEXT bit. */
+ val = buffer[0] >> 2;
+ break;
+
+ case 'I': /* MAC/EMAC scale factor. */
+ val = buffer[2] >> 1;
+ break;
+
+ case 'F': /* EMAC ACCx. */
+ val = buffer[0] >> 1;
+ break;
+
+ case 'f':
+ val = buffer[1];
+ break;
+
+ case 's':
+ val = buffer[1];
+ break;
+
+ case 'd': /* Destination, for register or quick. */
+ val = (buffer[0] << 8) + buffer[1];
+ val >>= 9;
+ break;
+
+ case 'x': /* Destination, for general arg. */
+ val = (buffer[0] << 8) + buffer[1];
+ val >>= 6;
+ break;
+
+ case 'k':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[3] >> 4);
+ break;
+
+ case 'C':
+ FETCH_DATA (info, buffer + 3);
+ val = buffer[3];
+ break;
+
+ case '1':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 12;
+ break;
+
+ case '2':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 6;
+ break;
+
+ case '3':
+ case 'j':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ break;
+
+ case '4':
+ FETCH_DATA (info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ val >>= 12;
+ break;
+
+ case '5':
+ FETCH_DATA (info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ val >>= 6;
+ break;
+
+ case '6':
+ FETCH_DATA (info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ break;
+
+ case '7':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 7;
+ break;
+
+ case '8':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 10;
+ break;
+
+ case '9':
+ FETCH_DATA (info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 5;
+ break;
+
+ case 'e':
+ val = (buffer[1] >> 6);
+ break;
+
+ case 'm':
+ val = (buffer[1] & 0x40 ? 0x8 : 0)
+ | ((buffer[0] >> 1) & 0x7)
+ | (buffer[3] & 0x80 ? 0x10 : 0);
+ break;
+
+ case 'n':
+ val = (buffer[1] & 0x40 ? 0x8 : 0) | ((buffer[0] >> 1) & 0x7);
+ break;
+
+ case 'o':
+ val = (buffer[2] >> 4) | (buffer[3] & 0x80 ? 0x10 : 0);
+ break;
+
+ case 'M':
+ val = (buffer[1] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
+ break;
+
+ case 'N':
+ val = (buffer[3] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
+ break;
+
+ case 'h':
+ val = buffer[2] >> 2;
+ break;
+
+ default:
+ abort ();
+ }
+
+ switch (bits)
+ {
+ case 1:
+ return val & 1;
+ case 2:
+ return val & 3;
+ case 3:
+ return val & 7;
+ case 4:
+ return val & 017;
+ case 5:
+ return val & 037;
+ case 6:
+ return val & 077;
+ case 7:
+ return val & 0177;
+ case 8:
+ return val & 0377;
+ case 12:
+ return val & 07777;
+ default:
+ abort ();
+ }
+}
+
+/* Check if an EA is valid for a particular code. This is required
+ for the EMAC instructions since the type of source address determines
+ if it is a EMAC-load instruciton if the EA is mode 2-5, otherwise it
+ is a non-load EMAC instruction and the bits mean register Ry.
+ A similar case exists for the movem instructions where the register
+ mask is interpreted differently for different EAs. */
+
+static bfd_boolean
+m68k_valid_ea (char code, int val)
+{
+ int mode, mask;
+#define M(n0,n1,n2,n3,n4,n5,n6,n70,n71,n72,n73,n74) \
+ (n0 | n1 << 1 | n2 << 2 | n3 << 3 | n4 << 4 | n5 << 5 | n6 << 6 \
+ | n70 << 7 | n71 << 8 | n72 << 9 | n73 << 10 | n74 << 11)
+
+ switch (code)
+ {
+ case '*':
+ mask = M (1,1,1,1,1,1,1,1,1,1,1,1);
+ break;
+ case '~':
+ mask = M (0,0,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case '%':
+ mask = M (1,1,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case ';':
+ mask = M (1,0,1,1,1,1,1,1,1,1,1,1);
+ break;
+ case '@':
+ mask = M (1,0,1,1,1,1,1,1,1,1,1,0);
+ break;
+ case '!':
+ mask = M (0,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '&':
+ mask = M (0,0,1,0,0,1,1,1,1,0,0,0);
+ break;
+ case '$':
+ mask = M (1,0,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case '?':
+ mask = M (1,0,1,0,0,1,1,1,1,0,0,0);
+ break;
+ case '/':
+ mask = M (1,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '|':
+ mask = M (0,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '>':
+ mask = M (0,0,1,0,1,1,1,1,1,0,0,0);
+ break;
+ case '<':
+ mask = M (0,0,1,1,0,1,1,1,1,1,1,0);
+ break;
+ case 'm':
+ mask = M (1,1,1,1,1,0,0,0,0,0,0,0);
+ break;
+ case 'n':
+ mask = M (0,0,0,0,0,1,0,0,0,1,0,0);
+ break;
+ case 'o':
+ mask = M (0,0,0,0,0,0,1,1,1,0,1,1);
+ break;
+ case 'p':
+ mask = M (1,1,1,1,1,1,0,0,0,0,0,0);
+ break;
+ case 'q':
+ mask = M (1,0,1,1,1,1,0,0,0,0,0,0);
+ break;
+ case 'v':
+ mask = M (1,0,1,1,1,1,0,1,1,0,0,0);
+ break;
+ case 'b':
+ mask = M (1,0,1,1,1,1,0,0,0,1,0,0);
+ break;
+ case 'w':
+ mask = M (0,0,1,1,1,1,0,0,0,1,0,0);
+ break;
+ case 'y':
+ mask = M (0,0,1,0,0,1,0,0,0,0,0,0);
+ break;
+ case 'z':
+ mask = M (0,0,1,0,0,1,0,0,0,1,0,0);
+ break;
+ case '4':
+ mask = M (0,0,1,1,1,1,0,0,0,0,0,0);
+ break;
+ default:
+ abort ();
+ }
+#undef M
+
+ mode = (val >> 3) & 7;
+ if (mode == 7)
+ mode += val & 7;
+ return (mask & (1 << mode)) != 0;
+}
+
+/* Print a base register REGNO and displacement DISP, on INFO->STREAM.
+ REGNO = -1 for pc, -2 for none (suppressed). */
+
+static void
+print_base (int regno, bfd_vma disp, disassemble_info *info)
+{
+ if (regno == -1)
+ {
+ (*info->fprintf_func) (info->stream, "%%pc@(");
+ (*info->print_address_func) (disp, info);
+ }
+ else
+ {
+ char buf[50];
+
+ if (regno == -2)
+ (*info->fprintf_func) (info->stream, "@(");
+ else if (regno == -3)
+ (*info->fprintf_func) (info->stream, "%%zpc@(");
+ else
+ (*info->fprintf_func) (info->stream, "%s@(", reg_names[regno]);
+
+ sprintf_vma (buf, disp);
+ (*info->fprintf_func) (info->stream, "%s", buf);
+ }
+}
+
+/* Print an indexed argument. The base register is BASEREG (-1 for pc).
+ P points to extension word, in buffer.
+ ADDR is the nominal core address of that extension word. */
+
+static unsigned char *
+print_indexed (int basereg,
+ unsigned char *p,
+ bfd_vma addr,
+ disassemble_info *info)
+{
+ int word;
+ static char *const scales[] = { "", ":2", ":4", ":8" };
+ bfd_vma base_disp;
+ bfd_vma outer_disp;
+ char buf[40];
+ char vmabuf[50];
+
+ word = NEXTWORD (p);
+
+ /* Generate the text for the index register.
+ Where this will be output is not yet determined. */
+ sprintf (buf, "%s:%c%s",
+ reg_names[(word >> 12) & 0xf],
+ (word & 0x800) ? 'l' : 'w',
+ scales[(word >> 9) & 3]);
+
+ /* Handle the 68000 style of indexing. */
+
+ if ((word & 0x100) == 0)
+ {
+ base_disp = word & 0xff;
+ if ((base_disp & 0x80) != 0)
+ base_disp -= 0x100;
+ if (basereg == -1)
+ base_disp += addr;
+ print_base (basereg, base_disp, info);
+ (*info->fprintf_func) (info->stream, ",%s)", buf);
+ return p;
+ }
+
+ /* Handle the generalized kind. */
+ /* First, compute the displacement to add to the base register. */
+ if (word & 0200)
+ {
+ if (basereg == -1)
+ basereg = -3;
+ else
+ basereg = -2;
+ }
+ if (word & 0100)
+ buf[0] = '\0';
+ base_disp = 0;
+ switch ((word >> 4) & 3)
+ {
+ case 2:
+ base_disp = NEXTWORD (p);
+ break;
+ case 3:
+ base_disp = NEXTLONG (p);
+ }
+ if (basereg == -1)
+ base_disp += addr;
+
+ /* Handle single-level case (not indirect). */
+ if ((word & 7) == 0)
+ {
+ print_base (basereg, base_disp, info);
+ if (buf[0] != '\0')
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ (*info->fprintf_func) (info->stream, ")");
+ return p;
+ }
+
+ /* Two level. Compute displacement to add after indirection. */
+ outer_disp = 0;
+ switch (word & 3)
+ {
+ case 2:
+ outer_disp = NEXTWORD (p);
+ break;
+ case 3:
+ outer_disp = NEXTLONG (p);
+ }
+
+ print_base (basereg, base_disp, info);
+ if ((word & 4) == 0 && buf[0] != '\0')
+ {
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ buf[0] = '\0';
+ }
+ sprintf_vma (vmabuf, outer_disp);
+ (*info->fprintf_func) (info->stream, ")@(%s", vmabuf);
+ if (buf[0] != '\0')
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ (*info->fprintf_func) (info->stream, ")");
+
+ return p;
+}
+
+/* Returns number of bytes "eaten" by the operand, or
+ return -1 if an invalid operand was found, or -2 if
+ an opcode tabe error was found.
+ ADDR is the pc for this arg to be relative to. */
+
+static int
+print_insn_arg (const char *d,
+ unsigned char *buffer,
+ unsigned char *p0,
+ bfd_vma addr,
+ disassemble_info *info)
+{
+ int val = 0;
+ int place = d[1];
+ unsigned char *p = p0;
+ int regno;
+ const char *regname;
+ unsigned char *p1;
+ double flval;
+ int flt_p;
+ bfd_signed_vma disp;
+ unsigned int uval;
+
+ switch (*d)
+ {
+ case 'c': /* Cache identifier. */
+ {
+ static char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" };
+ val = fetch_arg (buffer, place, 2, info);
+ (*info->fprintf_func) (info->stream, cacheFieldName[val]);
+ break;
+ }
+
+ case 'a': /* Address register indirect only. Cf. case '+'. */
+ {
+ (*info->fprintf_func)
+ (info->stream,
+ "%s@",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+ }
+
+ case '_': /* 32-bit absolute address for move16. */
+ {
+ uval = NEXTULONG (p);
+ (*info->print_address_func) (uval, info);
+ break;
+ }
+
+ case 'C':
+ (*info->fprintf_func) (info->stream, "%%ccr");
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%%sr");
+ break;
+
+ case 'U':
+ (*info->fprintf_func) (info->stream, "%%usp");
+ break;
+
+ case 'E':
+ (*info->fprintf_func) (info->stream, "%%acc");
+ break;
+
+ case 'G':
+ (*info->fprintf_func) (info->stream, "%%macsr");
+ break;
+
+ case 'H':
+ (*info->fprintf_func) (info->stream, "%%mask");
+ break;
+
+ case 'J':
+ {
+ /* FIXME: There's a problem here, different m68k processors call the
+ same address different names. This table can't get it right
+ because it doesn't know which processor it's disassembling for. */
+ static const struct { char *name; int value; } names[]
+ = {{"%sfc", 0x000}, {"%dfc", 0x001}, {"%cacr", 0x002},
+ {"%tc", 0x003}, {"%itt0",0x004}, {"%itt1", 0x005},
+ {"%dtt0",0x006}, {"%dtt1",0x007}, {"%buscr",0x008},
+ {"%usp", 0x800}, {"%vbr", 0x801}, {"%caar", 0x802},
+ {"%msp", 0x803}, {"%isp", 0x804},
+ {"%flashbar", 0xc04}, {"%rambar", 0xc05}, /* mcf528x added these. */
+
+ /* Should we be calling this psr like we do in case 'Y'? */
+ {"%mmusr",0x805},
+
+ {"%urp", 0x806}, {"%srp", 0x807}, {"%pcr", 0x808}};
+
+ val = fetch_arg (buffer, place, 12, info);
+ for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--)
+ if (names[regno].value == val)
+ {
+ (*info->fprintf_func) (info->stream, "%s", names[regno].name);
+ break;
+ }
+ if (regno < 0)
+ (*info->fprintf_func) (info->stream, "%d", val);
+ }
+ break;
+
+ case 'Q':
+ val = fetch_arg (buffer, place, 3, info);
+ /* 0 means 8, except for the bkpt instruction... */
+ if (val == 0 && d[1] != 's')
+ val = 8;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'x':
+ val = fetch_arg (buffer, place, 3, info);
+ /* 0 means -1. */
+ if (val == 0)
+ val = -1;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'M':
+ if (place == 'h')
+ {
+ static char *const scalefactor_name[] = { "<<", ">>" };
+ val = fetch_arg (buffer, place, 1, info);
+ (*info->fprintf_func) (info->stream, scalefactor_name[val]);
+ }
+ else
+ {
+ val = fetch_arg (buffer, place, 8, info);
+ if (val & 0x80)
+ val = val - 0x100;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ }
+ break;
+
+ case 'T':
+ val = fetch_arg (buffer, place, 4, info);
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 3, info)]);
+ break;
+
+ case 'A':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 3, info) + 010]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 4, info)]);
+ break;
+
+ case 'r':
+ regno = fetch_arg (buffer, place, 4, info);
+ if (regno > 7)
+ (*info->fprintf_func) (info->stream, "%s@", reg_names[regno]);
+ else
+ (*info->fprintf_func) (info->stream, "@(%s)", reg_names[regno]);
+ break;
+
+ case 'F':
+ (*info->fprintf_func)
+ (info->stream, "%%fp%d",
+ fetch_arg (buffer, place, 3, info));
+ break;
+
+ case 'O':
+ val = fetch_arg (buffer, place, 6, info);
+ if (val & 0x20)
+ (*info->fprintf_func) (info->stream, "%s", reg_names[val & 7]);
+ else
+ (*info->fprintf_func) (info->stream, "%d", val);
+ break;
+
+ case '+':
+ (*info->fprintf_func)
+ (info->stream, "%s@+",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+
+ case '-':
+ (*info->fprintf_func)
+ (info->stream, "%s@-",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+
+ case 'k':
+ if (place == 'k')
+ (*info->fprintf_func)
+ (info->stream, "{%s}",
+ reg_names[fetch_arg (buffer, place, 3, info)]);
+ else if (place == 'C')
+ {
+ val = fetch_arg (buffer, place, 7, info);
+ if (val > 63) /* This is a signed constant. */
+ val -= 128;
+ (*info->fprintf_func) (info->stream, "{#%d}", val);
+ }
+ else
+ return -2;
+ break;
+
+ case '#':
+ case '^':
+ p1 = buffer + (*d == '#' ? 2 : 4);
+ if (place == 's')
+ val = fetch_arg (buffer, place, 4, info);
+ else if (place == 'C')
+ val = fetch_arg (buffer, place, 7, info);
+ else if (place == '8')
+ val = fetch_arg (buffer, place, 3, info);
+ else if (place == '3')
+ val = fetch_arg (buffer, place, 8, info);
+ else if (place == 'b')
+ val = NEXTBYTE (p1);
+ else if (place == 'w' || place == 'W')
+ val = NEXTWORD (p1);
+ else if (place == 'l')
+ val = NEXTLONG (p1);
+ else
+ return -2;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'B':
+ if (place == 'b')
+ disp = NEXTBYTE (p);
+ else if (place == 'B')
+ disp = COERCE_SIGNED_CHAR (buffer[1]);
+ else if (place == 'w' || place == 'W')
+ disp = NEXTWORD (p);
+ else if (place == 'l' || place == 'L' || place == 'C')
+ disp = NEXTLONG (p);
+ else if (place == 'g')
+ {
+ disp = NEXTBYTE (buffer);
+ if (disp == 0)
+ disp = NEXTWORD (p);
+ else if (disp == -1)
+ disp = NEXTLONG (p);
+ }
+ else if (place == 'c')
+ {
+ if (buffer[1] & 0x40) /* If bit six is one, long offset. */
+ disp = NEXTLONG (p);
+ else
+ disp = NEXTWORD (p);
+ }
+ else
+ return -2;
+
+ (*info->print_address_func) (addr + disp, info);
+ break;
+
+ case 'd':
+ val = NEXTWORD (p);
+ (*info->fprintf_func)
+ (info->stream, "%s@(%d)",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8], val);
+ break;
+
+ case 's':
+ (*info->fprintf_func) (info->stream, "%s",
+ fpcr_names[fetch_arg (buffer, place, 3, info)]);
+ break;
+
+ case 'e':
+ val = fetch_arg(buffer, place, 2, info);
+ (*info->fprintf_func) (info->stream, "%%acc%d", val);
+ break;
+
+ case 'g':
+ val = fetch_arg(buffer, place, 1, info);
+ (*info->fprintf_func) (info->stream, "%%accext%s", val==0 ? "01" : "23");
+ break;
+
+ case 'i':
+ val = fetch_arg(buffer, place, 2, info);
+ if (val == 1)
+ (*info->fprintf_func) (info->stream, "<<");
+ else if (val == 3)
+ (*info->fprintf_func) (info->stream, ">>");
+ else
+ return -1;
+ break;
+
+ case 'I':
+ /* Get coprocessor ID... */
+ val = fetch_arg (buffer, 'd', 3, info);
+
+ if (val != 1) /* Unusual coprocessor ID? */
+ (*info->fprintf_func) (info->stream, "(cpid=%d) ", val);
+ break;
+
+ case '4':
+ case '*':
+ case '~':
+ case '%':
+ case ';':
+ case '@':
+ case '!':
+ case '$':
+ case '?':
+ case '/':
+ case '&':
+ case '|':
+ case '<':
+ case '>':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'v':
+ case 'b':
+ case 'w':
+ case 'y':
+ case 'z':
+ if (place == 'd')
+ {
+ val = fetch_arg (buffer, 'x', 6, info);
+ val = ((val & 7) << 3) + ((val >> 3) & 7);
+ }
+ else
+ val = fetch_arg (buffer, 's', 6, info);
+
+ /* If the <ea> is invalid for *d, then reject this match. */
+ if (!m68k_valid_ea (*d, val))
+ return -1;
+
+ /* Get register number assuming address register. */
+ regno = (val & 7) + 8;
+ regname = reg_names[regno];
+ switch (val >> 3)
+ {
+ case 0:
+ (*info->fprintf_func) (info->stream, "%s", reg_names[val]);
+ break;
+
+ case 1:
+ (*info->fprintf_func) (info->stream, "%s", regname);
+ break;
+
+ case 2:
+ (*info->fprintf_func) (info->stream, "%s@", regname);
+ break;
+
+ case 3:
+ (*info->fprintf_func) (info->stream, "%s@+", regname);
+ break;
+
+ case 4:
+ (*info->fprintf_func) (info->stream, "%s@-", regname);
+ break;
+
+ case 5:
+ val = NEXTWORD (p);
+ (*info->fprintf_func) (info->stream, "%s@(%d)", regname, val);
+ break;
+
+ case 6:
+ p = print_indexed (regno, p, addr, info);
+ break;
+
+ case 7:
+ switch (val & 7)
+ {
+ case 0:
+ val = NEXTWORD (p);
+ (*info->print_address_func) (val, info);
+ break;
+
+ case 1:
+ uval = NEXTULONG (p);
+ (*info->print_address_func) (uval, info);
+ break;
+
+ case 2:
+ val = NEXTWORD (p);
+ (*info->fprintf_func) (info->stream, "%%pc@(");
+ (*info->print_address_func) (addr + val, info);
+ (*info->fprintf_func) (info->stream, ")");
+ break;
+
+ case 3:
+ p = print_indexed (-1, p, addr, info);
+ break;
+
+ case 4:
+ flt_p = 1; /* Assume it's a float... */
+ switch (place)
+ {
+ case 'b':
+ val = NEXTBYTE (p);
+ flt_p = 0;
+ break;
+
+ case 'w':
+ val = NEXTWORD (p);
+ flt_p = 0;
+ break;
+
+ case 'l':
+ val = NEXTLONG (p);
+ flt_p = 0;
+ break;
+
+ case 'f':
+ NEXTSINGLE (flval, p);
+ break;
+
+ case 'F':
+ NEXTDOUBLE (flval, p);
+ break;
+
+ case 'x':
+ NEXTEXTEND (flval, p);
+ break;
+
+ case 'p':
+ flval = NEXTPACKED (p);
+ break;
+
+ default:
+ return -1;
+ }
+ if (flt_p) /* Print a float? */
+ (*info->fprintf_func) (info->stream, "#%g", flval);
+ else
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ /* If place is '/', then this is the case of the mask bit for
+ mac/emac loads. Now that the arg has been printed, grab the
+ mask bit and if set, add a '&' to the arg. */
+ if (place == '/')
+ {
+ val = fetch_arg (buffer, place, 1, info);
+ if (val)
+ info->fprintf_func (info->stream, "&");
+ }
+ break;
+
+ case 'L':
+ case 'l':
+ if (place == 'w')
+ {
+ char doneany;
+ p1 = buffer + 2;
+ val = NEXTWORD (p1);
+ /* Move the pointer ahead if this point is farther ahead
+ than the last. */
+ p = p1 > p ? p1 : p;
+ if (val == 0)
+ {
+ (*info->fprintf_func) (info->stream, "#0");
+ break;
+ }
+ if (*d == 'l')
+ {
+ int newval = 0;
+
+ for (regno = 0; regno < 16; ++regno)
+ if (val & (0x8000 >> regno))
+ newval |= 1 << regno;
+ val = newval;
+ }
+ val &= 0xffff;
+ doneany = 0;
+ for (regno = 0; regno < 16; ++regno)
+ if (val & (1 << regno))
+ {
+ int first_regno;
+
+ if (doneany)
+ (*info->fprintf_func) (info->stream, "/");
+ doneany = 1;
+ (*info->fprintf_func) (info->stream, "%s", reg_names[regno]);
+ first_regno = regno;
+ while (val & (1 << (regno + 1)))
+ ++regno;
+ if (regno > first_regno)
+ (*info->fprintf_func) (info->stream, "-%s",
+ reg_names[regno]);
+ }
+ }
+ else if (place == '3')
+ {
+ /* `fmovem' insn. */
+ char doneany;
+ val = fetch_arg (buffer, place, 8, info);
+ if (val == 0)
+ {
+ (*info->fprintf_func) (info->stream, "#0");
+ break;
+ }
+ if (*d == 'l')
+ {
+ int newval = 0;
+
+ for (regno = 0; regno < 8; ++regno)
+ if (val & (0x80 >> regno))
+ newval |= 1 << regno;
+ val = newval;
+ }
+ val &= 0xff;
+ doneany = 0;
+ for (regno = 0; regno < 8; ++regno)
+ if (val & (1 << regno))
+ {
+ int first_regno;
+ if (doneany)
+ (*info->fprintf_func) (info->stream, "/");
+ doneany = 1;
+ (*info->fprintf_func) (info->stream, "%%fp%d", regno);
+ first_regno = regno;
+ while (val & (1 << (regno + 1)))
+ ++regno;
+ if (regno > first_regno)
+ (*info->fprintf_func) (info->stream, "-%%fp%d", regno);
+ }
+ }
+ else if (place == '8')
+ {
+ /* fmoveml for FP status registers. */
+ (*info->fprintf_func) (info->stream, "%s",
+ fpcr_names[fetch_arg (buffer, place, 3,
+ info)]);
+ }
+ else
+ return -2;
+ break;
+
+ case 'X':
+ place = '8';
+ case 'Y':
+ case 'Z':
+ case 'W':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ {
+ int val = fetch_arg (buffer, place, 5, info);
+ char *name = 0;
+
+ switch (val)
+ {
+ case 2: name = "%tt0"; break;
+ case 3: name = "%tt1"; break;
+ case 0x10: name = "%tc"; break;
+ case 0x11: name = "%drp"; break;
+ case 0x12: name = "%srp"; break;
+ case 0x13: name = "%crp"; break;
+ case 0x14: name = "%cal"; break;
+ case 0x15: name = "%val"; break;
+ case 0x16: name = "%scc"; break;
+ case 0x17: name = "%ac"; break;
+ case 0x18: name = "%psr"; break;
+ case 0x19: name = "%pcsr"; break;
+ case 0x1c:
+ case 0x1d:
+ {
+ int break_reg = ((buffer[3] >> 2) & 7);
+
+ (*info->fprintf_func)
+ (info->stream, val == 0x1c ? "%%bad%d" : "%%bac%d",
+ break_reg);
+ }
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, "<mmu register %d>", val);
+ }
+ if (name)
+ (*info->fprintf_func) (info->stream, "%s", name);
+ }
+ break;
+
+ case 'f':
+ {
+ int fc = fetch_arg (buffer, place, 5, info);
+
+ if (fc == 1)
+ (*info->fprintf_func) (info->stream, "%%dfc");
+ else if (fc == 0)
+ (*info->fprintf_func) (info->stream, "%%sfc");
+ else
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream, _("<function code %d>"), fc);
+ }
+ break;
+
+ case 'V':
+ (*info->fprintf_func) (info->stream, "%%val");
+ break;
+
+ case 't':
+ {
+ int level = fetch_arg (buffer, place, 3, info);
+
+ (*info->fprintf_func) (info->stream, "%d", level);
+ }
+ break;
+
+ case 'u':
+ {
+ short is_upper = 0;
+ int reg = fetch_arg (buffer, place, 5, info);
+
+ if (reg & 0x10)
+ {
+ is_upper = 1;
+ reg &= 0xf;
+ }
+ (*info->fprintf_func) (info->stream, "%s%s",
+ reg_half_names[reg],
+ is_upper ? "u" : "l");
+ }
+ break;
+
+ default:
+ return -2;
+ }
+
+ return p - p0;
+}
+
+/* Try to match the current instruction to best and if so, return the
+ number of bytes consumed from the instruction stream, else zero. */
+
+static int
+match_insn_m68k (bfd_vma memaddr,
+ disassemble_info * info,
+ const struct m68k_opcode * best,
+ struct private * priv)
+{
+ unsigned char *save_p;
+ unsigned char *p;
+ const char *d;
+
+ bfd_byte *buffer = priv->the_buffer;
+ fprintf_ftype save_printer = info->fprintf_func;
+ void (* save_print_address) (bfd_vma, struct disassemble_info *)
+ = info->print_address_func;
+
+ /* Point at first word of argument data,
+ and at descriptor for first argument. */
+ p = buffer + 2;
+
+ /* Figure out how long the fixed-size portion of the instruction is.
+ The only place this is stored in the opcode table is
+ in the arguments--look for arguments which specify fields in the 2nd
+ or 3rd words of the instruction. */
+ for (d = best->args; *d; d += 2)
+ {
+ /* I don't think it is necessary to be checking d[0] here;
+ I suspect all this could be moved to the case statement below. */
+ if (d[0] == '#')
+ {
+ if (d[1] == 'l' && p - buffer < 6)
+ p = buffer + 6;
+ else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8')
+ p = buffer + 4;
+ }
+
+ if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4)
+ p = buffer + 4;
+
+ switch (d[1])
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '7':
+ case '8':
+ case '9':
+ case 'i':
+ if (p - buffer < 4)
+ p = buffer + 4;
+ break;
+ case '4':
+ case '5':
+ case '6':
+ if (p - buffer < 6)
+ p = buffer + 6;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* pflusha is an exceptions. It takes no arguments but is two words
+ long. Recognize it by looking at the lower 16 bits of the mask. */
+ if (p - buffer < 4 && (best->match & 0xFFFF) != 0)
+ p = buffer + 4;
+
+ /* lpstop is another exception. It takes a one word argument but is
+ three words long. */
+ if (p - buffer < 6
+ && (best->match & 0xffff) == 0xffff
+ && best->args[0] == '#'
+ && best->args[1] == 'w')
+ {
+ /* Copy the one word argument into the usual location for a one
+ word argument, to simplify printing it. We can get away with
+ this because we know exactly what the second word is, and we
+ aren't going to print anything based on it. */
+ p = buffer + 6;
+ FETCH_DATA (info, p);
+ buffer[2] = buffer[4];
+ buffer[3] = buffer[5];
+ }
+
+ FETCH_DATA (info, p);
+
+ d = best->args;
+
+ save_p = p;
+ info->print_address_func = dummy_print_address;
+ info->fprintf_func = (fprintf_ftype) dummy_printer;
+
+ /* We scan the operands twice. The first time we don't print anything,
+ but look for errors. */
+ for (; *d; d += 2)
+ {
+ int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
+
+ if (eaten >= 0)
+ p += eaten;
+ else if (eaten == -1)
+ {
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+ return 0;
+ }
+ else
+ {
+ info->fprintf_func (info->stream,
+ /* xgettext:c-format */
+ _("<internal error in opcode table: %s %s>\n"),
+ best->name, best->args);
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+ return 2;
+ }
+ }
+
+ p = save_p;
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+
+ d = best->args;
+
+ info->fprintf_func (info->stream, "%s", best->name);
+
+ if (*d)
+ info->fprintf_func (info->stream, " ");
+
+ while (*d)
+ {
+ p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
+ d += 2;
+
+ if (*d && *(d - 2) != 'I' && *d != 'k')
+ info->fprintf_func (info->stream, ",");
+ }
+
+ return p - buffer;
+}
+
+/* Print the m68k instruction at address MEMADDR in debugged memory,
+ on INFO->STREAM. Returns length of the instruction, in bytes. */
+
+int
+print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
+{
+ int i;
+ const char *d;
+ unsigned int arch_mask;
+ struct private priv;
+ bfd_byte *buffer = priv.the_buffer;
+ int major_opcode;
+ static int numopcodes[16];
+ static const struct m68k_opcode **opcodes[16];
+ int val;
+
+ if (!opcodes[0])
+ {
+ /* Speed up the matching by sorting the opcode
+ table on the upper four bits of the opcode. */
+ const struct m68k_opcode **opc_pointer[16];
+
+ /* First count how many opcodes are in each of the sixteen buckets. */
+ for (i = 0; i < m68k_numopcodes; i++)
+ numopcodes[(m68k_opcodes[i].opcode >> 28) & 15]++;
+
+ /* Then create a sorted table of pointers
+ that point into the unsorted table. */
+ opc_pointer[0] = malloc (sizeof (struct m68k_opcode *)
+ * m68k_numopcodes);
+ opcodes[0] = opc_pointer[0];
+
+ for (i = 1; i < 16; i++)
+ {
+ opc_pointer[i] = opc_pointer[i - 1] + numopcodes[i - 1];
+ opcodes[i] = opc_pointer[i];
+ }
+
+ for (i = 0; i < m68k_numopcodes; i++)
+ *opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i];
+ }
+
+ info->private_data = (PTR) &priv;
+ /* Tell objdump to use two bytes per chunk
+ and six bytes per line for displaying raw data. */
+ info->bytes_per_chunk = 2;
+ info->bytes_per_line = 6;
+ info->display_endian = BFD_ENDIAN_BIG;
+ priv.max_fetched = priv.the_buffer;
+ priv.insn_start = memaddr;
+
+ if (setjmp (priv.bailout) != 0)
+ /* Error return. */
+ return -1;
+
+ switch (info->mach)
+ {
+ default:
+ case 0:
+ arch_mask = (unsigned int) -1;
+ break;
+ case bfd_mach_m68000:
+ arch_mask = m68000|m68881|m68851;
+ break;
+ case bfd_mach_m68008:
+ arch_mask = m68008|m68881|m68851;
+ break;
+ case bfd_mach_m68010:
+ arch_mask = m68010|m68881|m68851;
+ break;
+ case bfd_mach_m68020:
+ arch_mask = m68020|m68881|m68851;
+ break;
+ case bfd_mach_m68030:
+ arch_mask = m68030|m68881|m68851;
+ break;
+ case bfd_mach_m68040:
+ arch_mask = m68040|m68881|m68851;
+ break;
+ case bfd_mach_m68060:
+ arch_mask = m68060|m68881|m68851;
+ break;
+ case bfd_mach_mcf5200:
+ arch_mask = mcfisa_a;
+ break;
+ case bfd_mach_mcf521x:
+ case bfd_mach_mcf528x:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp|mcfemac;
+ break;
+ case bfd_mach_mcf5206e:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
+ break;
+ case bfd_mach_mcf5249:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfemac;
+ break;
+ case bfd_mach_mcf5307:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
+ break;
+ case bfd_mach_mcf5407:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac;
+ break;
+ case bfd_mach_mcf547x:
+ case bfd_mach_mcf548x:
+ case bfd_mach_mcfv4e:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp|cfloat|mcfemac;
+ break;
+ }
+
+ FETCH_DATA (info, buffer + 2);
+ major_opcode = (buffer[0] >> 4) & 15;
+
+ for (i = 0; i < numopcodes[major_opcode]; i++)
+ {
+ const struct m68k_opcode *opc = opcodes[major_opcode][i];
+ unsigned long opcode = opc->opcode;
+ unsigned long match = opc->match;
+
+ if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24)))
+ && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16)))
+ /* Only fetch the next two bytes if we need to. */
+ && (((0xffff & match) == 0)
+ ||
+ (FETCH_DATA (info, buffer + 4)
+ && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8)))
+ && ((0xff & buffer[3] & match) == (0xff & opcode)))
+ )
+ && (opc->arch & arch_mask) != 0)
+ {
+ /* Don't use for printout the variants of divul and divsl
+ that have the same register number in two places.
+ The more general variants will match instead. */
+ for (d = opc->args; *d; d += 2)
+ if (d[1] == 'D')
+ break;
+
+ /* Don't use for printout the variants of most floating
+ point coprocessor instructions which use the same
+ register number in two places, as above. */
+ if (*d == '\0')
+ for (d = opc->args; *d; d += 2)
+ if (d[1] == 't')
+ break;
+
+ /* Don't match fmovel with more than one register;
+ wait for fmoveml. */
+ if (*d == '\0')
+ {
+ for (d = opc->args; *d; d += 2)
+ {
+ if (d[0] == 's' && d[1] == '8')
+ {
+ val = fetch_arg (buffer, d[1], 3, info);
+ if ((val & (val - 1)) != 0)
+ break;
+ }
+ }
+ }
+
+ if (*d == '\0')
+ if ((val = match_insn_m68k (memaddr, info, opc, & priv)))
+ return val;
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->fprintf_func (info->stream, "0%o", (buffer[0] << 8) + buffer[1]);
+ return 2;
+}
+/* **** End of m68k-dis.c */
+/* **** m68k-opc.h from sourceware.org CVS 2005-08-14. */
+/* Opcode table for m680[012346]0/m6888[12]/m68851/mcf5200.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 1, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ 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 file; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define one(x) ((unsigned int) (x) << 16)
+#define two(x, y) (((unsigned int) (x) << 16) + (y))
+
+/* The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler will bomb at
+ runtime. */
+
+const struct m68k_opcode m68k_opcodes[] =
+{
+{"abcd", 2, one(0140400), one(0170770), "DsDd", m68000up },
+{"abcd", 2, one(0140410), one(0170770), "-s-d", m68000up },
+
+{"addaw", 2, one(0150300), one(0170700), "*wAd", m68000up },
+{"addal", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a },
+
+{"addib", 4, one(0003000), one(0177700), "#b$s", m68000up },
+{"addiw", 4, one(0003100), one(0177700), "#w$s", m68000up },
+{"addil", 6, one(0003200), one(0177700), "#l$s", m68000up },
+{"addil", 6, one(0003200), one(0177700), "#lDs", mcfisa_a },
+
+{"addqb", 2, one(0050000), one(0170700), "Qd$b", m68000up },
+{"addqw", 2, one(0050100), one(0170700), "Qd%w", m68000up },
+{"addql", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a },
+
+/* The add opcode can generate the adda, addi, and addq instructions. */
+{"addb", 2, one(0050000), one(0170700), "Qd$b", m68000up },
+{"addb", 4, one(0003000), one(0177700), "#b$s", m68000up },
+{"addb", 2, one(0150000), one(0170700), ";bDd", m68000up },
+{"addb", 2, one(0150400), one(0170700), "Dd~b", m68000up },
+{"addw", 2, one(0050100), one(0170700), "Qd%w", m68000up },
+{"addw", 2, one(0150300), one(0170700), "*wAd", m68000up },
+{"addw", 4, one(0003100), one(0177700), "#w$s", m68000up },
+{"addw", 2, one(0150100), one(0170700), "*wDd", m68000up },
+{"addw", 2, one(0150500), one(0170700), "Dd~w", m68000up },
+{"addl", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a },
+{"addl", 6, one(0003200), one(0177700), "#l$s", m68000up },
+{"addl", 6, one(0003200), one(0177700), "#lDs", mcfisa_a },
+{"addl", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"addl", 2, one(0150200), one(0170700), "*lDd", m68000up | mcfisa_a },
+{"addl", 2, one(0150600), one(0170700), "Dd~l", m68000up | mcfisa_a },
+
+{"addxb", 2, one(0150400), one(0170770), "DsDd", m68000up },
+{"addxb", 2, one(0150410), one(0170770), "-s-d", m68000up },
+{"addxw", 2, one(0150500), one(0170770), "DsDd", m68000up },
+{"addxw", 2, one(0150510), one(0170770), "-s-d", m68000up },
+{"addxl", 2, one(0150600), one(0170770), "DsDd", m68000up | mcfisa_a },
+{"addxl", 2, one(0150610), one(0170770), "-s-d", m68000up },
+
+{"andib", 4, one(0001000), one(0177700), "#b$s", m68000up },
+{"andib", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andiw", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andiw", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"andil", 6, one(0001200), one(0177700), "#l$s", m68000up },
+{"andil", 6, one(0001200), one(0177700), "#lDs", mcfisa_a },
+{"andi", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andi", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andi", 4, one(0001174), one(0177777), "#wSs", m68000up },
+
+/* The and opcode can generate the andi instruction. */
+{"andb", 4, one(0001000), one(0177700), "#b$s", m68000up },
+{"andb", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andb", 2, one(0140000), one(0170700), ";bDd", m68000up },
+{"andb", 2, one(0140400), one(0170700), "Dd~b", m68000up },
+{"andw", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andw", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"andw", 2, one(0140100), one(0170700), ";wDd", m68000up },
+{"andw", 2, one(0140500), one(0170700), "Dd~w", m68000up },
+{"andl", 6, one(0001200), one(0177700), "#l$s", m68000up },
+{"andl", 6, one(0001200), one(0177700), "#lDs", mcfisa_a },
+{"andl", 2, one(0140200), one(0170700), ";lDd", m68000up | mcfisa_a },
+{"andl", 2, one(0140600), one(0170700), "Dd~l", m68000up | mcfisa_a },
+{"and", 4, one(0001100), one(0177700), "#w$w", m68000up },
+{"and", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"and", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"and", 2, one(0140100), one(0170700), ";wDd", m68000up },
+{"and", 2, one(0140500), one(0170700), "Dd~w", m68000up },
+
+{"aslb", 2, one(0160400), one(0170770), "QdDs", m68000up },
+{"aslb", 2, one(0160440), one(0170770), "DdDs", m68000up },
+{"aslw", 2, one(0160500), one(0170770), "QdDs", m68000up },
+{"aslw", 2, one(0160540), one(0170770), "DdDs", m68000up },
+{"aslw", 2, one(0160700), one(0177700), "~s", m68000up },
+{"asll", 2, one(0160600), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"asll", 2, one(0160640), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"asrb", 2, one(0160000), one(0170770), "QdDs", m68000up },
+{"asrb", 2, one(0160040), one(0170770), "DdDs", m68000up },
+{"asrw", 2, one(0160100), one(0170770), "QdDs", m68000up },
+{"asrw", 2, one(0160140), one(0170770), "DdDs", m68000up },
+{"asrw", 2, one(0160300), one(0177700), "~s", m68000up },
+{"asrl", 2, one(0160200), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"asrl", 2, one(0160240), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"bhiw", 2, one(0061000), one(0177777), "BW", m68000up | mcfisa_a },
+{"blsw", 2, one(0061400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bccw", 2, one(0062000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bcsw", 2, one(0062400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bnew", 2, one(0063000), one(0177777), "BW", m68000up | mcfisa_a },
+{"beqw", 2, one(0063400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bvcw", 2, one(0064000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bvsw", 2, one(0064400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bplw", 2, one(0065000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bmiw", 2, one(0065400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bgew", 2, one(0066000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bltw", 2, one(0066400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bgtw", 2, one(0067000), one(0177777), "BW", m68000up | mcfisa_a },
+{"blew", 2, one(0067400), one(0177777), "BW", m68000up | mcfisa_a },
+
+{"bhil", 2, one(0061377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"blsl", 2, one(0061777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bccl", 2, one(0062377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bcsl", 2, one(0062777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bnel", 2, one(0063377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"beql", 2, one(0063777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bvcl", 2, one(0064377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bvsl", 2, one(0064777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bpll", 2, one(0065377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bmil", 2, one(0065777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bgel", 2, one(0066377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bltl", 2, one(0066777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bgtl", 2, one(0067377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"blel", 2, one(0067777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+
+{"bhis", 2, one(0061000), one(0177400), "BB", m68000up | mcfisa_a },
+{"blss", 2, one(0061400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bccs", 2, one(0062000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bcss", 2, one(0062400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bnes", 2, one(0063000), one(0177400), "BB", m68000up | mcfisa_a },
+{"beqs", 2, one(0063400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bvcs", 2, one(0064000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bvss", 2, one(0064400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bpls", 2, one(0065000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bmis", 2, one(0065400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bges", 2, one(0066000), one(0177400), "BB", m68000up | mcfisa_a },
+{"blts", 2, one(0066400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bgts", 2, one(0067000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bles", 2, one(0067400), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"jhi", 2, one(0061000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jls", 2, one(0061400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jcc", 2, one(0062000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jcs", 2, one(0062400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jne", 2, one(0063000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jeq", 2, one(0063400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jvc", 2, one(0064000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jvs", 2, one(0064400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jpl", 2, one(0065000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jmi", 2, one(0065400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jge", 2, one(0066000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jlt", 2, one(0066400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jgt", 2, one(0067000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jle", 2, one(0067400), one(0177400), "Bg", m68000up | mcfisa_a },
+
+{"bchg", 2, one(0000500), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bchg", 4, one(0004100), one(0177700), "#b$s", m68000up },
+{"bchg", 4, one(0004100), one(0177700), "#bqs", mcfisa_a },
+
+{"bclr", 2, one(0000600), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bclr", 4, one(0004200), one(0177700), "#b$s", m68000up },
+{"bclr", 4, one(0004200), one(0177700), "#bqs", mcfisa_a },
+
+{"bfchg", 4, two(0165300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfclr", 4, two(0166300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfexts", 4, two(0165700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfextu", 4, two(0164700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfffo", 4, two(0166700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfins", 4, two(0167700, 0), two(0177700, 0100000), "D1?sO2O3", m68020up },
+{"bfset", 4, two(0167300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bftst", 4, two(0164300, 0), two(0177700, 0170000), "/sO2O3", m68020up },
+
+{"bgnd", 2, one(0045372), one(0177777), "", cpu32 },
+
+{"bitrev", 2, one(0000300), one(0177770), "Ds", mcfisa_aa},
+
+{"bkpt", 2, one(0044110), one(0177770), "ts", m68010up },
+
+{"braw", 2, one(0060000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bral", 2, one(0060377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bras", 2, one(0060000), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"bset", 2, one(0000700), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bset", 2, one(0000700), one(0170700), "Ddvs", mcfisa_a },
+{"bset", 4, one(0004300), one(0177700), "#b$s", m68000up },
+{"bset", 4, one(0004300), one(0177700), "#bqs", mcfisa_a },
+
+{"bsrw", 2, one(0060400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bsrl", 2, one(0060777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bsrs", 2, one(0060400), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"btst", 2, one(0000400), one(0170700), "Dd;b", m68000up | mcfisa_a },
+{"btst", 4, one(0004000), one(0177700), "#b@s", m68000up },
+{"btst", 4, one(0004000), one(0177700), "#bqs", mcfisa_a },
+
+{"byterev", 2, one(0001300), one(0177770), "Ds", mcfisa_aa},
+
+{"callm", 4, one(0003300), one(0177700), "#b!s", m68020 },
+
+{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up },
+{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up },
+{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up },
+{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up },
+
+{"casb", 4, two(0005300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casw", 4, two(0006300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casl", 4, two(0007300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+
+{"chk2b", 4, two(0000300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"chk2w", 4, two(0001300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"chk2l", 4, two(0002300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+
+{"chkl", 2, one(0040400), one(0170700), ";lDd", m68000up },
+{"chkw", 2, one(0040600), one(0170700), ";wDd", m68000up },
+
+#define SCOPE_LINE (0x1 << 3)
+#define SCOPE_PAGE (0x2 << 3)
+#define SCOPE_ALL (0x3 << 3)
+
+{"cinva", 2, one(0xf400|SCOPE_ALL), one(0xff38), "ce", m68040up },
+{"cinvl", 2, one(0xf400|SCOPE_LINE), one(0xff38), "ceas", m68040up },
+{"cinvp", 2, one(0xf400|SCOPE_PAGE), one(0xff38), "ceas", m68040up },
+
+{"cpusha", 2, one(0xf420|SCOPE_ALL), one(0xff38), "ce", m68040up },
+{"cpushl", 2, one(0xf420|SCOPE_LINE), one(0xff38), "ceas", m68040up | mcfisa_a },
+{"cpushp", 2, one(0xf420|SCOPE_PAGE), one(0xff38), "ceas", m68040up },
+
+#undef SCOPE_LINE
+#undef SCOPE_PAGE
+#undef SCOPE_ALL
+
+{"clrb", 2, one(0041000), one(0177700), "$s", m68000up | mcfisa_a },
+{"clrw", 2, one(0041100), one(0177700), "$s", m68000up | mcfisa_a },
+{"clrl", 2, one(0041200), one(0177700), "$s", m68000up | mcfisa_a },
+
+{"cmp2b", 4, two(0000300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"cmp2w", 4, two(0001300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"cmp2l", 4, two(0002300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+
+{"cmpaw", 2, one(0130300), one(0170700), "*wAd", m68000up },
+{"cmpal", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a },
+
+{"cmpib", 4, one(0006000), one(0177700), "#b@s", m68000up },
+{"cmpib", 4, one(0006000), one(0177700), "#bDs", mcfisa_b },
+{"cmpiw", 4, one(0006100), one(0177700), "#w@s", m68000up },
+{"cmpiw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b },
+{"cmpil", 6, one(0006200), one(0177700), "#l@s", m68000up },
+{"cmpil", 6, one(0006200), one(0177700), "#lDs", mcfisa_a },
+
+{"cmpmb", 2, one(0130410), one(0170770), "+s+d", m68000up },
+{"cmpmw", 2, one(0130510), one(0170770), "+s+d", m68000up },
+{"cmpml", 2, one(0130610), one(0170770), "+s+d", m68000up },
+
+/* The cmp opcode can generate the cmpa, cmpm, and cmpi instructions. */
+{"cmpb", 4, one(0006000), one(0177700), "#b@s", m68000up },
+{"cmpb", 4, one(0006000), one(0177700), "#bDs", mcfisa_b },
+{"cmpb", 2, one(0130410), one(0170770), "+s+d", m68000up },
+{"cmpb", 2, one(0130000), one(0170700), ";bDd", m68000up },
+{"cmpb", 2, one(0130000), one(0170700), "*bDd", mcfisa_b },
+{"cmpw", 2, one(0130300), one(0170700), "*wAd", m68000up },
+{"cmpw", 4, one(0006100), one(0177700), "#w@s", m68000up },
+{"cmpw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b },
+{"cmpw", 2, one(0130510), one(0170770), "+s+d", m68000up },
+{"cmpw", 2, one(0130100), one(0170700), "*wDd", m68000up | mcfisa_b },
+{"cmpl", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"cmpl", 6, one(0006200), one(0177700), "#l@s", m68000up },
+{"cmpl", 6, one(0006200), one(0177700), "#lDs", mcfisa_a },
+{"cmpl", 2, one(0130610), one(0170770), "+s+d", m68000up },
+{"cmpl", 2, one(0130200), one(0170700), "*lDd", m68000up | mcfisa_a },
+
+{"dbcc", 2, one(0052310), one(0177770), "DsBw", m68000up },
+{"dbcs", 2, one(0052710), one(0177770), "DsBw", m68000up },
+{"dbeq", 2, one(0053710), one(0177770), "DsBw", m68000up },
+{"dbf", 2, one(0050710), one(0177770), "DsBw", m68000up },
+{"dbge", 2, one(0056310), one(0177770), "DsBw", m68000up },
+{"dbgt", 2, one(0057310), one(0177770), "DsBw", m68000up },
+{"dbhi", 2, one(0051310), one(0177770), "DsBw", m68000up },
+{"dble", 2, one(0057710), one(0177770), "DsBw", m68000up },
+{"dbls", 2, one(0051710), one(0177770), "DsBw", m68000up },
+{"dblt", 2, one(0056710), one(0177770), "DsBw", m68000up },
+{"dbmi", 2, one(0055710), one(0177770), "DsBw", m68000up },
+{"dbne", 2, one(0053310), one(0177770), "DsBw", m68000up },
+{"dbpl", 2, one(0055310), one(0177770), "DsBw", m68000up },
+{"dbt", 2, one(0050310), one(0177770), "DsBw", m68000up },
+{"dbvc", 2, one(0054310), one(0177770), "DsBw", m68000up },
+{"dbvs", 2, one(0054710), one(0177770), "DsBw", m68000up },
+
+{"divsw", 2, one(0100700), one(0170700), ";wDd", m68000up | mcfhwdiv },
+
+{"divsl", 4, two(0046100,0006000),two(0177700,0107770),";lD3D1", m68020up|cpu32 },
+{"divsl", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+{"divsl", 4, two(0046100,0004000),two(0177700,0107770),"qsDD", mcfhwdiv },
+
+{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lD3D1",m68020up|cpu32 },
+{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+
+{"divuw", 2, one(0100300), one(0170700), ";wDd", m68000up | mcfhwdiv },
+
+{"divul", 4, two(0046100,0002000),two(0177700,0107770),";lD3D1", m68020up|cpu32 },
+{"divul", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+{"divul", 4, two(0046100,0000000),two(0177700,0107770),"qsDD", mcfhwdiv },
+
+{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lD3D1",m68020up|cpu32 },
+{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+
+{"eorib", 4, one(0005000), one(0177700), "#b$s", m68000up },
+{"eorib", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eoriw", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eoriw", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eoril", 6, one(0005200), one(0177700), "#l$s", m68000up },
+{"eoril", 6, one(0005200), one(0177700), "#lDs", mcfisa_a },
+{"eori", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eori", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eori", 4, one(0005100), one(0177700), "#w$s", m68000up },
+
+/* The eor opcode can generate the eori instruction. */
+{"eorb", 4, one(0005000), one(0177700), "#b$s", m68000up },
+{"eorb", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eorb", 2, one(0130400), one(0170700), "Dd$s", m68000up },
+{"eorw", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eorw", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eorw", 2, one(0130500), one(0170700), "Dd$s", m68000up },
+{"eorl", 6, one(0005200), one(0177700), "#l$s", m68000up },
+{"eorl", 6, one(0005200), one(0177700), "#lDs", mcfisa_a },
+{"eorl", 2, one(0130600), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"eor", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eor", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eor", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eor", 2, one(0130500), one(0170700), "Dd$s", m68000up },
+
+{"exg", 2, one(0140500), one(0170770), "DdDs", m68000up },
+{"exg", 2, one(0140510), one(0170770), "AdAs", m68000up },
+{"exg", 2, one(0140610), one(0170770), "DdAs", m68000up },
+{"exg", 2, one(0140610), one(0170770), "AsDd", m68000up },
+
+{"extw", 2, one(0044200), one(0177770), "Ds", m68000up|mcfisa_a },
+{"extl", 2, one(0044300), one(0177770), "Ds", m68000up|mcfisa_a },
+{"extbl", 2, one(0044700), one(0177770), "Ds", m68020up|cpu32|mcfisa_a },
+
+{"ff1", 2, one(0002300), one(0177770), "Ds", mcfisa_aa},
+
+/* float stuff starts here */
+
+{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsp", 4, two(0xF000, 0x4C18), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", cfloat },
+{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fabsx", 4, two(0xF000, 0x4818), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsp", 4, two(0xF000, 0x4C58), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsabss", 4, two(0xF000, 0x4258), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabss", 4, two(0xF000, 0x4458), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsabsx", 4, two(0xF000, 0x4858), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdabsb", 4, two(0xF000, 0x585C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsb", 4, two(0xF000, 0x585c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up},
+{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdabsd", 4, two(0xF000, 0x545C), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdabsd", 4, two(0xF000, 0x545c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up},
+{"fdabsl", 4, two(0xF000, 0x405C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsl", 4, two(0xF000, 0x405c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up},
+{"fdabsp", 4, two(0xF000, 0x4C5c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up},
+{"fdabss", 4, two(0xF000, 0x425C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabss", 4, two(0xF000, 0x445c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up},
+{"fdabsw", 4, two(0xF000, 0x505C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsw", 4, two(0xF000, 0x505c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x485c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiFt", m68040up},
+
+{"facosb", 4, two(0xF000, 0x581C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"facosd", 4, two(0xF000, 0x541C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"facosl", 4, two(0xF000, 0x401C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"facosp", 4, two(0xF000, 0x4C1C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"facoss", 4, two(0xF000, 0x441C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"facosw", 4, two(0xF000, 0x501C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"facosx", 4, two(0xF000, 0x481C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddd", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddp", 4, two(0xF000, 0x4C22), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddx", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"faddx", 4, two(0xF000, 0x4822), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddp", 4, two(0xF000, 0x4C62), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsadds", 4, two(0xF000, 0x4462), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsadds", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddx", 4, two(0xF000, 0x0062), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsaddx", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fdaddb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddb", 4, two(0xF000, 0x5866), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdaddd", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddd", 4, two(0xF000, 0x5466), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdaddl", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdaddl", 4, two(0xF000, 0x4066), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdaddp", 4, two(0xF000, 0x4C66), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdadds", 4, two(0xF000, 0x4466), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdadds", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddw", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddw", 4, two(0xF000, 0x5066), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdaddx", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdaddx", 4, two(0xF000, 0x4866), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fasinb", 4, two(0xF000, 0x580C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fasind", 4, two(0xF000, 0x540C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fasinl", 4, two(0xF000, 0x400C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fasinp", 4, two(0xF000, 0x4C0C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fasins", 4, two(0xF000, 0x440C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fasinw", 4, two(0xF000, 0x500C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fasinx", 4, two(0xF000, 0x480C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanb", 4, two(0xF000, 0x580A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatand", 4, two(0xF000, 0x540A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanl", 4, two(0xF000, 0x400A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanp", 4, two(0xF000, 0x4C0A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatans", 4, two(0xF000, 0x440A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanw", 4, two(0xF000, 0x500A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanx", 4, two(0xF000, 0x480A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanhb", 4, two(0xF000, 0x580D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatanhd", 4, two(0xF000, 0x540D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanhl", 4, two(0xF000, 0x400D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanhp", 4, two(0xF000, 0x4C0D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatanhs", 4, two(0xF000, 0x440D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanhw", 4, two(0xF000, 0x500D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x480D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fbeq", 2, one(0xF081), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbf", 2, one(0xF080), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbge", 2, one(0xF093), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgl", 2, one(0xF096), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgle", 2, one(0xF097), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgt", 2, one(0xF092), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fble", 2, one(0xF095), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fblt", 2, one(0xF094), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbne", 2, one(0xF08E), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnge", 2, one(0xF09C), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngl", 2, one(0xF099), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngle", 2, one(0xF098), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngt", 2, one(0xF09D), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnle", 2, one(0xF09A), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnlt", 2, one(0xF09B), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fboge", 2, one(0xF083), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbogl", 2, one(0xF086), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbogt", 2, one(0xF082), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbole", 2, one(0xF085), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbolt", 2, one(0xF084), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbor", 2, one(0xF087), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbseq", 2, one(0xF091), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbsf", 2, one(0xF090), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbsne", 2, one(0xF09E), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbst", 2, one(0xF09F), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbt", 2, one(0xF08F), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbueq", 2, one(0xF089), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbuge", 2, one(0xF08B), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbugt", 2, one(0xF08A), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbule", 2, one(0xF08D), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbult", 2, one(0xF08C), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbun", 2, one(0xF088), one(0xF1FF), "IdBW", mfloat | cfloat },
+
+{"fbeql", 2, one(0xF0C1), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbfl", 2, one(0xF0C0), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgel", 2, one(0xF0D3), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgll", 2, one(0xF0D6), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbglel", 2, one(0xF0D7), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgtl", 2, one(0xF0D2), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fblel", 2, one(0xF0D5), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbltl", 2, one(0xF0D4), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnel", 2, one(0xF0CE), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngel", 2, one(0xF0DC), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngll", 2, one(0xF0D9), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnglel", 2, one(0xF0D8), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngtl", 2, one(0xF0DD), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnlel", 2, one(0xF0DA), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnltl", 2, one(0xF0DB), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogel", 2, one(0xF0C3), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogll", 2, one(0xF0C6), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogtl", 2, one(0xF0C2), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbolel", 2, one(0xF0C5), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fboltl", 2, one(0xF0C4), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fborl", 2, one(0xF0C7), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbseql", 2, one(0xF0D1), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbsfl", 2, one(0xF0D0), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbsnel", 2, one(0xF0DE), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbstl", 2, one(0xF0DF), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbtl", 2, one(0xF0CF), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbueql", 2, one(0xF0C9), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbugel", 2, one(0xF0CB), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbugtl", 2, one(0xF0CA), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbulel", 2, one(0xF0CD), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbultl", 2, one(0xF0CC), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbunl", 2, one(0xF0C8), one(0xF1FF), "IdBC", mfloat | cfloat },
+
+{"fjeq", 2, one(0xF081), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjf", 2, one(0xF080), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjge", 2, one(0xF093), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgl", 2, one(0xF096), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgle", 2, one(0xF097), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgt", 2, one(0xF092), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjle", 2, one(0xF095), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjlt", 2, one(0xF094), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjne", 2, one(0xF08E), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnge", 2, one(0xF09C), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngl", 2, one(0xF099), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngle", 2, one(0xF098), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngt", 2, one(0xF09D), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnle", 2, one(0xF09A), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnlt", 2, one(0xF09B), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjoge", 2, one(0xF083), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjogl", 2, one(0xF086), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjogt", 2, one(0xF082), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjole", 2, one(0xF085), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjolt", 2, one(0xF084), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjor", 2, one(0xF087), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjseq", 2, one(0xF091), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjsf", 2, one(0xF090), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjsne", 2, one(0xF09E), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjst", 2, one(0xF09F), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjt", 2, one(0xF08F), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjueq", 2, one(0xF089), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjuge", 2, one(0xF08B), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjugt", 2, one(0xF08A), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjule", 2, one(0xF08D), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjult", 2, one(0xF08C), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjun", 2, one(0xF088), one(0xF1BF), "IdBc", mfloat | cfloat },
+
+{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fcmpd", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpp", 4, two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpx", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcmpx", 4, two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fcosb", 4, two(0xF000, 0x581D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcosd", 4, two(0xF000, 0x541D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcosl", 4, two(0xF000, 0x401D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcosp", 4, two(0xF000, 0x4C1D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoss", 4, two(0xF000, 0x441D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcosw", 4, two(0xF000, 0x501D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcosx", 4, two(0xF000, 0x481D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fcoshb", 4, two(0xF000, 0x5819), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcoshd", 4, two(0xF000, 0x5419), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcoshl", 4, two(0xF000, 0x4019), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcoshp", 4, two(0xF000, 0x4C19), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoshs", 4, two(0xF000, 0x4419), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcoshw", 4, two(0xF000, 0x5019), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x4819), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fdbeq", 4, two(0xF048, 0x0001), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbf", 4, two(0xF048, 0x0000), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbge", 4, two(0xF048, 0x0013), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgl", 4, two(0xF048, 0x0016), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgle", 4, two(0xF048, 0x0017), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgt", 4, two(0xF048, 0x0012), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdble", 4, two(0xF048, 0x0015), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdblt", 4, two(0xF048, 0x0014), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbne", 4, two(0xF048, 0x000E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnge", 4, two(0xF048, 0x001C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngl", 4, two(0xF048, 0x0019), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngle", 4, two(0xF048, 0x0018), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngt", 4, two(0xF048, 0x001D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnle", 4, two(0xF048, 0x001A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnlt", 4, two(0xF048, 0x001B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdboge", 4, two(0xF048, 0x0003), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogl", 4, two(0xF048, 0x0006), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogt", 4, two(0xF048, 0x0002), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbole", 4, two(0xF048, 0x0005), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbolt", 4, two(0xF048, 0x0004), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbor", 4, two(0xF048, 0x0007), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbseq", 4, two(0xF048, 0x0011), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsf", 4, two(0xF048, 0x0010), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsne", 4, two(0xF048, 0x001E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbst", 4, two(0xF048, 0x001F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbt", 4, two(0xF048, 0x000F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbueq", 4, two(0xF048, 0x0009), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbuge", 4, two(0xF048, 0x000B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbugt", 4, two(0xF048, 0x000A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbule", 4, two(0xF048, 0x000D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbult", 4, two(0xF048, 0x000C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbun", 4, two(0xF048, 0x0008), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+
+{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivd", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivp", 4, two(0xF000, 0x4C20), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivx", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fdivx", 4, two(0xF000, 0x4820), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivd", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivp", 4, two(0xF000, 0x4C60), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivx", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsdivx", 4, two(0xF000, 0x4860), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivd", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivp", 4, two(0xF000, 0x4C64), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivx", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fddivx", 4, two(0xF000, 0x4864), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fetoxb", 4, two(0xF000, 0x5810), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxd", 4, two(0xF000, 0x5410), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxl", 4, two(0xF000, 0x4010), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxp", 4, two(0xF000, 0x4C10), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxs", 4, two(0xF000, 0x4410), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxw", 4, two(0xF000, 0x5010), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x4810), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fetoxm1b", 4, two(0xF000, 0x5808), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxm1d", 4, two(0xF000, 0x5408), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxm1l", 4, two(0xF000, 0x4008), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxm1p", 4, two(0xF000, 0x4C08), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxm1s", 4, two(0xF000, 0x4408), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxm1w", 4, two(0xF000, 0x5008), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x4808), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetexpb", 4, two(0xF000, 0x581E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetexpd", 4, two(0xF000, 0x541E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetexpl", 4, two(0xF000, 0x401E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetexpp", 4, two(0xF000, 0x4C1E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetexps", 4, two(0xF000, 0x441E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetexpw", 4, two(0xF000, 0x501E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x481E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetmanb", 4, two(0xF000, 0x581F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetmand", 4, two(0xF000, 0x541F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetmanl", 4, two(0xF000, 0x401F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetmanp", 4, two(0xF000, 0x4C1F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetmans", 4, two(0xF000, 0x441F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetmanw", 4, two(0xF000, 0x501F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x481F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintp", 4, two(0xF000, 0x4C01), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintx", 4, two(0xF000, 0x4801), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzp", 4, two(0xF000, 0x4C03), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintrzx", 4, two(0xF000, 0x4803), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog10b", 4, two(0xF000, 0x5815), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog10d", 4, two(0xF000, 0x5415), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog10l", 4, two(0xF000, 0x4015), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog10p", 4, two(0xF000, 0x4C15), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog10s", 4, two(0xF000, 0x4415), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog10w", 4, two(0xF000, 0x5015), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog10x", 4, two(0xF000, 0x4815), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog2b", 4, two(0xF000, 0x5816), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog2d", 4, two(0xF000, 0x5416), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog2l", 4, two(0xF000, 0x4016), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog2p", 4, two(0xF000, 0x4C16), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog2s", 4, two(0xF000, 0x4416), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog2w", 4, two(0xF000, 0x5016), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog2x", 4, two(0xF000, 0x4816), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognb", 4, two(0xF000, 0x5814), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognd", 4, two(0xF000, 0x5414), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognl", 4, two(0xF000, 0x4014), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp", 4, two(0xF000, 0x4C14), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flogns", 4, two(0xF000, 0x4414), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognw", 4, two(0xF000, 0x5014), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognx", 4, two(0xF000, 0x4814), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognp1b", 4, two(0xF000, 0x5806), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognp1d", 4, two(0xF000, 0x5406), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognp1l", 4, two(0xF000, 0x4006), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp1p", 4, two(0xF000, 0x4C06), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flognp1s", 4, two(0xF000, 0x4406), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognp1w", 4, two(0xF000, 0x5006), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x4806), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fmodb", 4, two(0xF000, 0x5821), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmodd", 4, two(0xF000, 0x5421), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmodl", 4, two(0xF000, 0x4021), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmodp", 4, two(0xF000, 0x4C21), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmods", 4, two(0xF000, 0x4421), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmodw", 4, two(0xF000, 0x5021), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmodx", 4, two(0xF000, 0x0021), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmodx", 4, two(0xF000, 0x4821), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat },
+{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7$b", mfloat },
+{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7~F", mfloat },
+{"fmoved", 4, two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat },
+{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7$l", mfloat },
+/* FIXME: the next two variants should not permit moving an address
+ register to anything but the floating point instruction register. */
+{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ls8", mfloat },
+{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat },
+ /* Move the FP control registers. */
+{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8ps", cfloat },
+{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Iibss8", cfloat },
+{"fmovep", 4, two(0xF000, 0x4C00), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmovep", 4, two(0xF000, 0x6C00), two(0xF1C0, 0xFC00), "IiF7~pkC", mfloat },
+{"fmovep", 4, two(0xF000, 0x7C00), two(0xF1C0, 0xFC0F), "IiF7~pDk", mfloat },
+{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7$f", mfloat },
+{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7$w", mfloat },
+{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fmovex", 4, two(0xF000, 0x0000), two(0xF1FF, 0xE07F), "IiF8F7", mfloat },
+{"fmovex", 4, two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fmovex", 4, two(0xF000, 0x6800), two(0xF1C0, 0xFC7F), "IiF7~x", mfloat },
+
+{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmoveb", 4, two(0xF000, 0x7840), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmoved", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsmoved", 4, two(0xF000, 0x7440), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat },
+{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmovel", 4, two(0xF000, 0x6040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmoves", 4, two(0xF000, 0x6440), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmovew", 4, two(0xF000, 0x7040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmovex", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsmovex", 4, two(0xF000, 0x4840), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsmovep", 4, two(0xF000, 0x4C40), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+
+{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmoveb", 4, two(0xF000, 0x7844), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmoved", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdmoved", 4, two(0xF000, 0x7444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmovel", 4, two(0xF000, 0x6044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmoves", 4, two(0xF000, 0x6444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmovew", 4, two(0xF000, 0x7044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovex", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdmovex", 4, two(0xF000, 0x4844), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdmovep", 4, two(0xF000, 0x4C44), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+
+{"fmovecrx", 4, two(0xF000, 0x5C00), two(0xF1FF, 0xFC00), "Ii#CF7", mfloat },
+
+{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizsl3", cfloat },
+{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat },
+{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat },
+{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Iil3ys", cfloat },
+
+{"fmovemx", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat },
+{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat },
+
+{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat },
+/* FIXME: In the next instruction, we should only permit %dn if the
+ target is a single register. We should only permit %an if the
+ target is a single %fpiar. */
+{"fmoveml", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*lL8", mfloat },
+
+{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "IizsL3", cfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "IiL3ys", cfloat },
+
+{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat },
+{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat },
+{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat },
+{"fmovem", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat },
+{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat },
+{"fmovem", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat },
+{"fmovem", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat },
+{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmovem", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ss8", mfloat },
+{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat },
+{"fmovem", 4, two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*sL8", mfloat },
+
+{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmuld", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulp", 4, two(0xF000, 0x4C23), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulx", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmulx", 4, two(0xF000, 0x4823), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmuld", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulp", 4, two(0xF000, 0x4C63), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulx", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsmulx", 4, two(0xF000, 0x4863), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmuld", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulp", 4, two(0xF000, 0x4C67), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulx", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdmulx", 4, two(0xF000, 0x4867), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegp", 4, two(0xF000, 0x4C1A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fnegx", 4, two(0xF000, 0x481A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegp", 4, two(0xF000, 0x4C5A), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsnegx", 4, two(0xF000, 0x485A), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegp", 4, two(0xF000, 0x4C5E), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdnegx", 4, two(0xF000, 0x485E), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fnop", 4, two(0xF280, 0x0000), two(0xFFFF, 0xFFFF), "Ii", mfloat | cfloat },
+
+{"fremb", 4, two(0xF000, 0x5825), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fremd", 4, two(0xF000, 0x5425), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"freml", 4, two(0xF000, 0x4025), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fremp", 4, two(0xF000, 0x4C25), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"frems", 4, two(0xF000, 0x4425), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fremw", 4, two(0xF000, 0x5025), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fremx", 4, two(0xF000, 0x0025), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fremx", 4, two(0xF000, 0x4825), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"frestore", 2, one(0xF140), one(0xF1C0), "Id<s", mfloat },
+{"frestore", 2, one(0xF140), one(0xF1C0), "Idys", cfloat },
+
+{"fsave", 2, one(0xF100), one(0xF1C0), "Id>s", mfloat },
+{"fsave", 2, one(0xF100), one(0xF1C0), "Idzs", cfloat },
+
+{"fscaleb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fscaled", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fscalel", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fscalep", 4, two(0xF000, 0x4C26), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fscales", 4, two(0xF000, 0x4426), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fscalew", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fscalex", 4, two(0xF000, 0x0026), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fscalex", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+/* $ is necessary to prevent the assembler from using PC-relative.
+ If @ were used, "label: fseq label" could produce "ftrapeq", 2,
+ because "label" became "pc@label". */
+{"fseq", 4, two(0xF040, 0x0001), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsf", 4, two(0xF040, 0x0000), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsge", 4, two(0xF040, 0x0013), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgl", 4, two(0xF040, 0x0016), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgle", 4, two(0xF040, 0x0017), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgt", 4, two(0xF040, 0x0012), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsle", 4, two(0xF040, 0x0015), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fslt", 4, two(0xF040, 0x0014), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsne", 4, two(0xF040, 0x000E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnge", 4, two(0xF040, 0x001C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngl", 4, two(0xF040, 0x0019), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngle", 4, two(0xF040, 0x0018), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngt", 4, two(0xF040, 0x001D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnle", 4, two(0xF040, 0x001A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnlt", 4, two(0xF040, 0x001B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsoge", 4, two(0xF040, 0x0003), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogl", 4, two(0xF040, 0x0006), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogt", 4, two(0xF040, 0x0002), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsole", 4, two(0xF040, 0x0005), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsolt", 4, two(0xF040, 0x0004), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsor", 4, two(0xF040, 0x0007), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsseq", 4, two(0xF040, 0x0011), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssf", 4, two(0xF040, 0x0010), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssne", 4, two(0xF040, 0x001E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsst", 4, two(0xF040, 0x001F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fst", 4, two(0xF040, 0x000F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsueq", 4, two(0xF040, 0x0009), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsuge", 4, two(0xF040, 0x000B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsugt", 4, two(0xF040, 0x000A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsule", 4, two(0xF040, 0x000D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsult", 4, two(0xF040, 0x000C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsun", 4, two(0xF040, 0x0008), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+
+{"fsgldivb", 4, two(0xF000, 0x5824), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsgldivd", 4, two(0xF000, 0x5424), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsgldivl", 4, two(0xF000, 0x4024), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsgldivp", 4, two(0xF000, 0x4C24), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsgldivs", 4, two(0xF000, 0x4424), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsgldivw", 4, two(0xF000, 0x5024), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x4824), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsglmulb", 4, two(0xF000, 0x5827), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsglmuld", 4, two(0xF000, 0x5427), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsglmull", 4, two(0xF000, 0x4027), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsglmulp", 4, two(0xF000, 0x4C27), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsglmuls", 4, two(0xF000, 0x4427), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsglmulw", 4, two(0xF000, 0x5027), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x4827), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsinb", 4, two(0xF000, 0x580E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsind", 4, two(0xF000, 0x540E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinl", 4, two(0xF000, 0x400E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinp", 4, two(0xF000, 0x4C0E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsins", 4, two(0xF000, 0x440E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinw", 4, two(0xF000, 0x500E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinx", 4, two(0xF000, 0x480E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsincosb", 4, two(0xF000, 0x5830), two(0xF1C0, 0xFC78), "Ii;bF3F7", mfloat },
+{"fsincosd", 4, two(0xF000, 0x5430), two(0xF1C0, 0xFC78), "Ii;FF3F7", mfloat },
+{"fsincosl", 4, two(0xF000, 0x4030), two(0xF1C0, 0xFC78), "Ii;lF3F7", mfloat },
+{"fsincosp", 4, two(0xF000, 0x4C30), two(0xF1C0, 0xFC78), "Ii;pF3F7", mfloat },
+{"fsincoss", 4, two(0xF000, 0x4430), two(0xF1C0, 0xFC78), "Ii;fF3F7", mfloat },
+{"fsincosw", 4, two(0xF000, 0x5030), two(0xF1C0, 0xFC78), "Ii;wF3F7", mfloat },
+{"fsincosx", 4, two(0xF000, 0x0030), two(0xF1C0, 0xE078), "IiF8F3F7", mfloat },
+{"fsincosx", 4, two(0xF000, 0x4830), two(0xF1C0, 0xFC78), "Ii;xF3F7", mfloat },
+
+{"fsinhb", 4, two(0xF000, 0x5802), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsinhd", 4, two(0xF000, 0x5402), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinhl", 4, two(0xF000, 0x4002), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinhp", 4, two(0xF000, 0x4C02), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsinhs", 4, two(0xF000, 0x4402), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinhw", 4, two(0xF000, 0x5002), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x4802), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtp", 4, two(0xF000, 0x4C04), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsqrtx", 4, two(0xF000, 0x4804), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtp", 4, two(0xF000, 0x4C41), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fssqrtx", 4, two(0xF000, 0x4841), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x5445), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtp", 4, two(0xF000, 0x4C45), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdsqrtx", 4, two(0xF000, 0x4845), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubd", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubp", 4, two(0xF000, 0x4C28), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsubx", 4, two(0xF000, 0x4828), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubb", 4, two(0xF000, 0x5868), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fssubd", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubp", 4, two(0xF000, 0x4C68), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fssubx", 4, two(0xF000, 0x4868), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdsubb", 4, two(0xF000, 0x586A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubb", 4, two(0xF000, 0x586c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdsubd", 4, two(0xF000, 0x006A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdsubd", 4, two(0xF000, 0x546A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdsubd", 4, two(0xF000, 0x546c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdsubl", 4, two(0xF000, 0x406A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubl", 4, two(0xF000, 0x406c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdsubp", 4, two(0xF000, 0x4C6c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdsubs", 4, two(0xF000, 0x446A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubs", 4, two(0xF000, 0x446c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdsubw", 4, two(0xF000, 0x506A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubw", 4, two(0xF000, 0x506c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x486c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"ftanb", 4, two(0xF000, 0x580F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftand", 4, two(0xF000, 0x540F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanl", 4, two(0xF000, 0x400F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanp", 4, two(0xF000, 0x4C0F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftans", 4, two(0xF000, 0x440F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanw", 4, two(0xF000, 0x500F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanx", 4, two(0xF000, 0x480F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftanhb", 4, two(0xF000, 0x5809), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftanhd", 4, two(0xF000, 0x5409), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanhl", 4, two(0xF000, 0x4009), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanhp", 4, two(0xF000, 0x4C09), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftanhs", 4, two(0xF000, 0x4409), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanhw", 4, two(0xF000, 0x5009), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x4809), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftentoxb", 4, two(0xF000, 0x5812), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftentoxd", 4, two(0xF000, 0x5412), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftentoxl", 4, two(0xF000, 0x4012), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftentoxp", 4, two(0xF000, 0x4C12), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftentoxs", 4, two(0xF000, 0x4412), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftentoxw", 4, two(0xF000, 0x5012), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x4812), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftrapeq", 4, two(0xF07C, 0x0001), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapf", 4, two(0xF07C, 0x0000), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapge", 4, two(0xF07C, 0x0013), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgl", 4, two(0xF07C, 0x0016), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgle", 4, two(0xF07C, 0x0017), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgt", 4, two(0xF07C, 0x0012), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraple", 4, two(0xF07C, 0x0015), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraplt", 4, two(0xF07C, 0x0014), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapne", 4, two(0xF07C, 0x000E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnge", 4, two(0xF07C, 0x001C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngl", 4, two(0xF07C, 0x0019), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngle", 4,two(0xF07C, 0x0018), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngt", 4, two(0xF07C, 0x001D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnle", 4, two(0xF07C, 0x001A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnlt", 4, two(0xF07C, 0x001B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapoge", 4, two(0xF07C, 0x0003), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogl", 4, two(0xF07C, 0x0006), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogt", 4, two(0xF07C, 0x0002), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapole", 4, two(0xF07C, 0x0005), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapolt", 4, two(0xF07C, 0x0004), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapor", 4, two(0xF07C, 0x0007), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapseq", 4, two(0xF07C, 0x0011), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsf", 4, two(0xF07C, 0x0010), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsne", 4, two(0xF07C, 0x001E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapst", 4, two(0xF07C, 0x001F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapt", 4, two(0xF07C, 0x000F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapueq", 4, two(0xF07C, 0x0009), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapuge", 4, two(0xF07C, 0x000B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapugt", 4, two(0xF07C, 0x000A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapule", 4, two(0xF07C, 0x000D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapult", 4, two(0xF07C, 0x000C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapun", 4, two(0xF07C, 0x0008), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+
+{"ftrapeqw", 4, two(0xF07A, 0x0001), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapfw", 4, two(0xF07A, 0x0000), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgew", 4, two(0xF07A, 0x0013), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglw", 4, two(0xF07A, 0x0016), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglew", 4,two(0xF07A, 0x0017), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgtw", 4, two(0xF07A, 0x0012), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraplew", 4, two(0xF07A, 0x0015), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapltw", 4, two(0xF07A, 0x0014), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnew", 4, two(0xF07A, 0x000E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngew", 4,two(0xF07A, 0x001C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglw", 4,two(0xF07A, 0x0019), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglew", 4,two(0xF07A, 0x0018), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngtw", 4,two(0xF07A, 0x001D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnlew", 4,two(0xF07A, 0x001A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnltw", 4,two(0xF07A, 0x001B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogew", 4,two(0xF07A, 0x0003), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoglw", 4,two(0xF07A, 0x0006), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogtw", 4,two(0xF07A, 0x0002), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapolew", 4,two(0xF07A, 0x0005), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoltw", 4,two(0xF07A, 0x0004), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraporw", 4, two(0xF07A, 0x0007), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapseqw", 4,two(0xF07A, 0x0011), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsfw", 4, two(0xF07A, 0x0010), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsnew", 4,two(0xF07A, 0x001E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapstw", 4, two(0xF07A, 0x001F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraptw", 4, two(0xF07A, 0x000F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapueqw", 4,two(0xF07A, 0x0009), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugew", 4,two(0xF07A, 0x000B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugtw", 4,two(0xF07A, 0x000A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapulew", 4,two(0xF07A, 0x000D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapultw", 4,two(0xF07A, 0x000C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapunw", 4, two(0xF07A, 0x0008), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+
+{"ftrapeql", 4, two(0xF07B, 0x0001), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapfl", 4, two(0xF07B, 0x0000), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgel", 4, two(0xF07B, 0x0013), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgll", 4, two(0xF07B, 0x0016), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapglel", 4,two(0xF07B, 0x0017), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgtl", 4, two(0xF07B, 0x0012), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraplel", 4, two(0xF07B, 0x0015), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapltl", 4, two(0xF07B, 0x0014), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnel", 4, two(0xF07B, 0x000E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngel", 4,two(0xF07B, 0x001C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngll", 4,two(0xF07B, 0x0019), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnglel", 4,two(0xF07B, 0x0018), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngtl", 4,two(0xF07B, 0x001D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnlel", 4,two(0xF07B, 0x001A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnltl", 4,two(0xF07B, 0x001B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogel", 4,two(0xF07B, 0x0003), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogll", 4,two(0xF07B, 0x0006), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogtl", 4,two(0xF07B, 0x0002), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapolel", 4,two(0xF07B, 0x0005), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapoltl", 4,two(0xF07B, 0x0004), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraporl", 4, two(0xF07B, 0x0007), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapseql", 4,two(0xF07B, 0x0011), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsfl", 4, two(0xF07B, 0x0010), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsnel", 4,two(0xF07B, 0x001E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapstl", 4, two(0xF07B, 0x001F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraptl", 4, two(0xF07B, 0x000F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapueql", 4,two(0xF07B, 0x0009), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugel", 4,two(0xF07B, 0x000B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugtl", 4,two(0xF07B, 0x000A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapulel", 4,two(0xF07B, 0x000D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapultl", 4,two(0xF07B, 0x000C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapunl", 4, two(0xF07B, 0x0008), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+
+{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Ii;b", mfloat },
+{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstd", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", cfloat },
+{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Ii;F", mfloat },
+{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Ii;l", mfloat },
+{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstp", 4, two(0xF000, 0x4C3A), two(0xF1C0, 0xFC7F), "Ii;p", mfloat },
+{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Ii;f", mfloat },
+{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Ii;w", mfloat },
+{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstx", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", mfloat },
+{"ftstx", 4, two(0xF000, 0x483A), two(0xF1C0, 0xFC7F), "Ii;x", mfloat },
+
+{"ftwotoxb", 4, two(0xF000, 0x5811), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftwotoxd", 4, two(0xF000, 0x5411), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftwotoxl", 4, two(0xF000, 0x4011), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftwotoxp", 4, two(0xF000, 0x4C11), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftwotoxs", 4, two(0xF000, 0x4411), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftwotoxw", 4, two(0xF000, 0x5011), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x4811), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"halt", 2, one(0045310), one(0177777), "", m68060 | mcfisa_a },
+
+{"illegal", 2, one(0045374), one(0177777), "", m68000up | mcfisa_a },
+{"intouch", 2, one(0xf428), one(0xfff8), "As", mcfisa_b },
+
+{"jmp", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jra", 2, one(0060000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jra", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jbsr", 2, one(0060400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jbsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"lea", 2, one(0040700), one(0170700), "!sAd", m68000up | mcfisa_a },
+
+{"lpstop", 6, two(0174000,0000700),two(0177777,0177777),"#w", cpu32|m68060 },
+
+{"linkw", 4, one(0047120), one(0177770), "As#w", m68000up | mcfisa_a },
+{"linkl", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 },
+{"link", 4, one(0047120), one(0177770), "As#W", m68000up | mcfisa_a },
+{"link", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 },
+
+{"lslb", 2, one(0160410), one(0170770), "QdDs", m68000up },
+{"lslb", 2, one(0160450), one(0170770), "DdDs", m68000up },
+{"lslw", 2, one(0160510), one(0170770), "QdDs", m68000up },
+{"lslw", 2, one(0160550), one(0170770), "DdDs", m68000up },
+{"lslw", 2, one(0161700), one(0177700), "~s", m68000up },
+{"lsll", 2, one(0160610), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"lsll", 2, one(0160650), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"lsrb", 2, one(0160010), one(0170770), "QdDs", m68000up },
+{"lsrb", 2, one(0160050), one(0170770), "DdDs", m68000up },
+{"lsrw", 2, one(0160110), one(0170770), "QdDs", m68000up },
+{"lsrw", 2, one(0160150), one(0170770), "DdDs", m68000up },
+{"lsrw", 2, one(0161300), one(0177700), "~s", m68000up },
+{"lsrl", 2, one(0160210), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"lsrl", 2, one(0160250), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0910), "uNuoiI4/Rn", mcfmac },
+{"macw", 4, two(0xa080, 0x0200), two(0xf180, 0x0910), "uNuoMh4/Rn", mcfmac },
+{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0f10), "uNuo4/Rn", mcfmac },
+{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0900), "uMumiI", mcfmac },
+{"macw", 4, two(0xa000, 0x0200), two(0xf1b0, 0x0900), "uMumMh", mcfmac },
+{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0f00), "uMum", mcfmac },
+
+{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0900), "uNuoiI4/RneG", mcfemac },/* Ry,Rx,SF,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0200), two(0xf100, 0x0900), "uNuoMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0f00), "uNuo4/RneG", mcfemac },/* Ry,Rx,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */
+{"macw", 4, two(0xa000, 0x0200), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */
+
+{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac },
+{"macl", 4, two(0xa080, 0x0a00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac },
+{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0800), "RMRm", mcfmac },
+
+{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0900), "RMRmiIeH", mcfemac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf130, 0x0900), "RMRmMheH", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0f00), "RMRmeH", mcfemac },
+
+/* NOTE: The mcf5200 family programmer's reference manual does not
+ indicate the byte form of the movea instruction is invalid (as it
+ is on 68000 family cpus). However, experiments on the 5202 yeild
+ unexpected results. The value is copied, but it is not sign extended
+ (as is done with movea.w) and the top three bytes in the address
+ register are not disturbed. I don't know if this is the intended
+ behavior --- it could be a hole in instruction decoding (Motorola
+ decided not to trap all invalid instructions for performance reasons)
+ --- but I suspect that it is not.
+
+ I reported this to Motorola ISD Technical Communications Support,
+ which replied that other coldfire assemblers reject movea.b. For
+ this reason I've decided to not allow moveab.
+
+ jtc@cygnus.com - 97/01/24. */
+
+{"moveal", 2, one(0020100), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"moveaw", 2, one(0030100), one(0170700), "*wAd", m68000up | mcfisa_a },
+
+{"movclrl", 2, one(0xA1C0), one(0xf9f0), "eFRs", mcfemac },
+
+{"movec", 4, one(0047173), one(0177777), "R1Jj", m68010up | mcfisa_a },
+{"movec", 4, one(0047173), one(0177777), "R1#j", m68010up | mcfisa_a },
+{"movec", 4, one(0047172), one(0177777), "JjR1", m68010up },
+{"movec", 4, one(0047172), one(0177777), "#jR1", m68010up },
+
+{"movemw", 4, one(0044200), one(0177700), "Lw&s", m68000up },
+{"movemw", 4, one(0044240), one(0177770), "lw-s", m68000up },
+{"movemw", 4, one(0044200), one(0177700), "#w>s", m68000up },
+{"movemw", 4, one(0046200), one(0177700), "<sLw", m68000up },
+{"movemw", 4, one(0046200), one(0177700), "<s#w", m68000up },
+{"moveml", 4, one(0044300), one(0177700), "Lw&s", m68000up },
+{"moveml", 4, one(0044340), one(0177770), "lw-s", m68000up },
+{"moveml", 4, one(0044300), one(0177700), "#w>s", m68000up },
+{"moveml", 4, one(0046300), one(0177700), "<sLw", m68000up },
+{"moveml", 4, one(0046300), one(0177700), "<s#w", m68000up },
+/* FIXME: need specifier for mode 2 and 5 to simplify below insn patterns. */
+{"moveml", 4, one(0044320), one(0177770), "Lwas", mcfisa_a },
+{"moveml", 4, one(0044320), one(0177770), "#was", mcfisa_a },
+{"moveml", 4, one(0044350), one(0177770), "Lwds", mcfisa_a },
+{"moveml", 4, one(0044350), one(0177770), "#wds", mcfisa_a },
+{"moveml", 4, one(0046320), one(0177770), "asLw", mcfisa_a },
+{"moveml", 4, one(0046320), one(0177770), "as#w", mcfisa_a },
+{"moveml", 4, one(0046350), one(0177770), "dsLw", mcfisa_a },
+{"moveml", 4, one(0046350), one(0177770), "ds#w", mcfisa_a },
+
+{"movepw", 2, one(0000410), one(0170770), "dsDd", m68000up },
+{"movepw", 2, one(0000610), one(0170770), "Ddds", m68000up },
+{"movepl", 2, one(0000510), one(0170770), "dsDd", m68000up },
+{"movepl", 2, one(0000710), one(0170770), "Ddds", m68000up },
+
+{"moveq", 2, one(0070000), one(0170400), "MsDd", m68000up | mcfisa_a },
+{"moveq", 2, one(0070000), one(0170400), "#BDd", m68000up | mcfisa_a },
+
+/* The move opcode can generate the movea and moveq instructions. */
+{"moveb", 2, one(0010000), one(0170000), ";b$d", m68000up },
+{"moveb", 2, one(0010000), one(0170070), "Ds$d", mcfisa_a },
+{"moveb", 2, one(0010020), one(0170070), "as$d", mcfisa_a },
+{"moveb", 2, one(0010030), one(0170070), "+s$d", mcfisa_a },
+{"moveb", 2, one(0010040), one(0170070), "-s$d", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170000), "nsqd", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170700), "obDd", mcfisa_a },
+{"moveb", 2, one(0010200), one(0170700), "obad", mcfisa_a },
+{"moveb", 2, one(0010300), one(0170700), "ob+d", mcfisa_a },
+{"moveb", 2, one(0010400), one(0170700), "ob-d", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170000), "obnd", mcfisa_b },
+
+{"movew", 2, one(0030000), one(0170000), "*w%d", m68000up },
+{"movew", 2, one(0030000), one(0170000), "ms%d", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "nspd", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "owmd", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "ownd", mcfisa_b },
+{"movew", 2, one(0040300), one(0177700), "Ss$s", m68000up },
+{"movew", 2, one(0040300), one(0177770), "SsDs", mcfisa_a },
+{"movew", 2, one(0041300), one(0177700), "Cs$s", m68010up },
+{"movew", 2, one(0041300), one(0177770), "CsDs", mcfisa_a },
+{"movew", 2, one(0042300), one(0177700), ";wCd", m68000up },
+{"movew", 2, one(0042300), one(0177700), "DsCd", mcfisa_a },
+{"movew", 4, one(0042374), one(0177777), "#wCd", mcfisa_a },
+{"movew", 2, one(0043300), one(0177700), ";wSd", m68000up },
+{"movew", 2, one(0043300), one(0177700), "DsSd", mcfisa_a },
+{"movew", 4, one(0043374), one(0177777), "#wSd", mcfisa_a },
+
+{"movel", 2, one(0070000), one(0170400), "MsDd", m68000up | mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "*l%d", m68000up },
+{"movel", 2, one(0020000), one(0170000), "ms%d", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "nspd", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "olmd", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "olnd", mcfisa_b },
+{"movel", 2, one(0047140), one(0177770), "AsUd", m68000up | mcfusp },
+{"movel", 2, one(0047150), one(0177770), "UdAs", m68000up | mcfusp },
+{"movel", 2, one(0120600), one(0177760), "EsRs", mcfmac },
+{"movel", 2, one(0120400), one(0177760), "RsEs", mcfmac },
+{"movel", 6, one(0120474), one(0177777), "#lEs", mcfmac },
+{"movel", 2, one(0124600), one(0177760), "GsRs", mcfmac },
+{"movel", 2, one(0124400), one(0177760), "RsGs", mcfmac },
+{"movel", 6, one(0124474), one(0177777), "#lGs", mcfmac },
+{"movel", 2, one(0126600), one(0177760), "HsRs", mcfmac },
+{"movel", 2, one(0126400), one(0177760), "RsHs", mcfmac },
+{"movel", 6, one(0126474), one(0177777), "#lHs", mcfmac },
+{"movel", 2, one(0124700), one(0177777), "GsCs", mcfmac },
+
+{"movel", 2, one(0xa180), one(0xf9f0), "eFRs", mcfemac }, /* ACCx,Rx. */
+{"movel", 2, one(0xab80), one(0xfbf0), "g]Rs", mcfemac }, /* ACCEXTx,Rx. */
+{"movel", 2, one(0xa980), one(0xfff0), "G-Rs", mcfemac }, /* macsr,Rx. */
+{"movel", 2, one(0xad80), one(0xfff0), "H-Rs", mcfemac }, /* mask,Rx. */
+{"movel", 2, one(0xa110), one(0xf9fc), "efeF", mcfemac }, /* ACCy,ACCx. */
+{"movel", 2, one(0xa9c0), one(0xffff), "G-C-", mcfemac }, /* macsr,ccr. */
+{"movel", 2, one(0xa100), one(0xf9f0), "RseF", mcfemac }, /* Rx,ACCx. */
+{"movel", 6, one(0xa13c), one(0xf9ff), "#leF", mcfemac }, /* #,ACCx. */
+{"movel", 2, one(0xab00), one(0xfbc0), "Rsg]", mcfemac }, /* Rx,ACCEXTx. */
+{"movel", 6, one(0xab3c), one(0xfbff), "#lg]", mcfemac }, /* #,ACCEXTx. */
+{"movel", 2, one(0xa900), one(0xffc0), "RsG-", mcfemac }, /* Rx,macsr. */
+{"movel", 6, one(0xa93c), one(0xffff), "#lG-", mcfemac }, /* #,macsr. */
+{"movel", 2, one(0xad00), one(0xffc0), "RsH-", mcfemac }, /* Rx,mask. */
+{"movel", 6, one(0xad3c), one(0xffff), "#lH-", mcfemac }, /* #,mask. */
+
+{"move", 2, one(0030000), one(0170000), "*w%d", m68000up },
+{"move", 2, one(0030000), one(0170000), "ms%d", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "nspd", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "owmd", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "ownd", mcfisa_b },
+{"move", 2, one(0040300), one(0177700), "Ss$s", m68000up },
+{"move", 2, one(0040300), one(0177770), "SsDs", mcfisa_a },
+{"move", 2, one(0041300), one(0177700), "Cs$s", m68010up },
+{"move", 2, one(0041300), one(0177770), "CsDs", mcfisa_a },
+{"move", 2, one(0042300), one(0177700), ";wCd", m68000up },
+{"move", 2, one(0042300), one(0177700), "DsCd", mcfisa_a },
+{"move", 4, one(0042374), one(0177777), "#wCd", mcfisa_a },
+{"move", 2, one(0043300), one(0177700), ";wSd", m68000up },
+{"move", 2, one(0043300), one(0177700), "DsSd", mcfisa_a },
+{"move", 4, one(0043374), one(0177777), "#wSd", mcfisa_a },
+
+{"move", 2, one(0047140), one(0177770), "AsUd", m68000up },
+{"move", 2, one(0047150), one(0177770), "UdAs", m68000up },
+
+{"mov3ql", 2, one(0120500), one(0170700), "xd%s", mcfisa_b },
+{"mvsb", 2, one(0070400), one(0170700), "*bDd", mcfisa_b },
+{"mvsw", 2, one(0070500), one(0170700), "*wDd", mcfisa_b },
+{"mvzb", 2, one(0070600), one(0170700), "*bDd", mcfisa_b },
+{"mvzw", 2, one(0070700), one(0170700), "*wDd", mcfisa_b },
+
+{"movesb", 4, two(0007000, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesb", 4, two(0007000, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movesw", 4, two(0007100, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesw", 4, two(0007100, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movesl", 4, two(0007200, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesl", 4, two(0007200, 04000), two(0177700, 07777), "R1~s", m68010up },
+
+{"move16", 4, two(0xf620, 0x8000), two(0xfff8, 0x8fff), "+s+1", m68040up },
+{"move16", 2, one(0xf600), one(0xfff8), "+s_L", m68040up },
+{"move16", 2, one(0xf608), one(0xfff8), "_L+s", m68040up },
+{"move16", 2, one(0xf610), one(0xfff8), "as_L", m68040up },
+{"move16", 2, one(0xf618), one(0xfff8), "_Las", m68040up },
+
+{"msacw", 4, two(0xa080, 0x0100), two(0xf180, 0x0910), "uNuoiI4/Rn", mcfmac },
+{"msacw", 4, two(0xa080, 0x0300), two(0xf180, 0x0910), "uNuoMh4/Rn", mcfmac },
+{"msacw", 4, two(0xa080, 0x0100), two(0xf180, 0x0f10), "uNuo4/Rn", mcfmac },
+{"msacw", 4, two(0xa000, 0x0100), two(0xf1b0, 0x0900), "uMumiI", mcfmac },
+{"msacw", 4, two(0xa000, 0x0300), two(0xf1b0, 0x0900), "uMumMh", mcfmac },
+{"msacw", 4, two(0xa000, 0x0100), two(0xf1b0, 0x0f00), "uMum", mcfmac },
+
+{"msacw", 4, two(0xa000, 0x0100), two(0xf100, 0x0900), "uMumiI4/RneG", mcfemac },/* Ry,Rx,SF,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0300), two(0xf100, 0x0900), "uMumMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf100, 0x0f00), "uMum4/RneG", mcfemac },/* Ry,Rx,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */
+{"msacw", 4, two(0xa000, 0x0300), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */
+
+{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac },
+{"msacl", 4, two(0xa080, 0x0b00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac },
+{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0800), "RMRm", mcfmac },
+
+{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0900), "RMRmiIeH", mcfemac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf130, 0x0900), "RMRmMheH", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0f00), "RMRmeH", mcfemac },
+
+{"mulsw", 2, one(0140700), one(0170700), ";wDd", m68000up|mcfisa_a },
+{"mulsl", 4, two(0046000,004000), two(0177700,0107770), ";lD1", m68020up|cpu32 },
+{"mulsl", 4, two(0046000,004000), two(0177700,0107770), "qsD1", mcfisa_a },
+{"mulsl", 4, two(0046000,006000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 },
+
+{"muluw", 2, one(0140300), one(0170700), ";wDd", m68000up|mcfisa_a },
+{"mulul", 4, two(0046000,000000), two(0177700,0107770), ";lD1", m68020up|cpu32 },
+{"mulul", 4, two(0046000,000000), two(0177700,0107770), "qsD1", mcfisa_a },
+{"mulul", 4, two(0046000,002000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 },
+
+{"nbcd", 2, one(0044000), one(0177700), "$s", m68000up },
+
+{"negb", 2, one(0042000), one(0177700), "$s", m68000up },
+{"negw", 2, one(0042100), one(0177700), "$s", m68000up },
+{"negl", 2, one(0042200), one(0177700), "$s", m68000up },
+{"negl", 2, one(0042200), one(0177700), "Ds", mcfisa_a},
+
+{"negxb", 2, one(0040000), one(0177700), "$s", m68000up },
+{"negxw", 2, one(0040100), one(0177700), "$s", m68000up },
+{"negxl", 2, one(0040200), one(0177700), "$s", m68000up },
+{"negxl", 2, one(0040200), one(0177700), "Ds", mcfisa_a},
+
+{"nop", 2, one(0047161), one(0177777), "", m68000up | mcfisa_a},
+
+{"notb", 2, one(0043000), one(0177700), "$s", m68000up },
+{"notw", 2, one(0043100), one(0177700), "$s", m68000up },
+{"notl", 2, one(0043200), one(0177700), "$s", m68000up },
+{"notl", 2, one(0043200), one(0177700), "Ds", mcfisa_a},
+
+{"orib", 4, one(0000000), one(0177700), "#b$s", m68000up },
+{"orib", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"oriw", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"oriw", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"oril", 6, one(0000200), one(0177700), "#l$s", m68000up },
+{"oril", 6, one(0000200), one(0177700), "#lDs", mcfisa_a },
+{"ori", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"ori", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"ori", 4, one(0000174), one(0177777), "#wSs", m68000up },
+
+/* The or opcode can generate the ori instruction. */
+{"orb", 4, one(0000000), one(0177700), "#b$s", m68000up },
+{"orb", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"orb", 2, one(0100000), one(0170700), ";bDd", m68000up },
+{"orb", 2, one(0100400), one(0170700), "Dd~s", m68000up },
+{"orw", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"orw", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"orw", 2, one(0100100), one(0170700), ";wDd", m68000up },
+{"orw", 2, one(0100500), one(0170700), "Dd~s", m68000up },
+{"orl", 6, one(0000200), one(0177700), "#l$s", m68000up },
+{"orl", 6, one(0000200), one(0177700), "#lDs", mcfisa_a },
+{"orl", 2, one(0100200), one(0170700), ";lDd", m68000up | mcfisa_a },
+{"orl", 2, one(0100600), one(0170700), "Dd~s", m68000up | mcfisa_a },
+{"or", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"or", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"or", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"or", 2, one(0100100), one(0170700), ";wDd", m68000up },
+{"or", 2, one(0100500), one(0170700), "Dd~s", m68000up },
+
+{"pack", 4, one(0100500), one(0170770), "DsDd#w", m68020up },
+{"pack", 4, one(0100510), one(0170770), "-s-d#w", m68020up },
+
+{"pbac", 2, one(0xf087), one(0xffbf), "Bc", m68851 },
+{"pbacw", 2, one(0xf087), one(0xffff), "BW", m68851 },
+{"pbas", 2, one(0xf086), one(0xffbf), "Bc", m68851 },
+{"pbasw", 2, one(0xf086), one(0xffff), "BW", m68851 },
+{"pbbc", 2, one(0xf081), one(0xffbf), "Bc", m68851 },
+{"pbbcw", 2, one(0xf081), one(0xffff), "BW", m68851 },
+{"pbbs", 2, one(0xf080), one(0xffbf), "Bc", m68851 },
+{"pbbsw", 2, one(0xf080), one(0xffff), "BW", m68851 },
+{"pbcc", 2, one(0xf08f), one(0xffbf), "Bc", m68851 },
+{"pbccw", 2, one(0xf08f), one(0xffff), "BW", m68851 },
+{"pbcs", 2, one(0xf08e), one(0xffbf), "Bc", m68851 },
+{"pbcsw", 2, one(0xf08e), one(0xffff), "BW", m68851 },
+{"pbgc", 2, one(0xf08d), one(0xffbf), "Bc", m68851 },
+{"pbgcw", 2, one(0xf08d), one(0xffff), "BW", m68851 },
+{"pbgs", 2, one(0xf08c), one(0xffbf), "Bc", m68851 },
+{"pbgsw", 2, one(0xf08c), one(0xffff), "BW", m68851 },
+{"pbic", 2, one(0xf08b), one(0xffbf), "Bc", m68851 },
+{"pbicw", 2, one(0xf08b), one(0xffff), "BW", m68851 },
+{"pbis", 2, one(0xf08a), one(0xffbf), "Bc", m68851 },
+{"pbisw", 2, one(0xf08a), one(0xffff), "BW", m68851 },
+{"pblc", 2, one(0xf083), one(0xffbf), "Bc", m68851 },
+{"pblcw", 2, one(0xf083), one(0xffff), "BW", m68851 },
+{"pbls", 2, one(0xf082), one(0xffbf), "Bc", m68851 },
+{"pblsw", 2, one(0xf082), one(0xffff), "BW", m68851 },
+{"pbsc", 2, one(0xf085), one(0xffbf), "Bc", m68851 },
+{"pbscw", 2, one(0xf085), one(0xffff), "BW", m68851 },
+{"pbss", 2, one(0xf084), one(0xffbf), "Bc", m68851 },
+{"pbssw", 2, one(0xf084), one(0xffff), "BW", m68851 },
+{"pbwc", 2, one(0xf089), one(0xffbf), "Bc", m68851 },
+{"pbwcw", 2, one(0xf089), one(0xffff), "BW", m68851 },
+{"pbws", 2, one(0xf088), one(0xffbf), "Bc", m68851 },
+{"pbwsw", 2, one(0xf088), one(0xffff), "BW", m68851 },
+
+{"pdbac", 4, two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbas", 4, two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbc", 4, two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbs", 4, two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcc", 4, two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcs", 4, two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgc", 4, two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgs", 4, two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbic", 4, two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbis", 4, two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdblc", 4, two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbls", 4, two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbsc", 4, two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbss", 4, two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbwc", 4, two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbws", 4, two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw", m68851 },
+
+{"pea", 2, one(0044100), one(0177700), "!s", m68000up|mcfisa_a },
+
+{"pflusha", 2, one(0xf518), one(0xfff8), "", m68040up },
+{"pflusha", 4, two(0xf000,0x2400), two(0xffff,0xffff), "", m68030 | m68851 },
+
+{"pflush", 4, two(0xf000,0x3010), two(0xffc0,0xfe10), "T3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3810), two(0xffc0,0xfe10), "T3T9&s", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3008), two(0xffc0,0xfe18), "D3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3808), two(0xffc0,0xfe18), "D3T9&s", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3000), two(0xffc0,0xfe1e), "f3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3800), two(0xffc0,0xfe1e), "f3T9&s", m68030|m68851 },
+{"pflush", 2, one(0xf508), one(0xfff8), "as", m68040up },
+{"pflush", 2, one(0xf508), one(0xfff8), "As", m68040up },
+
+{"pflushan", 2, one(0xf510), one(0xfff8), "", m68040up },
+{"pflushn", 2, one(0xf500), one(0xfff8), "as", m68040up },
+{"pflushn", 2, one(0xf500), one(0xfff8), "As", m68040up },
+
+{"pflushr", 4, two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s", m68851 },
+
+{"pflushs", 4, two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c10), two(0xfff8, 0xfe10), "T3T9&s", m68851 },
+{"pflushs", 4, two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s", m68851 },
+{"pflushs", 4, two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s", m68851 },
+
+{"ploadr", 4, two(0xf000,0x2210), two(0xffc0,0xfff0), "T3&s", m68030|m68851 },
+{"ploadr", 4, two(0xf000,0x2208), two(0xffc0,0xfff8), "D3&s", m68030|m68851 },
+{"ploadr", 4, two(0xf000,0x2200), two(0xffc0,0xfffe), "f3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2010), two(0xffc0,0xfff0), "T3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2008), two(0xffc0,0xfff8), "D3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2000), two(0xffc0,0xfffe), "f3&s", m68030|m68851 },
+
+{"plpar", 2, one(0xf5c8), one(0xfff8), "as", m68060 },
+{"plpaw", 2, one(0xf588), one(0xfff8), "as", m68060 },
+
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xffff), "*l08", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x5c00), two(0xffc0,0xffff), "*w18", m68851 },
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "*b28", m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xffff), "08%s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x5e00), two(0xffc0,0xffff), "18%s", m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "28%s", m68851 },
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "|sW8", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "W8~s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xe3e3), "*wX3", m68851 },
+{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xe3e3), "X3%s", m68851 },
+{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xffff), "*wY8", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xffff), "Y8%s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6600), two(0xffc0,0xffff), "Z8%s", m68851 },
+{"pmove", 4, two(0xf000,0x0800), two(0xffc0,0xfbff), "*l38", m68030 },
+{"pmove", 4, two(0xf000,0x0a00), two(0xffc0,0xfbff), "38%s", m68030 },
+
+{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "*l08", m68030 },
+{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "|sW8", m68030 },
+{"pmovefd", 4, two(0xf000, 0x0900), two(0xffc0, 0xfbff), "*l38", m68030 },
+
+{"prestore", 2, one(0xf140), one(0xffc0), "<s", m68851 },
+
+{"psave", 2, one(0xf100), one(0xffc0), ">s", m68851 },
+
+{"psac", 4, two(0xf040, 0x0007), two(0xffc0, 0xffff), "$s", m68851 },
+{"psas", 4, two(0xf040, 0x0006), two(0xffc0, 0xffff), "$s", m68851 },
+{"psbc", 4, two(0xf040, 0x0001), two(0xffc0, 0xffff), "$s", m68851 },
+{"psbs", 4, two(0xf040, 0x0000), two(0xffc0, 0xffff), "$s", m68851 },
+{"pscc", 4, two(0xf040, 0x000f), two(0xffc0, 0xffff), "$s", m68851 },
+{"pscs", 4, two(0xf040, 0x000e), two(0xffc0, 0xffff), "$s", m68851 },
+{"psgc", 4, two(0xf040, 0x000d), two(0xffc0, 0xffff), "$s", m68851 },
+{"psgs", 4, two(0xf040, 0x000c), two(0xffc0, 0xffff), "$s", m68851 },
+{"psic", 4, two(0xf040, 0x000b), two(0xffc0, 0xffff), "$s", m68851 },
+{"psis", 4, two(0xf040, 0x000a), two(0xffc0, 0xffff), "$s", m68851 },
+{"pslc", 4, two(0xf040, 0x0003), two(0xffc0, 0xffff), "$s", m68851 },
+{"psls", 4, two(0xf040, 0x0002), two(0xffc0, 0xffff), "$s", m68851 },
+{"pssc", 4, two(0xf040, 0x0005), two(0xffc0, 0xffff), "$s", m68851 },
+{"psss", 4, two(0xf040, 0x0004), two(0xffc0, 0xffff), "$s", m68851 },
+{"pswc", 4, two(0xf040, 0x0009), two(0xffc0, 0xffff), "$s", m68851 },
+{"psws", 4, two(0xf040, 0x0008), two(0xffc0, 0xffff), "$s", m68851 },
+
+{"ptestr", 4, two(0xf000,0x8210), two(0xffc0, 0xe3f0), "T3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8310), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8208), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8308), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8200), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8300), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 },
+{"ptestr", 2, one(0xf568), one(0xfff8), "as", m68040 },
+
+{"ptestw", 4, two(0xf000,0x8010), two(0xffc0,0xe3f0), "T3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8110), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8008), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8108), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8000), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8100), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 },
+{"ptestw", 2, one(0xf548), one(0xfff8), "as", m68040 },
+
+{"ptrapacw", 6, two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapacl", 6, two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapac", 4, two(0xf07c, 0x0007), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapasw", 6, two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapasl", 6, two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapas", 4, two(0xf07c, 0x0006), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbcw", 6, two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbcl", 6, two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbc", 4, two(0xf07c, 0x0001), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbsw", 6, two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbsl", 6, two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbs", 4, two(0xf07c, 0x0000), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapccw", 6, two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapccl", 6, two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcc", 4, two(0xf07c, 0x000f), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapcsw", 6, two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapcsl", 6, two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcs", 4, two(0xf07c, 0x000e), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgcw", 6, two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgcl", 6, two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgc", 4, two(0xf07c, 0x000d), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgsw", 6, two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgsl", 6, two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgs", 4, two(0xf07c, 0x000c), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapicw", 6, two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapicl", 6, two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapic", 4, two(0xf07c, 0x000b), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapisw", 6, two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapisl", 6, two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapis", 4, two(0xf07c, 0x000a), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplcw", 6, two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplcl", 6, two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l", m68851 },
+{"ptraplc", 4, two(0xf07c, 0x0003), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplsw", 6, two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplsl", 6, two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapls", 4, two(0xf07c, 0x0002), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapscw", 6, two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapscl", 6, two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapsc", 4, two(0xf07c, 0x0005), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapssw", 6, two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapssl", 6, two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapss", 4, two(0xf07c, 0x0004), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwcw", 6, two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwcl", 6, two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapwc", 4, two(0xf07c, 0x0009), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwsw", 6, two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwsl", 6, two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapws", 4, two(0xf07c, 0x0008), two(0xffff, 0xffff), "", m68851 },
+
+{"pulse", 2, one(0045314), one(0177777), "", m68060 | mcfisa_a },
+
+{"pvalid", 4, two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s", m68851 },
+{"pvalid", 4, two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s", m68851 },
+
+ /* FIXME: don't allow Dw==Dx. */
+{"remsl", 4, two(0x4c40, 0x0800), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv },
+{"remul", 4, two(0x4c40, 0x0000), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv },
+
+{"reset", 2, one(0047160), one(0177777), "", m68000up },
+
+{"rolb", 2, one(0160430), one(0170770), "QdDs", m68000up },
+{"rolb", 2, one(0160470), one(0170770), "DdDs", m68000up },
+{"rolw", 2, one(0160530), one(0170770), "QdDs", m68000up },
+{"rolw", 2, one(0160570), one(0170770), "DdDs", m68000up },
+{"rolw", 2, one(0163700), one(0177700), "~s", m68000up },
+{"roll", 2, one(0160630), one(0170770), "QdDs", m68000up },
+{"roll", 2, one(0160670), one(0170770), "DdDs", m68000up },
+
+{"rorb", 2, one(0160030), one(0170770), "QdDs", m68000up },
+{"rorb", 2, one(0160070), one(0170770), "DdDs", m68000up },
+{"rorw", 2, one(0160130), one(0170770), "QdDs", m68000up },
+{"rorw", 2, one(0160170), one(0170770), "DdDs", m68000up },
+{"rorw", 2, one(0163300), one(0177700), "~s", m68000up },
+{"rorl", 2, one(0160230), one(0170770), "QdDs", m68000up },
+{"rorl", 2, one(0160270), one(0170770), "DdDs", m68000up },
+
+{"roxlb", 2, one(0160420), one(0170770), "QdDs", m68000up },
+{"roxlb", 2, one(0160460), one(0170770), "DdDs", m68000up },
+{"roxlw", 2, one(0160520), one(0170770), "QdDs", m68000up },
+{"roxlw", 2, one(0160560), one(0170770), "DdDs", m68000up },
+{"roxlw", 2, one(0162700), one(0177700), "~s", m68000up },
+{"roxll", 2, one(0160620), one(0170770), "QdDs", m68000up },
+{"roxll", 2, one(0160660), one(0170770), "DdDs", m68000up },
+
+{"roxrb", 2, one(0160020), one(0170770), "QdDs", m68000up },
+{"roxrb", 2, one(0160060), one(0170770), "DdDs", m68000up },
+{"roxrw", 2, one(0160120), one(0170770), "QdDs", m68000up },
+{"roxrw", 2, one(0160160), one(0170770), "DdDs", m68000up },
+{"roxrw", 2, one(0162300), one(0177700), "~s", m68000up },
+{"roxrl", 2, one(0160220), one(0170770), "QdDs", m68000up },
+{"roxrl", 2, one(0160260), one(0170770), "DdDs", m68000up },
+
+{"rtd", 4, one(0047164), one(0177777), "#w", m68010up },
+
+{"rte", 2, one(0047163), one(0177777), "", m68000up | mcfisa_a },
+
+{"rtm", 2, one(0003300), one(0177760), "Rs", m68020 },
+
+{"rtr", 2, one(0047167), one(0177777), "", m68000up },
+
+{"rts", 2, one(0047165), one(0177777), "", m68000up | mcfisa_a },
+
+{"satsl", 2, one(0046200), one(0177770), "Ds", mcfisa_b },
+
+{"sbcd", 2, one(0100400), one(0170770), "DsDd", m68000up },
+{"sbcd", 2, one(0100410), one(0170770), "-s-d", m68000up },
+
+{"scc", 2, one(0052300), one(0177700), "$s", m68000up },
+{"scc", 2, one(0052300), one(0177700), "Ds", mcfisa_a },
+{"scs", 2, one(0052700), one(0177700), "$s", m68000up },
+{"scs", 2, one(0052700), one(0177700), "Ds", mcfisa_a },
+{"seq", 2, one(0053700), one(0177700), "$s", m68000up },
+{"seq", 2, one(0053700), one(0177700), "Ds", mcfisa_a },
+{"sf", 2, one(0050700), one(0177700), "$s", m68000up },
+{"sf", 2, one(0050700), one(0177700), "Ds", mcfisa_a },
+{"sge", 2, one(0056300), one(0177700), "$s", m68000up },
+{"sge", 2, one(0056300), one(0177700), "Ds", mcfisa_a },
+{"sgt", 2, one(0057300), one(0177700), "$s", m68000up },
+{"sgt", 2, one(0057300), one(0177700), "Ds", mcfisa_a },
+{"shi", 2, one(0051300), one(0177700), "$s", m68000up },
+{"shi", 2, one(0051300), one(0177700), "Ds", mcfisa_a },
+{"sle", 2, one(0057700), one(0177700), "$s", m68000up },
+{"sle", 2, one(0057700), one(0177700), "Ds", mcfisa_a },
+{"sls", 2, one(0051700), one(0177700), "$s", m68000up },
+{"sls", 2, one(0051700), one(0177700), "Ds", mcfisa_a },
+{"slt", 2, one(0056700), one(0177700), "$s", m68000up },
+{"slt", 2, one(0056700), one(0177700), "Ds", mcfisa_a },
+{"smi", 2, one(0055700), one(0177700), "$s", m68000up },
+{"smi", 2, one(0055700), one(0177700), "Ds", mcfisa_a },
+{"sne", 2, one(0053300), one(0177700), "$s", m68000up },
+{"sne", 2, one(0053300), one(0177700), "Ds", mcfisa_a },
+{"spl", 2, one(0055300), one(0177700), "$s", m68000up },
+{"spl", 2, one(0055300), one(0177700), "Ds", mcfisa_a },
+{"st", 2, one(0050300), one(0177700), "$s", m68000up },
+{"st", 2, one(0050300), one(0177700), "Ds", mcfisa_a },
+{"svc", 2, one(0054300), one(0177700), "$s", m68000up },
+{"svc", 2, one(0054300), one(0177700), "Ds", mcfisa_a },
+{"svs", 2, one(0054700), one(0177700), "$s", m68000up },
+{"svs", 2, one(0054700), one(0177700), "Ds", mcfisa_a },
+
+{"stop", 4, one(0047162), one(0177777), "#w", m68000up | mcfisa_a },
+
+{"strldsr", 4, two(0040347,0043374), two(0177777,0177777), "#w", mcfisa_aa},
+
+{"subal", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"subaw", 2, one(0110300), one(0170700), "*wAd", m68000up },
+
+{"subib", 4, one(0002000), one(0177700), "#b$s", m68000up },
+{"subiw", 4, one(0002100), one(0177700), "#w$s", m68000up },
+{"subil", 6, one(0002200), one(0177700), "#l$s", m68000up },
+{"subil", 6, one(0002200), one(0177700), "#lDs", mcfisa_a },
+
+{"subqb", 2, one(0050400), one(0170700), "Qd%s", m68000up },
+{"subqw", 2, one(0050500), one(0170700), "Qd%s", m68000up },
+{"subql", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a },
+
+/* The sub opcode can generate the suba, subi, and subq instructions. */
+{"subb", 2, one(0050400), one(0170700), "Qd%s", m68000up },
+{"subb", 4, one(0002000), one(0177700), "#b$s", m68000up },
+{"subb", 2, one(0110000), one(0170700), ";bDd", m68000up },
+{"subb", 2, one(0110400), one(0170700), "Dd~s", m68000up },
+{"subw", 2, one(0050500), one(0170700), "Qd%s", m68000up },
+{"subw", 4, one(0002100), one(0177700), "#w$s", m68000up },
+{"subw", 2, one(0110300), one(0170700), "*wAd", m68000up },
+{"subw", 2, one(0110100), one(0170700), "*wDd", m68000up },
+{"subw", 2, one(0110500), one(0170700), "Dd~s", m68000up },
+{"subl", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a },
+{"subl", 6, one(0002200), one(0177700), "#l$s", m68000up },
+{"subl", 6, one(0002200), one(0177700), "#lDs", mcfisa_a },
+{"subl", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"subl", 2, one(0110200), one(0170700), "*lDd", m68000up | mcfisa_a },
+{"subl", 2, one(0110600), one(0170700), "Dd~s", m68000up | mcfisa_a },
+
+{"subxb", 2, one(0110400), one(0170770), "DsDd", m68000up },
+{"subxb", 2, one(0110410), one(0170770), "-s-d", m68000up },
+{"subxw", 2, one(0110500), one(0170770), "DsDd", m68000up },
+{"subxw", 2, one(0110510), one(0170770), "-s-d", m68000up },
+{"subxl", 2, one(0110600), one(0170770), "DsDd", m68000up | mcfisa_a },
+{"subxl", 2, one(0110610), one(0170770), "-s-d", m68000up },
+
+{"swap", 2, one(0044100), one(0177770), "Ds", m68000up | mcfisa_a },
+
+/* swbeg and swbegl are magic constants used on sysV68. The compiler
+ generates them before a switch table. They tell the debugger and
+ disassembler that a switch table follows. The parameter is the
+ number of elements in the table. swbeg means that the entries in
+ the table are word (2 byte) sized, and swbegl means that the
+ entries in the table are longword (4 byte) sized. */
+{"swbeg", 4, one(0045374), one(0177777), "#w", m68000up | mcfisa_a },
+{"swbegl", 6, one(0045375), one(0177777), "#l", m68000up | mcfisa_a },
+
+{"tas", 2, one(0045300), one(0177700), "$s", m68000up | mcfisa_b},
+
+#define TBL1(name,insn_size,signed,round,size) \
+ {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)|0000400), \
+ two(0177700,0107777), "!sD1", cpu32 }, \
+ {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)), \
+ two(0177770,0107770), "DsD3D1", cpu32 }
+#define TBL(name1, name2, name3, s, r) \
+ TBL1(name1, 4, s, r, 0), TBL1(name2, 4, s, r, 1), TBL1(name3, 4, s, r, 2)
+TBL("tblsb", "tblsw", "tblsl", 2, 1),
+TBL("tblsnb", "tblsnw", "tblsnl", 2, 0),
+TBL("tblub", "tbluw", "tblul", 0, 1),
+TBL("tblunb", "tblunw", "tblunl", 0, 0),
+
+{"trap", 2, one(0047100), one(0177760), "Ts", m68000up | mcfisa_a },
+
+{"trapcc", 2, one(0052374), one(0177777), "", m68020up | cpu32 },
+{"trapcs", 2, one(0052774), one(0177777), "", m68020up | cpu32 },
+{"trapeq", 2, one(0053774), one(0177777), "", m68020up | cpu32 },
+{"trapf", 2, one(0050774), one(0177777), "", m68020up | cpu32 | mcfisa_a },
+{"trapge", 2, one(0056374), one(0177777), "", m68020up | cpu32 },
+{"trapgt", 2, one(0057374), one(0177777), "", m68020up | cpu32 },
+{"traphi", 2, one(0051374), one(0177777), "", m68020up | cpu32 },
+{"traple", 2, one(0057774), one(0177777), "", m68020up | cpu32 },
+{"trapls", 2, one(0051774), one(0177777), "", m68020up | cpu32 },
+{"traplt", 2, one(0056774), one(0177777), "", m68020up | cpu32 },
+{"trapmi", 2, one(0055774), one(0177777), "", m68020up | cpu32 },
+{"trapne", 2, one(0053374), one(0177777), "", m68020up | cpu32 },
+{"trappl", 2, one(0055374), one(0177777), "", m68020up | cpu32 },
+{"trapt", 2, one(0050374), one(0177777), "", m68020up | cpu32 },
+{"trapvc", 2, one(0054374), one(0177777), "", m68020up | cpu32 },
+{"trapvs", 2, one(0054774), one(0177777), "", m68020up | cpu32 },
+
+{"trapccw", 4, one(0052372), one(0177777), "#w", m68020up|cpu32 },
+{"trapcsw", 4, one(0052772), one(0177777), "#w", m68020up|cpu32 },
+{"trapeqw", 4, one(0053772), one(0177777), "#w", m68020up|cpu32 },
+{"trapfw", 4, one(0050772), one(0177777), "#w", m68020up|cpu32|mcfisa_a},
+{"trapgew", 4, one(0056372), one(0177777), "#w", m68020up|cpu32 },
+{"trapgtw", 4, one(0057372), one(0177777), "#w", m68020up|cpu32 },
+{"traphiw", 4, one(0051372), one(0177777), "#w", m68020up|cpu32 },
+{"traplew", 4, one(0057772), one(0177777), "#w", m68020up|cpu32 },
+{"traplsw", 4, one(0051772), one(0177777), "#w", m68020up|cpu32 },
+{"trapltw", 4, one(0056772), one(0177777), "#w", m68020up|cpu32 },
+{"trapmiw", 4, one(0055772), one(0177777), "#w", m68020up|cpu32 },
+{"trapnew", 4, one(0053372), one(0177777), "#w", m68020up|cpu32 },
+{"trapplw", 4, one(0055372), one(0177777), "#w", m68020up|cpu32 },
+{"traptw", 4, one(0050372), one(0177777), "#w", m68020up|cpu32 },
+{"trapvcw", 4, one(0054372), one(0177777), "#w", m68020up|cpu32 },
+{"trapvsw", 4, one(0054772), one(0177777), "#w", m68020up|cpu32 },
+
+{"trapccl", 6, one(0052373), one(0177777), "#l", m68020up|cpu32 },
+{"trapcsl", 6, one(0052773), one(0177777), "#l", m68020up|cpu32 },
+{"trapeql", 6, one(0053773), one(0177777), "#l", m68020up|cpu32 },
+{"trapfl", 6, one(0050773), one(0177777), "#l", m68020up|cpu32|mcfisa_a},
+{"trapgel", 6, one(0056373), one(0177777), "#l", m68020up|cpu32 },
+{"trapgtl", 6, one(0057373), one(0177777), "#l", m68020up|cpu32 },
+{"traphil", 6, one(0051373), one(0177777), "#l", m68020up|cpu32 },
+{"traplel", 6, one(0057773), one(0177777), "#l", m68020up|cpu32 },
+{"traplsl", 6, one(0051773), one(0177777), "#l", m68020up|cpu32 },
+{"trapltl", 6, one(0056773), one(0177777), "#l", m68020up|cpu32 },
+{"trapmil", 6, one(0055773), one(0177777), "#l", m68020up|cpu32 },
+{"trapnel", 6, one(0053373), one(0177777), "#l", m68020up|cpu32 },
+{"trappll", 6, one(0055373), one(0177777), "#l", m68020up|cpu32 },
+{"traptl", 6, one(0050373), one(0177777), "#l", m68020up|cpu32 },
+{"trapvcl", 6, one(0054373), one(0177777), "#l", m68020up|cpu32 },
+{"trapvsl", 6, one(0054773), one(0177777), "#l", m68020up|cpu32 },
+
+{"trapv", 2, one(0047166), one(0177777), "", m68000up },
+
+{"tstb", 2, one(0045000), one(0177700), ";b", m68020up|cpu32|mcfisa_a },
+{"tstb", 2, one(0045000), one(0177700), "$b", m68000up },
+{"tstw", 2, one(0045100), one(0177700), "*w", m68020up|cpu32|mcfisa_a },
+{"tstw", 2, one(0045100), one(0177700), "$w", m68000up },
+{"tstl", 2, one(0045200), one(0177700), "*l", m68020up|cpu32|mcfisa_a },
+{"tstl", 2, one(0045200), one(0177700), "$l", m68000up },
+
+{"unlk", 2, one(0047130), one(0177770), "As", m68000up | mcfisa_a },
+
+{"unpk", 4, one(0100600), one(0170770), "DsDd#w", m68020up },
+{"unpk", 4, one(0100610), one(0170770), "-s-d#w", m68020up },
+
+{"wddatab", 2, one(0175400), one(0177700), "~s", mcfisa_a },
+{"wddataw", 2, one(0175500), one(0177700), "~s", mcfisa_a },
+{"wddatal", 2, one(0175600), one(0177700), "~s", mcfisa_a },
+
+{"wdebug", 4, two(0175720, 03), two(0177770, 0xffff), "as", mcfisa_a },
+{"wdebug", 4, two(0175750, 03), two(0177770, 0xffff), "ds", mcfisa_a },
+};
+
+const int m68k_numopcodes = sizeof m68k_opcodes / sizeof m68k_opcodes[0];
+
+/* These aliases used to be in the above table, each one duplicating
+ all of the entries for its primary exactly. This table was
+ constructed by mechanical processing of the opcode table, with a
+ small number of tweaks done by hand. There are probably a lot more
+ aliases above that could be moved down here, except for very minor
+ differences. */
+
+const struct m68k_opcode_alias m68k_opcode_aliases[] =
+{
+ { "add", "addw", },
+ { "adda", "addaw", },
+ { "addi", "addiw", },
+ { "addq", "addqw", },
+ { "addx", "addxw", },
+ { "asl", "aslw", },
+ { "asr", "asrw", },
+ { "bhi", "bhiw", },
+ { "bls", "blsw", },
+ { "bcc", "bccw", },
+ { "bcs", "bcsw", },
+ { "bne", "bnew", },
+ { "beq", "beqw", },
+ { "bvc", "bvcw", },
+ { "bvs", "bvsw", },
+ { "bpl", "bplw", },
+ { "bmi", "bmiw", },
+ { "bge", "bgew", },
+ { "blt", "bltw", },
+ { "bgt", "bgtw", },
+ { "ble", "blew", },
+ { "bra", "braw", },
+ { "bsr", "bsrw", },
+ { "bhib", "bhis", },
+ { "blsb", "blss", },
+ { "bccb", "bccs", },
+ { "bcsb", "bcss", },
+ { "bneb", "bnes", },
+ { "beqb", "beqs", },
+ { "bvcb", "bvcs", },
+ { "bvsb", "bvss", },
+ { "bplb", "bpls", },
+ { "bmib", "bmis", },
+ { "bgeb", "bges", },
+ { "bltb", "blts", },
+ { "bgtb", "bgts", },
+ { "bleb", "bles", },
+ { "brab", "bras", },
+ { "bsrb", "bsrs", },
+ { "bhs", "bccw" },
+ { "bhss", "bccs" },
+ { "bhsb", "bccs" },
+ { "bhsw", "bccw" },
+ { "bhsl", "bccl" },
+ { "blo", "bcsw" },
+ { "blos", "bcss" },
+ { "blob", "bcss" },
+ { "blow", "bcsw" },
+ { "blol", "bcsl" },
+ { "br", "braw", },
+ { "brs", "bras", },
+ { "brb", "bras", },
+ { "brw", "braw", },
+ { "brl", "bral", },
+ { "jfnlt", "bcc", }, /* Apparently a sun alias. */
+ { "jfngt", "ble", }, /* Apparently a sun alias. */
+ { "jfeq", "beqs", }, /* Apparently a sun alias. */
+ { "bchgb", "bchg", },
+ { "bchgl", "bchg", },
+ { "bclrb", "bclr", },
+ { "bclrl", "bclr", },
+ { "bsetb", "bset", },
+ { "bsetl", "bset", },
+ { "btstb", "btst", },
+ { "btstl", "btst", },
+ { "cas2", "cas2w", },
+ { "cas", "casw", },
+ { "chk2", "chk2w", },
+ { "chk", "chkw", },
+ { "clr", "clrw", },
+ { "cmp2", "cmp2w", },
+ { "cmpa", "cmpaw", },
+ { "cmpi", "cmpiw", },
+ { "cmpm", "cmpmw", },
+ { "cmp", "cmpw", },
+ { "dbccw", "dbcc", },
+ { "dbcsw", "dbcs", },
+ { "dbeqw", "dbeq", },
+ { "dbfw", "dbf", },
+ { "dbgew", "dbge", },
+ { "dbgtw", "dbgt", },
+ { "dbhiw", "dbhi", },
+ { "dblew", "dble", },
+ { "dblsw", "dbls", },
+ { "dbltw", "dblt", },
+ { "dbmiw", "dbmi", },
+ { "dbnew", "dbne", },
+ { "dbplw", "dbpl", },
+ { "dbtw", "dbt", },
+ { "dbvcw", "dbvc", },
+ { "dbvsw", "dbvs", },
+ { "dbhs", "dbcc", },
+ { "dbhsw", "dbcc", },
+ { "dbra", "dbf", },
+ { "dbraw", "dbf", },
+ { "tdivsl", "divsl", },
+ { "divs", "divsw", },
+ { "divu", "divuw", },
+ { "ext", "extw", },
+ { "extbw", "extw", },
+ { "extwl", "extl", },
+ { "fbneq", "fbne", },
+ { "fbsneq", "fbsne", },
+ { "fdbneq", "fdbne", },
+ { "fdbsneq", "fdbsne", },
+ { "fmovecr", "fmovecrx", },
+ { "fmovm", "fmovem", },
+ { "fsneq", "fsne", },
+ { "fssneq", "fssne", },
+ { "ftrapneq", "ftrapne", },
+ { "ftrapsneq", "ftrapsne", },
+ { "fjneq", "fjne", },
+ { "fjsneq", "fjsne", },
+ { "jmpl", "jmp", },
+ { "jmps", "jmp", },
+ { "jsrl", "jsr", },
+ { "jsrs", "jsr", },
+ { "leal", "lea", },
+ { "lsl", "lslw", },
+ { "lsr", "lsrw", },
+ { "mac", "macw" },
+ { "movea", "moveaw", },
+ { "movem", "movemw", },
+ { "movml", "moveml", },
+ { "movmw", "movemw", },
+ { "movm", "movemw", },
+ { "movep", "movepw", },
+ { "movpw", "movepw", },
+ { "moves", "movesw" },
+ { "muls", "mulsw", },
+ { "mulu", "muluw", },
+ { "msac", "msacw" },
+ { "nbcdb", "nbcd" },
+ { "neg", "negw", },
+ { "negx", "negxw", },
+ { "not", "notw", },
+ { "peal", "pea", },
+ { "rol", "rolw", },
+ { "ror", "rorw", },
+ { "roxl", "roxlw", },
+ { "roxr", "roxrw", },
+ { "sats", "satsl", },
+ { "sbcdb", "sbcd", },
+ { "sccb", "scc", },
+ { "scsb", "scs", },
+ { "seqb", "seq", },
+ { "sfb", "sf", },
+ { "sgeb", "sge", },
+ { "sgtb", "sgt", },
+ { "shib", "shi", },
+ { "sleb", "sle", },
+ { "slsb", "sls", },
+ { "sltb", "slt", },
+ { "smib", "smi", },
+ { "sneb", "sne", },
+ { "splb", "spl", },
+ { "stb", "st", },
+ { "svcb", "svc", },
+ { "svsb", "svs", },
+ { "sfge", "sge", },
+ { "sfgt", "sgt", },
+ { "sfle", "sle", },
+ { "sflt", "slt", },
+ { "sfneq", "sne", },
+ { "suba", "subaw", },
+ { "subi", "subiw", },
+ { "subq", "subqw", },
+ { "sub", "subw", },
+ { "subx", "subxw", },
+ { "swapw", "swap", },
+ { "tasb", "tas", },
+ { "tpcc", "trapcc", },
+ { "tcc", "trapcc", },
+ { "tst", "tstw", },
+ { "jbra", "jra", },
+ { "jbhi", "jhi", },
+ { "jbls", "jls", },
+ { "jbcc", "jcc", },
+ { "jbcs", "jcs", },
+ { "jbne", "jne", },
+ { "jbeq", "jeq", },
+ { "jbvc", "jvc", },
+ { "jbvs", "jvs", },
+ { "jbpl", "jpl", },
+ { "jbmi", "jmi", },
+ { "jbge", "jge", },
+ { "jblt", "jlt", },
+ { "jbgt", "jgt", },
+ { "jble", "jle", },
+ { "movql", "moveq", },
+ { "moveql", "moveq", },
+ { "movl", "movel", },
+ { "movq", "moveq", },
+ { "moval", "moveal", },
+ { "movaw", "moveaw", },
+ { "movb", "moveb", },
+ { "movc", "movec", },
+ { "movecl", "movec", },
+ { "movpl", "movepl", },
+ { "movw", "movew", },
+ { "movsb", "movesb", },
+ { "movsl", "movesl", },
+ { "movsw", "movesw", },
+ { "mov3q", "mov3ql", },
+
+ { "tdivul", "divul", }, /* For m68k-svr4. */
+ { "fmovb", "fmoveb", },
+ { "fsmovb", "fsmoveb", },
+ { "fdmovb", "fdmoveb", },
+ { "fmovd", "fmoved", },
+ { "fsmovd", "fsmoved", },
+ { "fmovl", "fmovel", },
+ { "fsmovl", "fsmovel", },
+ { "fdmovl", "fdmovel", },
+ { "fmovp", "fmovep", },
+ { "fsmovp", "fsmovep", },
+ { "fdmovp", "fdmovep", },
+ { "fmovs", "fmoves", },
+ { "fsmovs", "fsmoves", },
+ { "fdmovs", "fdmoves", },
+ { "fmovw", "fmovew", },
+ { "fsmovw", "fsmovew", },
+ { "fdmovw", "fdmovew", },
+ { "fmovx", "fmovex", },
+ { "fsmovx", "fsmovex", },
+ { "fdmovx", "fdmovex", },
+ { "fmovcr", "fmovecr", },
+ { "fmovcrx", "fmovecrx", },
+ { "ftestb", "ftstb", },
+ { "ftestd", "ftstd", },
+ { "ftestl", "ftstl", },
+ { "ftestp", "ftstp", },
+ { "ftests", "ftsts", },
+ { "ftestw", "ftstw", },
+ { "ftestx", "ftstx", },
+
+ { "bitrevl", "bitrev", },
+ { "byterevl", "byterev", },
+ { "ff1l", "ff1", },
+
+};
+
+const int m68k_numaliases =
+ sizeof m68k_opcode_aliases / sizeof m68k_opcode_aliases[0];
+/* **** End of m68k-opc.c */
+/* **** floatformat.c from sourceware.org CVS 2005-08-14. */
+/* IEEE floating point support routines, for GDB, the GNU Debugger.
+ Copyright (C) 1991, 1994, 1999, 2000, 2003 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* This is needed to pick up the NAN macro on some systems. */
+//#define _GNU_SOURCE
+
+#ifndef INFINITY
+#ifdef HUGE_VAL
+#define INFINITY HUGE_VAL
+#else
+#define INFINITY (1.0 / 0.0)
+#endif
+#endif
+
+#ifndef NAN
+#define NAN (0.0 / 0.0)
+#endif
+
+static unsigned long get_field (const unsigned char *,
+ enum floatformat_byteorders,
+ unsigned int,
+ unsigned int,
+ unsigned int);
+static int floatformat_always_valid (const struct floatformat *fmt,
+ const char *from);
+
+static int
+floatformat_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,
+ const char *from ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
+ going to bother with trying to muck around with whether it is defined in
+ a system header, what we do if not, etc. */
+#define FLOATFORMAT_CHAR_BIT 8
+
+/* floatformats for IEEE single and double, big and little endian. */
+const struct floatformat floatformat_ieee_single_big =
+{
+ floatformat_big, 32, 0, 1, 8, 127, 255, 9, 23,
+ floatformat_intbit_no,
+ "floatformat_ieee_single_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_single_little =
+{
+ floatformat_little, 32, 0, 1, 8, 127, 255, 9, 23,
+ floatformat_intbit_no,
+ "floatformat_ieee_single_little",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_double_big =
+{
+ floatformat_big, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_double_little =
+{
+ floatformat_little, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_little",
+ floatformat_always_valid
+};
+
+/* floatformat for IEEE double, little endian byte order, with big endian word
+ ordering, as on the ARM. */
+
+const struct floatformat floatformat_ieee_double_littlebyte_bigword =
+{
+ floatformat_littlebyte_bigword, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_littlebyte_bigword",
+ floatformat_always_valid
+};
+
+static int floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from);
+
+static int
+floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from)
+{
+ /* In the i387 double-extended format, if the exponent is all ones,
+ then the integer bit must be set. If the exponent is neither 0
+ nor ~0, the intbit must also be set. Only if the exponent is
+ zero can it be zero, and then it must be zero. */
+ unsigned long exponent, int_bit;
+ const unsigned char *ufrom = (const unsigned char *) from;
+
+ exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->exp_start, fmt->exp_len);
+ int_bit = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->man_start, 1);
+
+ if ((exponent == 0) != (int_bit == 0))
+ return 0;
+ else
+ return 1;
+}
+
+const struct floatformat floatformat_i387_ext =
+{
+ floatformat_little, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
+ floatformat_intbit_yes,
+ "floatformat_i387_ext",
+ floatformat_i387_ext_is_valid
+};
+const struct floatformat floatformat_m68881_ext =
+{
+ /* Note that the bits from 16 to 31 are unused. */
+ floatformat_big, 96, 0, 1, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_m68881_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_i960_ext =
+{
+ /* Note that the bits from 0 to 15 are unused. */
+ floatformat_little, 96, 16, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_i960_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_m88110_ext =
+{
+ floatformat_big, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
+ floatformat_intbit_yes,
+ "floatformat_m88110_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_m88110_harris_ext =
+{
+ /* Harris uses raw format 128 bytes long, but the number is just an ieee
+ double, and the last 64 bits are wasted. */
+ floatformat_big,128, 0, 1, 11, 0x3ff, 0x7ff, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_m88110_ext_harris",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_arm_ext_big =
+{
+ /* Bits 1 to 16 are unused. */
+ floatformat_big, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_arm_ext_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_arm_ext_littlebyte_bigword =
+{
+ /* Bits 1 to 16 are unused. */
+ floatformat_littlebyte_bigword, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_arm_ext_littlebyte_bigword",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_spill_big =
+{
+ floatformat_big, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
+ floatformat_intbit_yes,
+ "floatformat_ia64_spill_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_spill_little =
+{
+ floatformat_little, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
+ floatformat_intbit_yes,
+ "floatformat_ia64_spill_little",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_quad_big =
+{
+ floatformat_big, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
+ floatformat_intbit_no,
+ "floatformat_ia64_quad_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_quad_little =
+{
+ floatformat_little, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
+ floatformat_intbit_no,
+ "floatformat_ia64_quad_little",
+ floatformat_always_valid
+};
+
+/* Extract a field which starts at START and is LEN bits long. DATA and
+ TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
+static unsigned long
+get_field (const unsigned char *data, enum floatformat_byteorders order,
+ unsigned int total_len, unsigned int start, unsigned int len)
+{
+ unsigned long result;
+ unsigned int cur_byte;
+ int cur_bitshift;
+
+ /* Start at the least significant part of the field. */
+ cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
+ cur_bitshift =
+ ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+ result = *(data + cur_byte) >> (-cur_bitshift);
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+
+ /* Move towards the most significant part of the field. */
+ while ((unsigned int) cur_bitshift < len)
+ {
+ if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
+ /* This is the last byte; zero out the bits which are not part of
+ this field. */
+ result |=
+ (*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1))
+ << cur_bitshift;
+ else
+ result |= *(data + cur_byte) << cur_bitshift;
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+ }
+ return result;
+}
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Convert from FMT to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+void
+floatformat_to_double (const struct floatformat *fmt,
+ const char *from, double *to)
+{
+ const unsigned char *ufrom = (const unsigned char *)from;
+ double dto;
+ long exponent;
+ unsigned long mant;
+ unsigned int mant_bits, mant_off;
+ int mant_bits_left;
+ int special_exponent; /* It's a NaN, denorm or zero */
+
+ exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->exp_start, fmt->exp_len);
+
+ /* If the exponent indicates a NaN, we don't have information to
+ decide what to do. So we handle it like IEEE, except that we
+ don't try to preserve the type of NaN. FIXME. */
+ if ((unsigned long) exponent == fmt->exp_nan)
+ {
+ int nan;
+
+ mant_off = fmt->man_start;
+ mant_bits_left = fmt->man_len;
+ nan = 0;
+ while (mant_bits_left > 0)
+ {
+ mant_bits = min (mant_bits_left, 32);
+
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits) != 0)
+ {
+ /* This is a NaN. */
+ nan = 1;
+ break;
+ }
+
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+
+ /* On certain systems (such as GNU/Linux), the use of the
+ INFINITY macro below may generate a warning that can not be
+ silenced due to a bug in GCC (PR preprocessor/11931). The
+ preprocessor fails to recognise the __extension__ keyword in
+ conjunction with the GNU/C99 extension for hexadecimal
+ floating point constants and will issue a warning when
+ compiling with -pedantic. */
+ if (nan)
+ dto = NAN;
+ else
+ dto = INFINITY;
+
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
+ dto = -dto;
+
+ *to = dto;
+
+ return;
+ }
+
+ mant_bits_left = fmt->man_len;
+ mant_off = fmt->man_start;
+ dto = 0.0;
+
+ special_exponent = exponent == 0 || (unsigned long) exponent == fmt->exp_nan;
+
+ /* Don't bias zero's, denorms or NaNs. */
+ if (!special_exponent)
+ exponent -= fmt->exp_bias;
+
+ /* Build the result algebraically. Might go infinite, underflow, etc;
+ who cares. */
+
+ /* If this format uses a hidden bit, explicitly add it in now. Otherwise,
+ increment the exponent by one to account for the integer bit. */
+
+ if (!special_exponent)
+ {
+ if (fmt->intbit == floatformat_intbit_no)
+ dto = ldexp (1.0, exponent);
+ else
+ exponent++;
+ }
+
+ while (mant_bits_left > 0)
+ {
+ mant_bits = min (mant_bits_left, 32);
+
+ mant = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits);
+
+ /* Handle denormalized numbers. FIXME: What should we do for
+ non-IEEE formats? */
+ if (exponent == 0 && mant != 0)
+ dto += ldexp ((double)mant,
+ (- fmt->exp_bias
+ - mant_bits
+ - (mant_off - fmt->man_start)
+ + 1));
+ else
+ dto += ldexp ((double)mant, exponent - mant_bits);
+ if (exponent != 0)
+ exponent -= mant_bits;
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+
+ /* Negate it if negative. */
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
+ dto = -dto;
+ *to = dto;
+}
+
+static void put_field (unsigned char *, enum floatformat_byteorders,
+ unsigned int,
+ unsigned int,
+ unsigned int,
+ unsigned long);
+
+/* Set a field which starts at START and is LEN bits long. DATA and
+ TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
+static void
+put_field (unsigned char *data, enum floatformat_byteorders order,
+ unsigned int total_len, unsigned int start, unsigned int len,
+ unsigned long stuff_to_put)
+{
+ unsigned int cur_byte;
+ int cur_bitshift;
+
+ /* Start at the least significant part of the field. */
+ cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
+ cur_bitshift =
+ ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+ *(data + cur_byte) &=
+ ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) << (-cur_bitshift));
+ *(data + cur_byte) |=
+ (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+
+ /* Move towards the most significant part of the field. */
+ while ((unsigned int) cur_bitshift < len)
+ {
+ if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
+ {
+ /* This is the last byte. */
+ *(data + cur_byte) &=
+ ~((1 << (len - cur_bitshift)) - 1);
+ *(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
+ }
+ else
+ *(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
+ & ((1 << FLOATFORMAT_CHAR_BIT) - 1));
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+ }
+}
+
+/* The converse: convert the double *FROM to an extended float
+ and store where TO points. Neither FROM nor TO have any alignment
+ restrictions. */
+
+void
+floatformat_from_double (const struct floatformat *fmt,
+ const double *from, char *to)
+{
+ double dfrom;
+ int exponent;
+ double mant;
+ unsigned int mant_bits, mant_off;
+ int mant_bits_left;
+ unsigned char *uto = (unsigned char *)to;
+
+ dfrom = *from;
+ memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT);
+
+ /* If negative, set the sign bit. */
+ if (dfrom < 0)
+ {
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
+ dfrom = -dfrom;
+ }
+
+ if (dfrom == 0)
+ {
+ /* 0.0. */
+ return;
+ }
+
+ if (dfrom != dfrom)
+ {
+ /* NaN. */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, fmt->exp_nan);
+ /* Be sure it's not infinity, but NaN value is irrelevant. */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
+ 32, 1);
+ return;
+ }
+
+ if (dfrom + dfrom == dfrom)
+ {
+ /* This can only happen for an infinite value (or zero, which we
+ already handled above). */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, fmt->exp_nan);
+ return;
+ }
+
+ mant = frexp (dfrom, &exponent);
+ if (exponent + fmt->exp_bias - 1 > 0)
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, exponent + fmt->exp_bias - 1);
+ else
+ {
+ /* Handle a denormalized number. FIXME: What should we do for
+ non-IEEE formats? */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, 0);
+ mant = ldexp (mant, exponent + fmt->exp_bias - 1);
+ }
+
+ mant_bits_left = fmt->man_len;
+ mant_off = fmt->man_start;
+ while (mant_bits_left > 0)
+ {
+ unsigned long mant_long;
+ mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
+
+ mant *= 4294967296.0;
+ mant_long = (unsigned long)mant;
+ mant -= mant_long;
+
+ /* If the integer bit is implicit, and we are not creating a
+ denormalized number, then we need to discard it. */
+ if ((unsigned int) mant_bits_left == fmt->man_len
+ && fmt->intbit == floatformat_intbit_no
+ && exponent + fmt->exp_bias - 1 > 0)
+ {
+ mant_long &= 0x7fffffff;
+ mant_bits -= 1;
+ }
+ else if (mant_bits < 32)
+ {
+ /* The bits we want are in the most significant MANT_BITS bits of
+ mant_long. Move them to the least significant. */
+ mant_long >>= 32 - mant_bits;
+ }
+
+ put_field (uto, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits, mant_long);
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+}
+
+/* Return non-zero iff the data at FROM is a valid number in format FMT. */
+
+int
+floatformat_is_valid (const struct floatformat *fmt, const char *from)
+{
+ return fmt->is_valid (fmt, from);
+}
+
+
+#ifdef IEEE_DEBUG
+
+/* This is to be run on a host which uses IEEE floating point. */
+
+void
+ieee_test (double n)
+{
+ double result;
+
+ floatformat_to_double (&floatformat_ieee_double_little, (char *) &n,
+ &result);
+ if ((n != result && (! isnan (n) || ! isnan (result)))
+ || (n < 0 && result >= 0)
+ || (n >= 0 && result < 0))
+ printf ("Differ(to): %.20g -> %.20g\n", n, result);
+
+ floatformat_from_double (&floatformat_ieee_double_little, &n,
+ (char *) &result);
+ if ((n != result && (! isnan (n) || ! isnan (result)))
+ || (n < 0 && result >= 0)
+ || (n >= 0 && result < 0))
+ printf ("Differ(from): %.20g -> %.20g\n", n, result);
+
+#if 0
+ {
+ char exten[16];
+
+ floatformat_from_double (&floatformat_m68881_ext, &n, exten);
+ floatformat_to_double (&floatformat_m68881_ext, exten, &result);
+ if (n != result)
+ printf ("Differ(to+from): %.20g -> %.20g\n", n, result);
+ }
+#endif
+
+#if IEEE_DEBUG > 1
+ /* This is to be run on a host which uses 68881 format. */
+ {
+ long double ex = *(long double *)exten;
+ if (ex != n)
+ printf ("Differ(from vs. extended): %.20g\n", n);
+ }
+#endif
+}
+
+int
+main (void)
+{
+ ieee_test (0.0);
+ ieee_test (0.5);
+ ieee_test (256.0);
+ ieee_test (0.12345);
+ ieee_test (234235.78907234);
+ ieee_test (-512.0);
+ ieee_test (-0.004321);
+ ieee_test (1.2E-70);
+ ieee_test (1.2E-316);
+ ieee_test (4.9406564584124654E-324);
+ ieee_test (- 4.9406564584124654E-324);
+ ieee_test (- 0.0);
+ ieee_test (- INFINITY);
+ ieee_test (- NAN);
+ ieee_test (INFINITY);
+ ieee_test (NAN);
+ return 0;
+}
+#endif
+/* **** End of floatformat.c */
diff --git a/m68k.ld b/m68k.ld
new file mode 100644
index 0000000..28da902
--- /dev/null
+++ b/m68k.ld
@@ -0,0 +1,177 @@
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-m68k", "elf32-m68k",
+ "elf32-m68k")
+OUTPUT_ARCH(m68k)
+ENTRY(_start)
+SEARCH_DIR("/usr/local/m68k-linux/lib");
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x4e754e75
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x4e754e75
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x4e754e75
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x2000) + (. & (0x2000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}
diff --git a/mips-dis.c b/mips-dis.c
new file mode 100644
index 0000000..2e7dc85
--- /dev/null
+++ b/mips-dis.c
@@ -0,0 +1,3999 @@
+/* Print mips instructions for GDB, the GNU debugger, or for objdump.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+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. */
+
+#include "dis-asm.h"
+
+/* mips.h. Mips opcode list for GDB, the GNU debugger.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* mips.h. Mips opcode list for GDB, the GNU debugger.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* These are bit masks and shift counts to use to access the various
+ fields of an instruction. To retrieve the X field of an
+ instruction, use the expression
+ (i >> OP_SH_X) & OP_MASK_X
+ To set the same field (to j), use
+ i = (i &~ (OP_MASK_X << OP_SH_X)) | (j << OP_SH_X)
+
+ Make sure you use fields that are appropriate for the instruction,
+ of course.
+
+ The 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The 'j' format uses OP and TARGET.
+
+ The 'r' format uses OP, RS, RT, RD, SHAMT and FUNCT.
+
+ The 'b' format uses OP, RS, RT and DELTA.
+
+ The floating point 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The floating point 'r' format uses OP, FMT, FT, FS, FD and FUNCT.
+
+ A breakpoint instruction uses OP, CODE and SPEC (10 bits of the
+ breakpoint instruction are not defined; Kane says the breakpoint
+ code field in BREAK is 20 bits; yet MIPS assemblers and debuggers
+ only use ten bits). An optional two-operand form of break/sdbbp
+ allows the lower ten bits to be set too, and MIPS32 and later
+ architectures allow 20 bits to be set with a signal operand
+ (using CODE20).
+
+ The syscall instruction uses CODE20.
+
+ The general coprocessor instructions use COPZ. */
+
+#define OP_MASK_OP 0x3f
+#define OP_SH_OP 26
+#define OP_MASK_RS 0x1f
+#define OP_SH_RS 21
+#define OP_MASK_FR 0x1f
+#define OP_SH_FR 21
+#define OP_MASK_FMT 0x1f
+#define OP_SH_FMT 21
+#define OP_MASK_BCC 0x7
+#define OP_SH_BCC 18
+#define OP_MASK_CODE 0x3ff
+#define OP_SH_CODE 16
+#define OP_MASK_CODE2 0x3ff
+#define OP_SH_CODE2 6
+#define OP_MASK_RT 0x1f
+#define OP_SH_RT 16
+#define OP_MASK_FT 0x1f
+#define OP_SH_FT 16
+#define OP_MASK_CACHE 0x1f
+#define OP_SH_CACHE 16
+#define OP_MASK_RD 0x1f
+#define OP_SH_RD 11
+#define OP_MASK_FS 0x1f
+#define OP_SH_FS 11
+#define OP_MASK_PREFX 0x1f
+#define OP_SH_PREFX 11
+#define OP_MASK_CCC 0x7
+#define OP_SH_CCC 8
+#define OP_MASK_CODE20 0xfffff /* 20 bit syscall/breakpoint code. */
+#define OP_SH_CODE20 6
+#define OP_MASK_SHAMT 0x1f
+#define OP_SH_SHAMT 6
+#define OP_MASK_FD 0x1f
+#define OP_SH_FD 6
+#define OP_MASK_TARGET 0x3ffffff
+#define OP_SH_TARGET 0
+#define OP_MASK_COPZ 0x1ffffff
+#define OP_SH_COPZ 0
+#define OP_MASK_IMMEDIATE 0xffff
+#define OP_SH_IMMEDIATE 0
+#define OP_MASK_DELTA 0xffff
+#define OP_SH_DELTA 0
+#define OP_MASK_FUNCT 0x3f
+#define OP_SH_FUNCT 0
+#define OP_MASK_SPEC 0x3f
+#define OP_SH_SPEC 0
+#define OP_SH_LOCC 8 /* FP condition code. */
+#define OP_SH_HICC 18 /* FP condition code. */
+#define OP_MASK_CC 0x7
+#define OP_SH_COP1NORM 25 /* Normal COP1 encoding. */
+#define OP_MASK_COP1NORM 0x1 /* a single bit. */
+#define OP_SH_COP1SPEC 21 /* COP1 encodings. */
+#define OP_MASK_COP1SPEC 0xf
+#define OP_MASK_COP1SCLR 0x4
+#define OP_MASK_COP1CMP 0x3
+#define OP_SH_COP1CMP 4
+#define OP_SH_FORMAT 21 /* FP short format field. */
+#define OP_MASK_FORMAT 0x7
+#define OP_SH_TRUE 16
+#define OP_MASK_TRUE 0x1
+#define OP_SH_GE 17
+#define OP_MASK_GE 0x01
+#define OP_SH_UNSIGNED 16
+#define OP_MASK_UNSIGNED 0x1
+#define OP_SH_HINT 16
+#define OP_MASK_HINT 0x1f
+#define OP_SH_MMI 0 /* Multimedia (parallel) op. */
+#define OP_MASK_MMI 0x3f
+#define OP_SH_MMISUB 6
+#define OP_MASK_MMISUB 0x1f
+#define OP_MASK_PERFREG 0x1f /* Performance monitoring. */
+#define OP_SH_PERFREG 1
+#define OP_SH_SEL 0 /* Coprocessor select field. */
+#define OP_MASK_SEL 0x7 /* The sel field of mfcZ and mtcZ. */
+#define OP_SH_CODE19 6 /* 19 bit wait code. */
+#define OP_MASK_CODE19 0x7ffff
+#define OP_SH_ALN 21
+#define OP_MASK_ALN 0x7
+#define OP_SH_VSEL 21
+#define OP_MASK_VSEL 0x1f
+#define OP_MASK_VECBYTE 0x7 /* Selector field is really 4 bits,
+ but 0x8-0xf don't select bytes. */
+#define OP_SH_VECBYTE 22
+#define OP_MASK_VECALIGN 0x7 /* Vector byte-align (alni.ob) op. */
+#define OP_SH_VECALIGN 21
+#define OP_MASK_INSMSB 0x1f /* "ins" MSB. */
+#define OP_SH_INSMSB 11
+#define OP_MASK_EXTMSBD 0x1f /* "ext" MSBD. */
+#define OP_SH_EXTMSBD 11
+
+#define OP_OP_COP0 0x10
+#define OP_OP_COP1 0x11
+#define OP_OP_COP2 0x12
+#define OP_OP_COP3 0x13
+#define OP_OP_LWC1 0x31
+#define OP_OP_LWC2 0x32
+#define OP_OP_LWC3 0x33 /* a.k.a. pref */
+#define OP_OP_LDC1 0x35
+#define OP_OP_LDC2 0x36
+#define OP_OP_LDC3 0x37 /* a.k.a. ld */
+#define OP_OP_SWC1 0x39
+#define OP_OP_SWC2 0x3a
+#define OP_OP_SWC3 0x3b
+#define OP_OP_SDC1 0x3d
+#define OP_OP_SDC2 0x3e
+#define OP_OP_SDC3 0x3f /* a.k.a. sd */
+
+/* Values in the 'VSEL' field. */
+#define MDMX_FMTSEL_IMM_QH 0x1d
+#define MDMX_FMTSEL_IMM_OB 0x1e
+#define MDMX_FMTSEL_VEC_QH 0x15
+#define MDMX_FMTSEL_VEC_OB 0x16
+
+/* This structure holds information for a particular instruction. */
+
+struct mips_opcode
+{
+ /* The name of the instruction. */
+ const char *name;
+ /* A string describing the arguments for this instruction. */
+ const char *args;
+ /* The basic opcode for the instruction. When assembling, this
+ opcode is modified by the arguments to produce the actual opcode
+ that is used. If pinfo is INSN_MACRO, then this is 0. */
+ unsigned long match;
+ /* If pinfo is not INSN_MACRO, then this is a bit mask for the
+ relevant portions of the opcode when disassembling. If the
+ actual opcode anded with the match field equals the opcode field,
+ then we have found the correct instruction. If pinfo is
+ INSN_MACRO, then this field is the macro identifier. */
+ unsigned long mask;
+ /* For a macro, this is INSN_MACRO. Otherwise, it is a collection
+ of bits describing the instruction, notably any relevant hazard
+ information. */
+ unsigned long pinfo;
+ /* A collection of bits describing the instruction sets of which this
+ instruction or macro is a member. */
+ unsigned long membership;
+};
+
+/* These are the characters which may appear in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ Each of these characters corresponds to a mask field defined above.
+
+ "<" 5 bit shift amount (OP_*_SHAMT)
+ ">" shift amount between 32 and 63, stored after subtracting 32 (OP_*_SHAMT)
+ "a" 26 bit target address (OP_*_TARGET)
+ "b" 5 bit base register (OP_*_RS)
+ "c" 10 bit breakpoint code (OP_*_CODE)
+ "d" 5 bit destination register specifier (OP_*_RD)
+ "h" 5 bit prefx hint (OP_*_PREFX)
+ "i" 16 bit unsigned immediate (OP_*_IMMEDIATE)
+ "j" 16 bit signed immediate (OP_*_DELTA)
+ "k" 5 bit cache opcode in target register position (OP_*_CACHE)
+ Also used for immediate operands in vr5400 vector insns.
+ "o" 16 bit signed offset (OP_*_DELTA)
+ "p" 16 bit PC relative branch target address (OP_*_DELTA)
+ "q" 10 bit extra breakpoint code (OP_*_CODE2)
+ "r" 5 bit same register used as both source and target (OP_*_RS)
+ "s" 5 bit source register specifier (OP_*_RS)
+ "t" 5 bit target register (OP_*_RT)
+ "u" 16 bit upper 16 bits of address (OP_*_IMMEDIATE)
+ "v" 5 bit same register used as both source and destination (OP_*_RS)
+ "w" 5 bit same register used as both target and destination (OP_*_RT)
+ "U" 5 bit same destination register in both OP_*_RD and OP_*_RT
+ (used by clo and clz)
+ "C" 25 bit coprocessor function code (OP_*_COPZ)
+ "B" 20 bit syscall/breakpoint function code (OP_*_CODE20)
+ "J" 19 bit wait function code (OP_*_CODE19)
+ "x" accept and ignore register name
+ "z" must be zero register
+ "K" 5 bit Hardware Register (rdhwr instruction) (OP_*_RD)
+ "+A" 5 bit ins/ext position, which becomes LSB (OP_*_SHAMT).
+ Enforces: 0 <= pos < 32.
+ "+B" 5 bit ins size, which becomes MSB (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ "+C" 5 bit ext size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ (Also used by "dext" w/ different limits, but limits for
+ that are checked by the M_DEXT macro.)
+ "+E" 5 bit dins/dext position, which becomes LSB-32 (OP_*_SHAMT).
+ Enforces: 32 <= pos < 64.
+ "+F" 5 bit "dinsm" size, which becomes MSB-32 (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+G" 5 bit "dextm" size, which becomes MSBD-32 (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+H" 5 bit "dextu" size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+
+ Floating point instructions:
+ "D" 5 bit destination register (OP_*_FD)
+ "M" 3 bit compare condition code (OP_*_CCC) (only used for mips4 and up)
+ "N" 3 bit branch condition code (OP_*_BCC) (only used for mips4 and up)
+ "S" 5 bit fs source 1 register (OP_*_FS)
+ "T" 5 bit ft source 2 register (OP_*_FT)
+ "R" 5 bit fr source 3 register (OP_*_FR)
+ "V" 5 bit same register used as floating source and destination (OP_*_FS)
+ "W" 5 bit same register used as floating target and destination (OP_*_FT)
+
+ Coprocessor instructions:
+ "E" 5 bit target register (OP_*_RT)
+ "G" 5 bit destination register (OP_*_RD)
+ "H" 3 bit sel field for (d)mtc* and (d)mfc* (OP_*_SEL)
+ "P" 5 bit performance-monitor register (OP_*_PERFREG)
+ "e" 5 bit vector register byte specifier (OP_*_VECBYTE)
+ "%" 3 bit immediate vr5400 vector alignment operand (OP_*_VECALIGN)
+ see also "k" above
+ "+D" Combined destination register ("G") and sel ("H") for CP0 ops,
+ for pretty-printing in disassembly only.
+
+ Macro instructions:
+ "A" General 32 bit expression
+ "I" 32 bit immediate (value placed in imm_expr).
+ "+I" 32 bit immediate (value placed in imm2_expr).
+ "F" 64 bit floating point constant in .rdata
+ "L" 64 bit floating point constant in .lit8
+ "f" 32 bit floating point constant
+ "l" 32 bit floating point constant in .lit4
+
+ MDMX instruction operands (note that while these use the FP register
+ fields, they accept both $fN and $vN names for the registers):
+ "O" MDMX alignment offset (OP_*_ALN)
+ "Q" MDMX vector/scalar/immediate source (OP_*_VSEL and OP_*_FT)
+ "X" MDMX destination register (OP_*_FD)
+ "Y" MDMX source register (OP_*_FS)
+ "Z" MDMX source register (OP_*_FT)
+
+ Other:
+ "()" parens surrounding optional value
+ "," separates operands
+ "[]" brackets around index for vector-op scalar operand specifier (vr5400)
+ "+" Start of extension sequence.
+
+ Characters used so far, for quick reference when adding more:
+ "%[]<>(),+"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefhijklopqrstuvwxz"
+
+ Extension character sequences used so far ("+" followed by the
+ following), for quick reference when adding more:
+ "ABCDEFGHI"
+*/
+
+/* These are the bits which may be set in the pinfo field of an
+ instructions, if it is not equal to INSN_MACRO. */
+
+/* Modifies the general purpose register in OP_*_RD. */
+#define INSN_WRITE_GPR_D 0x00000001
+/* Modifies the general purpose register in OP_*_RT. */
+#define INSN_WRITE_GPR_T 0x00000002
+/* Modifies general purpose register 31. */
+#define INSN_WRITE_GPR_31 0x00000004
+/* Modifies the floating point register in OP_*_FD. */
+#define INSN_WRITE_FPR_D 0x00000008
+/* Modifies the floating point register in OP_*_FS. */
+#define INSN_WRITE_FPR_S 0x00000010
+/* Modifies the floating point register in OP_*_FT. */
+#define INSN_WRITE_FPR_T 0x00000020
+/* Reads the general purpose register in OP_*_RS. */
+#define INSN_READ_GPR_S 0x00000040
+/* Reads the general purpose register in OP_*_RT. */
+#define INSN_READ_GPR_T 0x00000080
+/* Reads the floating point register in OP_*_FS. */
+#define INSN_READ_FPR_S 0x00000100
+/* Reads the floating point register in OP_*_FT. */
+#define INSN_READ_FPR_T 0x00000200
+/* Reads the floating point register in OP_*_FR. */
+#define INSN_READ_FPR_R 0x00000400
+/* Modifies coprocessor condition code. */
+#define INSN_WRITE_COND_CODE 0x00000800
+/* Reads coprocessor condition code. */
+#define INSN_READ_COND_CODE 0x00001000
+/* TLB operation. */
+#define INSN_TLB 0x00002000
+/* Reads coprocessor register other than floating point register. */
+#define INSN_COP 0x00004000
+/* Instruction loads value from memory, requiring delay. */
+#define INSN_LOAD_MEMORY_DELAY 0x00008000
+/* Instruction loads value from coprocessor, requiring delay. */
+#define INSN_LOAD_COPROC_DELAY 0x00010000
+/* Instruction has unconditional branch delay slot. */
+#define INSN_UNCOND_BRANCH_DELAY 0x00020000
+/* Instruction has conditional branch delay slot. */
+#define INSN_COND_BRANCH_DELAY 0x00040000
+/* Conditional branch likely: if branch not taken, insn nullified. */
+#define INSN_COND_BRANCH_LIKELY 0x00080000
+/* Moves to coprocessor register, requiring delay. */
+#define INSN_COPROC_MOVE_DELAY 0x00100000
+/* Loads coprocessor register from memory, requiring delay. */
+#define INSN_COPROC_MEMORY_DELAY 0x00200000
+/* Reads the HI register. */
+#define INSN_READ_HI 0x00400000
+/* Reads the LO register. */
+#define INSN_READ_LO 0x00800000
+/* Modifies the HI register. */
+#define INSN_WRITE_HI 0x01000000
+/* Modifies the LO register. */
+#define INSN_WRITE_LO 0x02000000
+/* Takes a trap (easier to keep out of delay slot). */
+#define INSN_TRAP 0x04000000
+/* Instruction stores value into memory. */
+#define INSN_STORE_MEMORY 0x08000000
+/* Instruction uses single precision floating point. */
+#define FP_S 0x10000000
+/* Instruction uses double precision floating point. */
+#define FP_D 0x20000000
+/* Instruction is part of the tx39's integer multiply family. */
+#define INSN_MULT 0x40000000
+/* Instruction synchronize shared memory. */
+#define INSN_SYNC 0x80000000
+/* Instruction reads MDMX accumulator. XXX FIXME: No bits left! */
+#define INSN_READ_MDMX_ACC 0
+/* Instruction writes MDMX accumulator. XXX FIXME: No bits left! */
+#define INSN_WRITE_MDMX_ACC 0
+
+/* Instruction is actually a macro. It should be ignored by the
+ disassembler, and requires special treatment by the assembler. */
+#define INSN_MACRO 0xffffffff
+
+/* Masks used to mark instructions to indicate which MIPS ISA level
+ they were introduced in. ISAs, as defined below, are logical
+ ORs of these bits, indicating that they support the instructions
+ defined at the given level. */
+
+#define INSN_ISA_MASK 0x00000fff
+#define INSN_ISA1 0x00000001
+#define INSN_ISA2 0x00000002
+#define INSN_ISA3 0x00000004
+#define INSN_ISA4 0x00000008
+#define INSN_ISA5 0x00000010
+#define INSN_ISA32 0x00000020
+#define INSN_ISA64 0x00000040
+#define INSN_ISA32R2 0x00000080
+#define INSN_ISA64R2 0x00000100
+
+/* Masks used for MIPS-defined ASEs. */
+#define INSN_ASE_MASK 0x0000f000
+
+/* MIPS 16 ASE */
+#define INSN_MIPS16 0x00002000
+/* MIPS-3D ASE */
+#define INSN_MIPS3D 0x00004000
+/* MDMX ASE */
+#define INSN_MDMX 0x00008000
+
+/* Chip specific instructions. These are bitmasks. */
+
+/* MIPS R4650 instruction. */
+#define INSN_4650 0x00010000
+/* LSI R4010 instruction. */
+#define INSN_4010 0x00020000
+/* NEC VR4100 instruction. */
+#define INSN_4100 0x00040000
+/* Toshiba R3900 instruction. */
+#define INSN_3900 0x00080000
+/* MIPS R10000 instruction. */
+#define INSN_10000 0x00100000
+/* Broadcom SB-1 instruction. */
+#define INSN_SB1 0x00200000
+/* NEC VR4111/VR4181 instruction. */
+#define INSN_4111 0x00400000
+/* NEC VR4120 instruction. */
+#define INSN_4120 0x00800000
+/* NEC VR5400 instruction. */
+#define INSN_5400 0x01000000
+/* NEC VR5500 instruction. */
+#define INSN_5500 0x02000000
+
+/* MIPS ISA defines, use instead of hardcoding ISA level. */
+
+#define ISA_UNKNOWN 0 /* Gas internal use. */
+#define ISA_MIPS1 (INSN_ISA1)
+#define ISA_MIPS2 (ISA_MIPS1 | INSN_ISA2)
+#define ISA_MIPS3 (ISA_MIPS2 | INSN_ISA3)
+#define ISA_MIPS4 (ISA_MIPS3 | INSN_ISA4)
+#define ISA_MIPS5 (ISA_MIPS4 | INSN_ISA5)
+
+#define ISA_MIPS32 (ISA_MIPS2 | INSN_ISA32)
+#define ISA_MIPS64 (ISA_MIPS5 | INSN_ISA32 | INSN_ISA64)
+
+#define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2)
+#define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2)
+
+
+/* CPU defines, use instead of hardcoding processor number. Keep this
+ in sync with bfd/archures.c in order for machine selection to work. */
+#define CPU_UNKNOWN 0 /* Gas internal use. */
+#define CPU_R3000 3000
+#define CPU_R3900 3900
+#define CPU_R4000 4000
+#define CPU_R4010 4010
+#define CPU_VR4100 4100
+#define CPU_R4111 4111
+#define CPU_VR4120 4120
+#define CPU_R4300 4300
+#define CPU_R4400 4400
+#define CPU_R4600 4600
+#define CPU_R4650 4650
+#define CPU_R5000 5000
+#define CPU_VR5400 5400
+#define CPU_VR5500 5500
+#define CPU_R6000 6000
+#define CPU_RM7000 7000
+#define CPU_R8000 8000
+#define CPU_R10000 10000
+#define CPU_R12000 12000
+#define CPU_MIPS16 16
+#define CPU_MIPS32 32
+#define CPU_MIPS32R2 33
+#define CPU_MIPS5 5
+#define CPU_MIPS64 64
+#define CPU_MIPS64R2 65
+#define CPU_SB1 12310201 /* octal 'SB', 01. */
+
+/* Test for membership in an ISA including chip specific ISAs. INSN
+ is pointer to an element of the opcode table; ISA is the specified
+ ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to
+ test, or zero if no CPU specific ISA test is desired. */
+
+#if 0
+#define OPCODE_IS_MEMBER(insn, isa, cpu) \
+ (((insn)->membership & isa) != 0 \
+ || (cpu == CPU_R4650 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_RM7000 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_R4010 && ((insn)->membership & INSN_4010) != 0) \
+ || (cpu == CPU_VR4100 && ((insn)->membership & INSN_4100) != 0) \
+ || (cpu == CPU_R3900 && ((insn)->membership & INSN_3900) != 0) \
+ || ((cpu == CPU_R10000 || cpu == CPU_R12000) \
+ && ((insn)->membership & INSN_10000) != 0) \
+ || (cpu == CPU_SB1 && ((insn)->membership & INSN_SB1) != 0) \
+ || (cpu == CPU_R4111 && ((insn)->membership & INSN_4111) != 0) \
+ || (cpu == CPU_VR4120 && ((insn)->membership & INSN_4120) != 0) \
+ || (cpu == CPU_VR5400 && ((insn)->membership & INSN_5400) != 0) \
+ || (cpu == CPU_VR5500 && ((insn)->membership & INSN_5500) != 0) \
+ || 0) /* Please keep this term for easier source merging. */
+#else
+#define OPCODE_IS_MEMBER(insn, isa, cpu) \
+ (1 != 0)
+#endif
+
+/* This is a list of macro expanded instructions.
+
+ _I appended means immediate
+ _A appended means address
+ _AB appended means address with base register
+ _D appended means 64 bit floating point constant
+ _S appended means 32 bit floating point constant. */
+
+enum
+{
+ M_ABS,
+ M_ADD_I,
+ M_ADDU_I,
+ M_AND_I,
+ M_BEQ,
+ M_BEQ_I,
+ M_BEQL_I,
+ M_BGE,
+ M_BGEL,
+ M_BGE_I,
+ M_BGEL_I,
+ M_BGEU,
+ M_BGEUL,
+ M_BGEU_I,
+ M_BGEUL_I,
+ M_BGT,
+ M_BGTL,
+ M_BGT_I,
+ M_BGTL_I,
+ M_BGTU,
+ M_BGTUL,
+ M_BGTU_I,
+ M_BGTUL_I,
+ M_BLE,
+ M_BLEL,
+ M_BLE_I,
+ M_BLEL_I,
+ M_BLEU,
+ M_BLEUL,
+ M_BLEU_I,
+ M_BLEUL_I,
+ M_BLT,
+ M_BLTL,
+ M_BLT_I,
+ M_BLTL_I,
+ M_BLTU,
+ M_BLTUL,
+ M_BLTU_I,
+ M_BLTUL_I,
+ M_BNE,
+ M_BNE_I,
+ M_BNEL_I,
+ M_DABS,
+ M_DADD_I,
+ M_DADDU_I,
+ M_DDIV_3,
+ M_DDIV_3I,
+ M_DDIVU_3,
+ M_DDIVU_3I,
+ M_DEXT,
+ M_DINS,
+ M_DIV_3,
+ M_DIV_3I,
+ M_DIVU_3,
+ M_DIVU_3I,
+ M_DLA_AB,
+ M_DLCA_AB,
+ M_DLI,
+ M_DMUL,
+ M_DMUL_I,
+ M_DMULO,
+ M_DMULO_I,
+ M_DMULOU,
+ M_DMULOU_I,
+ M_DREM_3,
+ M_DREM_3I,
+ M_DREMU_3,
+ M_DREMU_3I,
+ M_DSUB_I,
+ M_DSUBU_I,
+ M_DSUBU_I_2,
+ M_J_A,
+ M_JAL_1,
+ M_JAL_2,
+ M_JAL_A,
+ M_L_DOB,
+ M_L_DAB,
+ M_LA_AB,
+ M_LB_A,
+ M_LB_AB,
+ M_LBU_A,
+ M_LBU_AB,
+ M_LCA_AB,
+ M_LD_A,
+ M_LD_OB,
+ M_LD_AB,
+ M_LDC1_AB,
+ M_LDC2_AB,
+ M_LDC3_AB,
+ M_LDL_AB,
+ M_LDR_AB,
+ M_LH_A,
+ M_LH_AB,
+ M_LHU_A,
+ M_LHU_AB,
+ M_LI,
+ M_LI_D,
+ M_LI_DD,
+ M_LI_S,
+ M_LI_SS,
+ M_LL_AB,
+ M_LLD_AB,
+ M_LS_A,
+ M_LW_A,
+ M_LW_AB,
+ M_LWC0_A,
+ M_LWC0_AB,
+ M_LWC1_A,
+ M_LWC1_AB,
+ M_LWC2_A,
+ M_LWC2_AB,
+ M_LWC3_A,
+ M_LWC3_AB,
+ M_LWL_A,
+ M_LWL_AB,
+ M_LWR_A,
+ M_LWR_AB,
+ M_LWU_AB,
+ M_MOVE,
+ M_MUL,
+ M_MUL_I,
+ M_MULO,
+ M_MULO_I,
+ M_MULOU,
+ M_MULOU_I,
+ M_NOR_I,
+ M_OR_I,
+ M_REM_3,
+ M_REM_3I,
+ M_REMU_3,
+ M_REMU_3I,
+ M_DROL,
+ M_ROL,
+ M_DROL_I,
+ M_ROL_I,
+ M_DROR,
+ M_ROR,
+ M_DROR_I,
+ M_ROR_I,
+ M_S_DA,
+ M_S_DOB,
+ M_S_DAB,
+ M_S_S,
+ M_SC_AB,
+ M_SCD_AB,
+ M_SD_A,
+ M_SD_OB,
+ M_SD_AB,
+ M_SDC1_AB,
+ M_SDC2_AB,
+ M_SDC3_AB,
+ M_SDL_AB,
+ M_SDR_AB,
+ M_SEQ,
+ M_SEQ_I,
+ M_SGE,
+ M_SGE_I,
+ M_SGEU,
+ M_SGEU_I,
+ M_SGT,
+ M_SGT_I,
+ M_SGTU,
+ M_SGTU_I,
+ M_SLE,
+ M_SLE_I,
+ M_SLEU,
+ M_SLEU_I,
+ M_SLT_I,
+ M_SLTU_I,
+ M_SNE,
+ M_SNE_I,
+ M_SB_A,
+ M_SB_AB,
+ M_SH_A,
+ M_SH_AB,
+ M_SW_A,
+ M_SW_AB,
+ M_SWC0_A,
+ M_SWC0_AB,
+ M_SWC1_A,
+ M_SWC1_AB,
+ M_SWC2_A,
+ M_SWC2_AB,
+ M_SWC3_A,
+ M_SWC3_AB,
+ M_SWL_A,
+ M_SWL_AB,
+ M_SWR_A,
+ M_SWR_AB,
+ M_SUB_I,
+ M_SUBU_I,
+ M_SUBU_I_2,
+ M_TEQ_I,
+ M_TGE_I,
+ M_TGEU_I,
+ M_TLT_I,
+ M_TLTU_I,
+ M_TNE_I,
+ M_TRUNCWD,
+ M_TRUNCWS,
+ M_ULD,
+ M_ULD_A,
+ M_ULH,
+ M_ULH_A,
+ M_ULHU,
+ M_ULHU_A,
+ M_ULW,
+ M_ULW_A,
+ M_USH,
+ M_USH_A,
+ M_USW,
+ M_USW_A,
+ M_USD,
+ M_USD_A,
+ M_XOR_I,
+ M_COP0,
+ M_COP1,
+ M_COP2,
+ M_COP3,
+ M_NUM_MACROS
+};
+
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+extern const struct mips_opcode mips_builtin_opcodes[];
+extern const int bfd_mips_num_builtin_opcodes;
+extern struct mips_opcode *mips_opcodes;
+extern int bfd_mips_num_opcodes;
+#define NUMOPCODES bfd_mips_num_opcodes
+
+
+/* The rest of this file adds definitions for the mips16 TinyRISC
+ processor. */
+
+/* These are the bitmasks and shift counts used for the different
+ fields in the instruction formats. Other than OP, no masks are
+ provided for the fixed portions of an instruction, since they are
+ not needed.
+
+ The I format uses IMM11.
+
+ The RI format uses RX and IMM8.
+
+ The RR format uses RX, and RY.
+
+ The RRI format uses RX, RY, and IMM5.
+
+ The RRR format uses RX, RY, and RZ.
+
+ The RRI_A format uses RX, RY, and IMM4.
+
+ The SHIFT format uses RX, RY, and SHAMT.
+
+ The I8 format uses IMM8.
+
+ The I8_MOVR32 format uses RY and REGR32.
+
+ The IR_MOV32R format uses REG32R and MOV32Z.
+
+ The I64 format uses IMM8.
+
+ The RI64 format uses RY and IMM5.
+ */
+
+#define MIPS16OP_MASK_OP 0x1f
+#define MIPS16OP_SH_OP 11
+#define MIPS16OP_MASK_IMM11 0x7ff
+#define MIPS16OP_SH_IMM11 0
+#define MIPS16OP_MASK_RX 0x7
+#define MIPS16OP_SH_RX 8
+#define MIPS16OP_MASK_IMM8 0xff
+#define MIPS16OP_SH_IMM8 0
+#define MIPS16OP_MASK_RY 0x7
+#define MIPS16OP_SH_RY 5
+#define MIPS16OP_MASK_IMM5 0x1f
+#define MIPS16OP_SH_IMM5 0
+#define MIPS16OP_MASK_RZ 0x7
+#define MIPS16OP_SH_RZ 2
+#define MIPS16OP_MASK_IMM4 0xf
+#define MIPS16OP_SH_IMM4 0
+#define MIPS16OP_MASK_REGR32 0x1f
+#define MIPS16OP_SH_REGR32 0
+#define MIPS16OP_MASK_REG32R 0x1f
+#define MIPS16OP_SH_REG32R 3
+#define MIPS16OP_EXTRACT_REG32R(i) ((((i) >> 5) & 7) | ((i) & 0x18))
+#define MIPS16OP_MASK_MOVE32Z 0x7
+#define MIPS16OP_SH_MOVE32Z 0
+#define MIPS16OP_MASK_IMM6 0x3f
+#define MIPS16OP_SH_IMM6 5
+
+/* These are the characters which may appears in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ "y" 3 bit register (MIPS16OP_*_RY)
+ "x" 3 bit register (MIPS16OP_*_RX)
+ "z" 3 bit register (MIPS16OP_*_RZ)
+ "Z" 3 bit register (MIPS16OP_*_MOVE32Z)
+ "v" 3 bit same register as source and destination (MIPS16OP_*_RX)
+ "w" 3 bit same register as source and destination (MIPS16OP_*_RY)
+ "0" zero register ($0)
+ "S" stack pointer ($sp or $29)
+ "P" program counter
+ "R" return address register ($ra or $31)
+ "X" 5 bit MIPS register (MIPS16OP_*_REGR32)
+ "Y" 5 bit MIPS register (MIPS16OP_*_REG32R)
+ "6" 6 bit unsigned break code (MIPS16OP_*_IMM6)
+ "a" 26 bit jump address
+ "e" 11 bit extension value
+ "l" register list for entry instruction
+ "L" register list for exit instruction
+
+ The remaining codes may be extended. Except as otherwise noted,
+ the full extended operand is a 16 bit signed value.
+ "<" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 5 bit unsigned)
+ ">" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 5 bit unsigned)
+ "[" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 6 bit unsigned)
+ "]" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 6 bit unsigned)
+ "4" 4 bit signed immediate * 0 (MIPS16OP_*_IMM4) (full 15 bit signed)
+ "5" 5 bit unsigned immediate * 0 (MIPS16OP_*_IMM5)
+ "H" 5 bit unsigned immediate * 2 (MIPS16OP_*_IMM5)
+ "W" 5 bit unsigned immediate * 4 (MIPS16OP_*_IMM5)
+ "D" 5 bit unsigned immediate * 8 (MIPS16OP_*_IMM5)
+ "j" 5 bit signed immediate * 0 (MIPS16OP_*_IMM5)
+ "8" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8)
+ "V" 8 bit unsigned immediate * 4 (MIPS16OP_*_IMM8)
+ "C" 8 bit unsigned immediate * 8 (MIPS16OP_*_IMM8)
+ "U" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) (full 16 bit unsigned)
+ "k" 8 bit signed immediate * 0 (MIPS16OP_*_IMM8)
+ "K" 8 bit signed immediate * 8 (MIPS16OP_*_IMM8)
+ "p" 8 bit conditional branch address (MIPS16OP_*_IMM8)
+ "q" 11 bit branch address (MIPS16OP_*_IMM11)
+ "A" 8 bit PC relative address * 4 (MIPS16OP_*_IMM8)
+ "B" 5 bit PC relative address * 8 (MIPS16OP_*_IMM5)
+ "E" 5 bit PC relative address * 4 (MIPS16OP_*_IMM5)
+ */
+
+/* For the mips16, we use the same opcode table format and a few of
+ the same flags. However, most of the flags are different. */
+
+/* Modifies the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_WRITE_X 0x00000001
+/* Modifies the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_WRITE_Y 0x00000002
+/* Modifies the register in MIPS16OP_*_RZ. */
+#define MIPS16_INSN_WRITE_Z 0x00000004
+/* Modifies the T ($24) register. */
+#define MIPS16_INSN_WRITE_T 0x00000008
+/* Modifies the SP ($29) register. */
+#define MIPS16_INSN_WRITE_SP 0x00000010
+/* Modifies the RA ($31) register. */
+#define MIPS16_INSN_WRITE_31 0x00000020
+/* Modifies the general purpose register in MIPS16OP_*_REG32R. */
+#define MIPS16_INSN_WRITE_GPR_Y 0x00000040
+/* Reads the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_READ_X 0x00000080
+/* Reads the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_READ_Y 0x00000100
+/* Reads the register in MIPS16OP_*_MOVE32Z. */
+#define MIPS16_INSN_READ_Z 0x00000200
+/* Reads the T ($24) register. */
+#define MIPS16_INSN_READ_T 0x00000400
+/* Reads the SP ($29) register. */
+#define MIPS16_INSN_READ_SP 0x00000800
+/* Reads the RA ($31) register. */
+#define MIPS16_INSN_READ_31 0x00001000
+/* Reads the program counter. */
+#define MIPS16_INSN_READ_PC 0x00002000
+/* Reads the general purpose register in MIPS16OP_*_REGR32. */
+#define MIPS16_INSN_READ_GPR_X 0x00004000
+/* Is a branch insn. */
+#define MIPS16_INSN_BRANCH 0x00010000
+
+/* The following flags have the same value for the mips16 opcode
+ table:
+ INSN_UNCOND_BRANCH_DELAY
+ INSN_COND_BRANCH_DELAY
+ INSN_COND_BRANCH_LIKELY (never used)
+ INSN_READ_HI
+ INSN_READ_LO
+ INSN_WRITE_HI
+ INSN_WRITE_LO
+ INSN_TRAP
+ INSN_ISA3
+ */
+
+extern const struct mips_opcode mips16_opcodes[];
+extern const int bfd_mips16_num_opcodes;
+
+/* Short hand so the lines aren't too long. */
+
+#define LDD INSN_LOAD_MEMORY_DELAY
+#define LCD INSN_LOAD_COPROC_DELAY
+#define UBD INSN_UNCOND_BRANCH_DELAY
+#define CBD INSN_COND_BRANCH_DELAY
+#define COD INSN_COPROC_MOVE_DELAY
+#define CLD INSN_COPROC_MEMORY_DELAY
+#define CBL INSN_COND_BRANCH_LIKELY
+#define TRAP INSN_TRAP
+#define SM INSN_STORE_MEMORY
+
+#define WR_d INSN_WRITE_GPR_D
+#define WR_t INSN_WRITE_GPR_T
+#define WR_31 INSN_WRITE_GPR_31
+#define WR_D INSN_WRITE_FPR_D
+#define WR_T INSN_WRITE_FPR_T
+#define WR_S INSN_WRITE_FPR_S
+#define RD_s INSN_READ_GPR_S
+#define RD_b INSN_READ_GPR_S
+#define RD_t INSN_READ_GPR_T
+#define RD_S INSN_READ_FPR_S
+#define RD_T INSN_READ_FPR_T
+#define RD_R INSN_READ_FPR_R
+#define WR_CC INSN_WRITE_COND_CODE
+#define RD_CC INSN_READ_COND_CODE
+#define RD_C0 INSN_COP
+#define RD_C1 INSN_COP
+#define RD_C2 INSN_COP
+#define RD_C3 INSN_COP
+#define WR_C0 INSN_COP
+#define WR_C1 INSN_COP
+#define WR_C2 INSN_COP
+#define WR_C3 INSN_COP
+
+#define WR_HI INSN_WRITE_HI
+#define RD_HI INSN_READ_HI
+#define MOD_HI WR_HI|RD_HI
+
+#define WR_LO INSN_WRITE_LO
+#define RD_LO INSN_READ_LO
+#define MOD_LO WR_LO|RD_LO
+
+#define WR_HILO WR_HI|WR_LO
+#define RD_HILO RD_HI|RD_LO
+#define MOD_HILO WR_HILO|RD_HILO
+
+#define IS_M INSN_MULT
+
+#define WR_MACC INSN_WRITE_MDMX_ACC
+#define RD_MACC INSN_READ_MDMX_ACC
+
+#define I1 INSN_ISA1
+#define I2 INSN_ISA2
+#define I3 INSN_ISA3
+#define I4 INSN_ISA4
+#define I5 INSN_ISA5
+#define I32 INSN_ISA32
+#define I64 INSN_ISA64
+#define I33 INSN_ISA32R2
+#define I65 INSN_ISA64R2
+
+/* MIPS64 MIPS-3D ASE support. */
+#define I16 INSN_MIPS16
+
+/* MIPS64 MIPS-3D ASE support. */
+#define M3D INSN_MIPS3D
+
+/* MIPS64 MDMX ASE support. */
+#define MX INSN_MDMX
+
+#define P3 INSN_4650
+#define L1 INSN_4010
+#define V1 (INSN_4100 | INSN_4111 | INSN_4120)
+#define T3 INSN_3900
+#define M1 INSN_10000
+#define SB1 INSN_SB1
+#define N411 INSN_4111
+#define N412 INSN_4120
+#define N5 (INSN_5400 | INSN_5500)
+#define N54 INSN_5400
+#define N55 INSN_5500
+
+#define G1 (T3 \
+ )
+
+#define G2 (T3 \
+ )
+
+#define G3 (I4 \
+ )
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Because of the lookup algorithm used, entries with the same opcode
+ name must be contiguous.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+const struct mips_opcode mips_builtin_opcodes[] =
+{
+/* These instructions appear first so that the disassembler will find
+ them first. The assemblers uses a hash table based on the
+ instruction name anyhow. */
+/* name, args, match, mask, pinfo, membership */
+{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, I4|I32|G3 },
+{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, I4 },
+{"nop", "", 0x00000000, 0xffffffff, 0, I1 }, /* sll */
+{"ssnop", "", 0x00000040, 0xffffffff, 0, I32|N55 }, /* sll */
+{"ehb", "", 0x000000c0, 0xffffffff, 0, I33 }, /* sll */
+{"li", "t,j", 0x24000000, 0xffe00000, WR_t, I1 }, /* addiu */
+{"li", "t,i", 0x34000000, 0xffe00000, WR_t, I1 }, /* ori */
+{"li", "t,I", 0, (int) M_LI, INSN_MACRO, I1 },
+{"move", "d,s", 0, (int) M_MOVE, INSN_MACRO, I1 },
+{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, I3 },/* daddu */
+{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, I1 },/* addu */
+{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, I1 },/* or */
+{"b", "p", 0x10000000, 0xffff0000, UBD, I1 },/* beq 0,0 */
+{"b", "p", 0x04010000, 0xffff0000, UBD, I1 },/* bgez 0 */
+{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, I1 },/* bgezal 0*/
+
+{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, I1 },
+{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, I1 },
+{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"add.ob", "X,Y,Q", 0x7800000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"add.ob", "D,S,T", 0x4ac0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"add.ob", "D,S,T[e]", 0x4800000b, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"add.ob", "D,S,k", 0x4bc0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"add.qh", "X,Y,Q", 0x7820000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"adda.ob", "Y,Q", 0x78000037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"adda.qh", "Y,Q", 0x78200037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, I1 },
+{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, I1 },
+{"addl.ob", "Y,Q", 0x78000437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"addl.qh", "Y,Q", 0x78200437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"addr.ps", "D,S,T", 0x46c00018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, I1 },
+{"alni.ob", "X,Y,Z,O", 0x78000018, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"alni.ob", "D,S,T,%", 0x48000018, 0xff00003f, WR_D|RD_S|RD_T, N54 },
+{"alni.qh", "X,Y,Z,O", 0x7800001a, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"alnv.ob", "X,Y,Z,s", 0x78000019, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX|SB1 },
+{"alnv.qh", "X,Y,Z,s", 0x7800001b, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX },
+{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, I1 },
+{"and.ob", "X,Y,Q", 0x7800000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"and.ob", "D,S,T", 0x4ac0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"and.ob", "D,S,T[e]", 0x4800000c, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"and.ob", "D,S,k", 0x4bc0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"and.qh", "X,Y,Q", 0x7820000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, I1 },
+/* b is at the top of the table. */
+/* bal is at the top of the table. */
+{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc1any2f", "N,p", 0x45200000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any2t", "N,p", 0x45210000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any4f", "N,p", 0x45400000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any4t", "N,p", 0x45410000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, I1 },
+{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 },
+{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 },
+{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 },
+{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, I1 },
+{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 },
+{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 },
+{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 },
+/* bc2* are at the bottom of the table. */
+{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, I1 },
+{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, I1 },
+{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 },
+{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, I2|T3 },
+{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, I1 },
+{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, I1 },
+{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, I2|T3 },
+{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, I2|T3 },
+{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, I1 },
+{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, I1 },
+{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, I2|T3 },
+{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, I2|T3 },
+{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, I1 },
+{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 },
+{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, I1 },
+{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, I1 },
+{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, I2|T3 },
+{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, I2|T3 },
+{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, I1 },
+{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, I1 },
+{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, I2|T3 },
+{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, I2|T3 },
+{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, I1 },
+{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, I1 },
+{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, I2|T3 },
+{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, I2|T3 },
+{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, I1 },
+{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, I1 },
+{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, I2|T3 },
+{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, I2|T3 },
+{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, I1 },
+{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, I1 },
+{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, I2|T3 },
+{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, I2|T3 },
+{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, I1 },
+{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, I1 },
+{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, I2|T3 },
+{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, I2|T3 },
+{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, I1 },
+{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 },
+{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, I1 },
+{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, I1 },
+{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 },
+{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, I2|T3 },
+{"break", "", 0x0000000d, 0xffffffff, TRAP, I1 },
+{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, I1 },
+{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, I1 },
+{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.eq.ob", "Y,Q", 0x78000001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.eq.ob", "S,T", 0x4ac00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ob", "S,T[e]", 0x48000001, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ob", "S,k", 0x4bc00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.qh", "Y,Q", 0x78200001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.lt.ob", "Y,Q", 0x78000004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.lt.ob", "S,T", 0x4ac00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ob", "S,T[e]", 0x48000004, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ob", "S,k", 0x4bc00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.qh", "Y,Q", 0x78200004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.le.ob", "Y,Q", 0x78000005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.le.ob", "S,T", 0x4ac00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ob", "S,T[e]", 0x48000005, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ob", "S,k", 0x4bc00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.qh", "Y,Q", 0x78200005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"cabs.eq.d", "M,S,T", 0x46200072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.eq.ps", "M,S,T", 0x46c00072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.eq.s", "M,S,T", 0x46000072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.f.d", "M,S,T", 0x46200070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.f.ps", "M,S,T", 0x46c00070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.f.s", "M,S,T", 0x46000070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.le.d", "M,S,T", 0x4620007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.le.ps", "M,S,T", 0x46c0007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.le.s", "M,S,T", 0x4600007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.lt.d", "M,S,T", 0x4620007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.lt.ps", "M,S,T", 0x46c0007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.lt.s", "M,S,T", 0x4600007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.nge.d", "M,S,T", 0x4620007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.nge.ps","M,S,T", 0x46c0007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.nge.s", "M,S,T", 0x4600007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngl.d", "M,S,T", 0x4620007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngl.ps","M,S,T", 0x46c0007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngl.s", "M,S,T", 0x4600007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngle.d","M,S,T", 0x46200079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngle.ps","M,S,T",0x46c00079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngle.s","M,S,T", 0x46000079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngt.d", "M,S,T", 0x4620007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngt.ps","M,S,T", 0x46c0007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngt.s", "M,S,T", 0x4600007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ole.d", "M,S,T", 0x46200076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ole.ps","M,S,T", 0x46c00076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ole.s", "M,S,T", 0x46000076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.olt.d", "M,S,T", 0x46200074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.olt.ps","M,S,T", 0x46c00074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.olt.s", "M,S,T", 0x46000074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.seq.d", "M,S,T", 0x4620007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.seq.ps","M,S,T", 0x46c0007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.seq.s", "M,S,T", 0x4600007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.sf.d", "M,S,T", 0x46200078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.sf.ps", "M,S,T", 0x46c00078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.sf.s", "M,S,T", 0x46000078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ueq.d", "M,S,T", 0x46200073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ueq.ps","M,S,T", 0x46c00073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ueq.s", "M,S,T", 0x46000073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ule.d", "M,S,T", 0x46200077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ule.ps","M,S,T", 0x46c00077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ule.s", "M,S,T", 0x46000077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ult.d", "M,S,T", 0x46200075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ult.ps","M,S,T", 0x46c00075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ult.s", "M,S,T", 0x46000075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.un.d", "M,S,T", 0x46200071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.un.ps", "M,S,T", 0x46c00071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.un.s", "M,S,T", 0x46000071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, I3|I32|T3},
+{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, I1 },
+{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 },
+{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 },
+/* cfc2 is at the bottom of the table. */
+{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, I1 },
+{"clo", "U,s", 0x70000021, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 },
+{"clz", "U,s", 0x70000020, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 },
+{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 },
+{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 },
+/* ctc2 is at the bottom of the table. */
+{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_D|FP_S, I1 },
+{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I1 },
+{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 },
+{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 },
+{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"cvt.ps.pw", "D,S", 0x46800026, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D },
+{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"cvt.pw.ps", "D,S", 0x46c00024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D },
+{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, I3 },
+{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, I3 },
+{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, I3 },
+{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, I3 },
+{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, I3 },
+{"dbreak", "", 0x7000003f, 0xffffffff, 0, N5 },
+{"dclo", "U,s", 0x70000025, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 },
+{"dclz", "U,s", 0x70000024, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 },
+/* dctr and dctw are used on the r5000. */
+{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, I3 },
+{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, I3 },
+{"deret", "", 0x4200001f, 0xffffffff, 0, I32|G2 },
+{"dext", "t,r,I,+I", 0, (int) M_DEXT, INSN_MACRO, I65 },
+{"dext", "t,r,+A,+C", 0x7c000003, 0xfc00003f, WR_t|RD_s, I65 },
+{"dextm", "t,r,+A,+G", 0x7c000001, 0xfc00003f, WR_t|RD_s, I65 },
+{"dextu", "t,r,+E,+H", 0x7c000002, 0xfc00003f, WR_t|RD_s, I65 },
+/* For ddiv, see the comments about div. */
+{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, I3 },
+{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, I3 },
+/* For ddivu, see the comments about div. */
+{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, I3 },
+{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, I3 },
+{"di", "", 0x41606000, 0xffffffff, WR_t|WR_C0, I33 },
+{"di", "t", 0x41606000, 0xffe0ffff, WR_t|WR_C0, I33 },
+{"dins", "t,r,I,+I", 0, (int) M_DINS, INSN_MACRO, I65 },
+{"dins", "t,r,+A,+B", 0x7c000007, 0xfc00003f, WR_t|RD_s, I65 },
+{"dinsm", "t,r,+A,+F", 0x7c000005, 0xfc00003f, WR_t|RD_s, I65 },
+{"dinsu", "t,r,+E,+F", 0x7c000006, 0xfc00003f, WR_t|RD_s, I65 },
+/* The MIPS assembler treats the div opcode with two operands as
+ though the first operand appeared twice (the first operand is both
+ a source and a destination). To get the div machine instruction,
+ you must use an explicit destination of $0. */
+{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 },
+{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, I1 },
+{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, I1 },
+{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"div.ps", "D,V,T", 0x46c00003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+/* For divu, see the comments about div. */
+{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 },
+{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, I1 },
+{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, I1 },
+{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, I3 },
+{"dlca", "t,A(b)", 0, (int) M_DLCA_AB, INSN_MACRO, I3 },
+{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, I3 }, /* addiu */
+{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, I3 }, /* ori */
+{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, I3 },
+{"dmacc", "d,s,t", 0x00000029, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchi", "d,s,t", 0x00000229, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchis", "d,s,t", 0x00000629, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchiu", "d,s,t", 0x00000269, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchius", "d,s,t", 0x00000669, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccs", "d,s,t", 0x00000429, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccu", "d,s,t", 0x00000069, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccus", "d,s,t", 0x00000469, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|MOD_LO, N411 },
+{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, I3 },
+{"dmfc0", "t,+D", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 },
+{"dmfc0", "t,G,H", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 },
+{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I3 },
+{"dmtc0", "t,+D", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 },
+{"dmtc0", "t,G,H", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 },
+{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 },
+{"dmfc1", "t,G", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 },
+{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 },
+{"dmtc1", "t,G", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 },
+/* dmfc2 is at the bottom of the table. */
+/* dmtc2 is at the bottom of the table. */
+{"dmfc3", "t,G", 0x4c200000, 0xffe007ff, LCD|WR_t|RD_C3, I3 },
+{"dmfc3", "t,G,H", 0x4c200000, 0xffe007f8, LCD|WR_t|RD_C3, I64 },
+{"dmtc3", "t,G", 0x4ca00000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I3 },
+{"dmtc3", "t,G,H", 0x4ca00000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I64 },
+{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, I3 },
+{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, I3 },
+{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, I3 },
+{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, I3 },
+{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, I3 },
+{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, I3 },
+{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, I3 }, /* dsub 0 */
+{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, I3 }, /* dsubu 0*/
+{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, I3 },
+{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, I3 },
+{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, I3 },
+{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, I3 },
+{"dret", "", 0x7000003e, 0xffffffff, 0, N5 },
+{"drol", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I3 },
+{"drol", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I3 },
+{"dror", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I3 },
+{"dror", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I3 },
+{"dror", "d,w,<", 0x0020003a, 0xffe0003f, WR_d|RD_t, N5|I65 },
+{"drorv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I65 },
+{"dror32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, N5|I65 },
+{"drotl", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I65 },
+{"drotl", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I65 },
+{"drotr", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I65 },
+{"drotr", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I65 },
+{"drotrv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, I65 },
+{"drotr32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, I65 },
+{"dsbh", "d,w", 0x7c0000a4, 0xffe007ff, WR_d|RD_t, I65 },
+{"dshd", "d,w", 0x7c000164, 0xffe007ff, WR_d|RD_t, I65 },
+{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsllv */
+{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, /* dsll32 */
+{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrav */
+{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, /* dsra32 */
+{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrlv */
+{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, /* dsrl32 */
+{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, I3 },
+{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, I3 },
+{"ei", "", 0x41606020, 0xffffffff, WR_t|WR_C0, I33 },
+{"ei", "t", 0x41606020, 0xffe0ffff, WR_t|WR_C0, I33 },
+{"eret", "", 0x42000018, 0xffffffff, 0, I3|I32 },
+{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, I33 },
+{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"flushi", "", 0xbc010000, 0xffffffff, 0, L1 },
+{"flushd", "", 0xbc020000, 0xffffffff, 0, L1 },
+{"flushid", "", 0xbc030000, 0xffffffff, 0, L1 },
+{"hibernate","", 0x42000023, 0xffffffff, 0, V1 },
+{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, I33 },
+{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 },
+{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, I33 },
+{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, /* jr */
+/* SVR4 PIC code requires special handling for j, so it must be a
+ macro. */
+{"j", "a", 0, (int) M_J_A, INSN_MACRO, I1 },
+/* This form of j is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"j", "a", 0x08000000, 0xfc000000, UBD, I1 },
+{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, I1 },
+{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, I1 },
+{"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, I33 },
+{"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, I33 },
+/* SVR4 PIC code requires special handling for jal, so it must be a
+ macro. */
+{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, I1 },
+{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, I1 },
+{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, I1 },
+/* This form of jal is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, I1 },
+{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, I16 },
+{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, I1 },
+{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, I1 },
+{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, I1 },
+{"lca", "t,A(b)", 0, (int) M_LCA_AB, INSN_MACRO, I1 },
+{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, I3 },
+{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, I1 },
+{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, I1 },
+{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 },
+{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 },
+{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 },
+{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 },
+{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, /* ldc1 */
+{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, I1 },
+{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, I1 },
+{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, I2 },
+{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, I2 },
+{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, I2 },
+{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, I2 },
+{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, I3 },
+{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, I3 },
+{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, I3 },
+{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, I3 },
+{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 },
+{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, I1 },
+{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, I1 },
+/* li is at the start of the table. */
+{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, I1 },
+{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, I1 },
+{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, I1 },
+{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, I1 },
+{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, I2 },
+{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, I2 },
+{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, I3 },
+{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, I3 },
+{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, I1 },
+{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I5|N55 },
+{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, I1 },
+{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, I1 },
+{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 },
+{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 },
+{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, /* lwc1 */
+{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, I1 },
+{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, I1 },
+{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I1 },
+{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */
+{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I2 }, /* as lwl */
+{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I1 },
+{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */
+{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I2 }, /* as lwr */
+{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, I3 },
+{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, I3 },
+{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 },
+{"macc", "d,s,t", 0x00000028, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macc", "d,s,t", 0x00000158, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"maccs", "d,s,t", 0x00000428, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchi", "d,s,t", 0x00000228, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchi", "d,s,t", 0x00000358, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"macchis", "d,s,t", 0x00000628, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchiu", "d,s,t", 0x00000268, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchiu", "d,s,t", 0x00000359, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"macchius","d,s,t", 0x00000668, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"maccu", "d,s,t", 0x00000068, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"maccu", "d,s,t", 0x00000159, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"maccus", "d,s,t", 0x00000468, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 },
+{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 },
+{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55},
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 },
+{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55},
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 },
+{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|MOD_HILO, N411 },
+{"max.ob", "X,Y,Q", 0x78000007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"max.ob", "D,S,T", 0x4ac00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"max.ob", "D,S,T[e]", 0x48000007, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"max.ob", "D,S,k", 0x4bc00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"max.qh", "X,Y,Q", 0x78200007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mfpc", "t,P", 0x4000c801, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 },
+{"mfps", "t,P", 0x4000c800, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 },
+{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, I1 },
+{"mfc0", "t,+D", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 },
+{"mfc0", "t,G,H", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 },
+{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 },
+{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 },
+{"mfhc1", "t,S", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 },
+{"mfhc1", "t,G", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 },
+/* mfc2 is at the bottom of the table. */
+/* mfhc2 is at the bottom of the table. */
+{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, I1 },
+{"mfc3", "t,G,H", 0x4c000000, 0xffe007f8, LCD|WR_t|RD_C3, I32 },
+{"mfdr", "t,G", 0x7000003d, 0xffe007ff, LCD|WR_t|RD_C0, N5 },
+{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, I1 },
+{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, I1 },
+{"min.ob", "X,Y,Q", 0x78000006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"min.ob", "D,S,T", 0x4ac00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"min.ob", "D,S,T[e]", 0x48000006, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"min.ob", "D,S,k", 0x4bc00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"min.qh", "X,Y,Q", 0x78200006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_D|FP_S, I4|I32},
+{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 },
+{"movf.l", "D,S,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movf.l", "X,Y,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 },
+{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 },
+{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 },
+{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s, L1 },
+{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 },
+{"movn.l", "D,S,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movn.l", "X,Y,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 },
+{"movn.ps", "D,S,t", 0x46c00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 },
+{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC, I4|I32 },
+{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 },
+{"movt.l", "D,S,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movt.l", "X,Y,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 },
+{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 },
+{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 },
+{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s, L1 },
+{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 },
+{"movz.l", "D,S,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movz.l", "X,Y,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 },
+{"movz.ps", "D,S,t", 0x46c00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 },
+{"msac", "d,s,t", 0x000001d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msacu", "d,s,t", 0x000001d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msachi", "d,s,t", 0x000003d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msachiu", "d,s,t", 0x000003d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+/* move is at the top of the table. */
+{"msgn.qh", "X,Y,Q", 0x78200000, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"msub", "s,t", 0x70000004, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 },
+{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"msubu", "s,t", 0x70000005, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 },
+{"mtpc", "t,P", 0x4080c801, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 },
+{"mtps", "t,P", 0x4080c800, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 },
+{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I1 },
+{"mtc0", "t,+D", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 },
+{"mtc0", "t,G,H", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 },
+{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 },
+{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 },
+{"mthc1", "t,S", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 },
+{"mthc1", "t,G", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 },
+/* mtc2 is at the bottom of the table. */
+/* mthc2 is at the bottom of the table. */
+{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I1 },
+{"mtc3", "t,G,H", 0x4c800000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I32 },
+{"mtdr", "t,G", 0x7080003d, 0xffe007ff, COD|RD_t|WR_C0, N5 },
+{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, I1 },
+{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, I1 },
+{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"mul.ob", "X,Y,Q", 0x78000030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"mul.ob", "D,S,T", 0x4ac00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ob", "D,S,T[e]", 0x48000030, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ob", "D,S,k", 0x4bc00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"mul.qh", "X,Y,Q", 0x78200030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, I32|P3|N55},
+{"mul", "d,s,t", 0x00000058, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N54 },
+{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, I1 },
+{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, I1 },
+{"mula.ob", "Y,Q", 0x78000033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mula.ob", "S,T", 0x4ac00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.ob", "S,T[e]", 0x48000033, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.ob", "S,k", 0x4bc00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.qh", "Y,Q", 0x78200033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulhi", "d,s,t", 0x00000258, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulhiu", "d,s,t", 0x00000259, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mull.ob", "Y,Q", 0x78000433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mull.ob", "S,T", 0x4ac00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.ob", "S,T[e]", 0x48000433, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.ob", "S,k", 0x4bc00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.qh", "Y,Q", 0x78200433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, I1 },
+{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, I1 },
+{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, I1 },
+{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, I1 },
+{"mulr.ps", "D,S,T", 0x46c0001a, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"muls", "d,s,t", 0x000000d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulsu", "d,s,t", 0x000000d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulshi", "d,s,t", 0x000002d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulshiu", "d,s,t", 0x000002d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"muls.ob", "Y,Q", 0x78000032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"muls.ob", "S,T", 0x4ac00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.ob", "S,T[e]", 0x48000032, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.ob", "S,k", 0x4bc00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.qh", "Y,Q", 0x78200032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulsl.ob", "Y,Q", 0x78000432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mulsl.ob", "S,T", 0x4ac00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.ob", "S,T[e]", 0x48000432, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.ob", "S,k", 0x4bc00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.qh", "Y,Q", 0x78200432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 },
+{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 },
+{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"mulu", "d,s,t", 0x00000059, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, I1 }, /* sub 0 */
+{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, I1 }, /* subu 0 */
+{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+/* nop is at the start of the table. */
+{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, I1 },
+{"nor.ob", "X,Y,Q", 0x7800000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"nor.ob", "D,S,T", 0x4ac0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"nor.ob", "D,S,T[e]", 0x4800000f, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"nor.ob", "D,S,k", 0x4bc0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"nor.qh", "X,Y,Q", 0x7820000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, I1 },/*nor d,s,0*/
+{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, I1 },
+{"or.ob", "X,Y,Q", 0x7800000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"or.ob", "D,S,T", 0x4ac0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"or.ob", "D,S,T[e]", 0x4800000e, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"or.ob", "D,S,k", 0x4bc0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"or.qh", "X,Y,Q", 0x7820000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, I1 },
+{"pabsdiff.ob", "X,Y,Q",0x78000009, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+{"pabsdiffc.ob", "Y,Q", 0x78000035, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, SB1 },
+{"pavg.ob", "X,Y,Q", 0x78000008, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+{"pickf.ob", "X,Y,Q", 0x78000002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"pickf.ob", "D,S,T", 0x4ac00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.ob", "D,S,T[e]",0x48000002, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.ob", "D,S,k", 0x4bc00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.qh", "X,Y,Q", 0x78200002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"pickt.ob", "X,Y,Q", 0x78000003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"pickt.ob", "D,S,T", 0x4ac00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.ob", "D,S,T[e]",0x48000003, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.ob", "D,S,k", 0x4bc00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.qh", "X,Y,Q", 0x78200003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+ /* pref and prefx are at the start of the table. */
+{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"rach.ob", "X", 0x7a00003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"rach.ob", "D", 0x4a00003f, 0xfffff83f, WR_D, N54 },
+{"rach.qh", "X", 0x7a20003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"racl.ob", "X", 0x7800003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"racl.ob", "D", 0x4800003f, 0xfffff83f, WR_D, N54 },
+{"racl.qh", "X", 0x7820003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"racm.ob", "X", 0x7900003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"racm.ob", "D", 0x4900003f, 0xfffff83f, WR_D, N54 },
+{"racm.qh", "X", 0x7920003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, I4 },
+{"recip.ps","D,S", 0x46c00015, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, I4 },
+{"recip1.d", "D,S", 0x4620001d, 0xffff003f, WR_D|RD_S|FP_D, M3D },
+{"recip1.ps", "D,S", 0x46c0001d, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"recip1.s", "D,S", 0x4600001d, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"recip2.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"recip2.ps", "D,S,T", 0x46c0001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"recip2.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, I1 },
+{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, I1 },
+{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, I1 },
+{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, I1 },
+{"rdhwr", "t,K", 0x7c00003b, 0xffe007ff, WR_t, I33 },
+{"rdpgpr", "d,w", 0x41400000, 0xffe007ff, WR_d, I33 },
+{"rfe", "", 0x42000010, 0xffffffff, 0, I1|T3 },
+{"rnas.qh", "X,Q", 0x78200025, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rnau.ob", "X,Q", 0x78000021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rnau.qh", "X,Q", 0x78200021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rnes.qh", "X,Q", 0x78200026, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rneu.ob", "X,Q", 0x78000022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rneu.qh", "X,Q", 0x78200022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I1 },
+{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I1 },
+{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I1 },
+{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I1 },
+{"ror", "d,w,<", 0x00200002, 0xffe0003f, WR_d|RD_t, N5|I33 },
+{"rorv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I33 },
+{"rotl", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I33 },
+{"rotl", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I33 },
+{"rotr", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I33 },
+{"rotr", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I33 },
+{"rotrv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, I33 },
+{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, I4 },
+{"rsqrt.ps","D,S", 0x46c00016, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, I4 },
+{"rsqrt1.d", "D,S", 0x4620001e, 0xffff003f, WR_D|RD_S|FP_D, M3D },
+{"rsqrt1.ps", "D,S", 0x46c0001e, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"rsqrt1.s", "D,S", 0x4600001e, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"rsqrt2.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"rsqrt2.ps", "D,S,T", 0x46c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rsqrt2.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rzs.qh", "X,Q", 0x78200024, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rzu.ob", "X,Q", 0x78000020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rzu.ob", "D,k", 0x4bc00020, 0xffe0f83f, WR_D|RD_S|RD_T, N54 },
+{"rzu.qh", "X,Q", 0x78200020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, I1 },
+{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I2 },
+{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, I2 },
+{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I3 },
+{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, I3 },
+{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, I1 },
+{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, I1 },
+{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, G2 },
+{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, G2 },
+{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, G2 },
+{"sdbbp", "", 0x7000003f, 0xffffffff, TRAP, I32 },
+{"sdbbp", "B", 0x7000003f, 0xfc00003f, TRAP, I32 },
+{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 },
+{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 },
+{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, I2 },
+{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, I2 },
+{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, I2 },
+{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, I2 },
+{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, I1 },
+{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, I1 },
+{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, I3 },
+{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, I3 },
+{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 },
+{"seb", "d,w", 0x7c000420, 0xffe007ff, WR_d|RD_t, I33 },
+{"seh", "d,w", 0x7c000620, 0xffe007ff, WR_d|RD_t, I33 },
+{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t, L1 },
+{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t, L1 },
+{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, I1 },
+{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, I1 },
+{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, I1 },
+{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, I1 },
+{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, I1 },
+{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, I1 },
+{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, I1 },
+{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, I1 },
+{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, I1 },
+{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, I1 },
+{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, I1 },
+{"shfl.bfla.qh", "X,Y,Z", 0x7a20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.mixh.ob", "X,Y,Z", 0x7980001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.mixh.ob", "D,S,T", 0x4980001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.mixh.qh", "X,Y,Z", 0x7820001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.mixl.ob", "X,Y,Z", 0x79c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.mixl.ob", "D,S,T", 0x49c0001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.mixl.qh", "X,Y,Z", 0x78a0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.pach.ob", "X,Y,Z", 0x7900001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.pach.ob", "D,S,T", 0x4900001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.pach.qh", "X,Y,Z", 0x7920001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.pacl.ob", "D,S,T", 0x4940001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.repa.qh", "X,Y,Z", 0x7b20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.repb.qh", "X,Y,Z", 0x7ba0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.upsl.ob", "X,Y,Z", 0x78c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, I1 },
+{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, I1 },
+{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, I1 },
+{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, I1 },
+{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* sllv */
+{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, I1 },
+{"sll.ob", "X,Y,Q", 0x78000010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sll.ob", "D,S,T[e]", 0x48000010, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"sll.ob", "D,S,k", 0x4bc00010, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sll.qh", "X,Y,Q", 0x78200010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, I1 },
+{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, I1 },
+{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, I1 },
+{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, I1 },
+{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, I1 },
+{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, I1 },
+{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"sqrt.ps", "D,S", 0x46c00004, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srav */
+{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, I1 },
+{"sra.qh", "X,Y,Q", 0x78200013, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srlv */
+{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, I1 },
+{"srl.ob", "X,Y,Q", 0x78000012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"srl.ob", "D,S,T[e]", 0x48000012, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"srl.ob", "D,S,k", 0x4bc00012, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"srl.qh", "X,Y,Q", 0x78200012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+/* ssnop is at the start of the table. */
+{"standby", "", 0x42000021, 0xffffffff, 0, V1 },
+{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, I1 },
+{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"sub.ob", "X,Y,Q", 0x7800000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sub.ob", "D,S,T", 0x4ac0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ob", "D,S,T[e]", 0x4800000a, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ob", "D,S,k", 0x4bc0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"sub.qh", "X,Y,Q", 0x7820000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"suba.ob", "Y,Q", 0x78000036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"suba.qh", "Y,Q", 0x78200036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"subl.ob", "Y,Q", 0x78000436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"subl.qh", "Y,Q", 0x78200436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, I1 },
+{"suspend", "", 0x42000022, 0xffffffff, 0, V1 },
+{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I5|N55 },
+{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, I1 },
+{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, I1 },
+{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, I1 },
+{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 },
+{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 },
+{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, /* swc1 */
+{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, I1 },
+{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, I1 },
+{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, I1 },
+{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, I1 },
+{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I1 },
+{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */
+{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I2 }, /* as swl */
+{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, I1 },
+{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */
+{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, I2 }, /* as swr */
+{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 },
+{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2|G1 },
+{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, I2 },
+{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2 },
+{"synci", "o(b)", 0x041f0000, 0xfc1f0000, SM|RD_b, I33 },
+{"syscall", "", 0x0000000c, 0xffffffff, TRAP, I1 },
+{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, I1 },
+{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* teqi */
+{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, I2 },
+{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgei */
+{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, I2 },
+{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgeiu */
+{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, I2 },
+{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, I1 },
+{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, I1 },
+{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, I1 },
+{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, I1 },
+{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tlti */
+{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, I2 },
+{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tltiu */
+{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, I2 },
+{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tnei */
+{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, I2 },
+{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, I1 },
+{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, I1 },
+{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, I3 },
+{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, I3 },
+{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, I1 },
+{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, I1 },
+{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, I1 },
+{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, I1 },
+{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, I1 },
+{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, I1 },
+{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, I3 },
+{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, I3 },
+{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, I1 },
+{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, I1 },
+{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, I1 },
+{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, I1 },
+{"wach.ob", "Y", 0x7a00003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX|SB1 },
+{"wach.ob", "S", 0x4a00003e, 0xffff07ff, RD_S, N54 },
+{"wach.qh", "Y", 0x7a20003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX },
+{"wacl.ob", "Y,Z", 0x7800003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"wacl.ob", "S,T", 0x4800003e, 0xffe007ff, RD_S|RD_T, N54 },
+{"wacl.qh", "Y,Z", 0x7820003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"wait", "", 0x42000020, 0xffffffff, TRAP, I3|I32 },
+{"wait", "J", 0x42000020, 0xfe00003f, TRAP, I32|N55 },
+{"waiti", "", 0x42000020, 0xffffffff, TRAP, L1 },
+{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, L1 },
+{"wrpgpr", "d,w", 0x41c00000, 0xffe007ff, RD_t, I33 },
+{"wsbh", "d,w", 0x7c0000a0, 0xffe007ff, WR_d|RD_t, I33 },
+{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, I1 },
+{"xor.ob", "X,Y,Q", 0x7800000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"xor.ob", "D,S,T", 0x4ac0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"xor.ob", "D,S,T[e]", 0x4800000d, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"xor.ob", "D,S,k", 0x4bc0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"xor.qh", "X,Y,Q", 0x7820000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, I1 },
+
+/* Coprocessor 2 move/branch operations overlap with VR5400 .ob format
+ instructions so they are here for the latters to take precedence. */
+{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, I1 },
+{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"dmfc2", "t,G", 0x48200000, 0xffe007ff, LCD|WR_t|RD_C2, I3 },
+{"dmfc2", "t,G,H", 0x48200000, 0xffe007f8, LCD|WR_t|RD_C2, I64 },
+{"dmtc2", "t,G", 0x48a00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I3 },
+{"dmtc2", "t,G,H", 0x48a00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I64 },
+{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, I1 },
+{"mfc2", "t,G,H", 0x48000000, 0xffe007f8, LCD|WR_t|RD_C2, I32 },
+{"mfhc2", "t,i", 0x48600000, 0xffe00000, LCD|WR_t|RD_C2, I33 },
+{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I1 },
+{"mtc2", "t,G,H", 0x48800000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I32 },
+{"mthc2", "t,i", 0x48e00000, 0xffe00000, COD|RD_t|WR_C2|WR_CC, I33 },
+
+/* No hazard protection on coprocessor instructions--they shouldn't
+ change the state of the processor and if they do it's up to the
+ user to put in nops as necessary. These are at the end so that the
+ disassembler recognizes more specific versions first. */
+{"c0", "C", 0x42000000, 0xfe000000, 0, I1 },
+{"c1", "C", 0x46000000, 0xfe000000, 0, I1 },
+{"c2", "C", 0x4a000000, 0xfe000000, 0, I1 },
+{"c3", "C", 0x4e000000, 0xfe000000, 0, I1 },
+{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, I1 },
+{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, I1 },
+{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, I1 },
+{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, I1 },
+
+ /* Conflicts with the 4650's "mul" instruction. Nobody's using the
+ 4010 any more, so move this insn out of the way. If the object
+ format gave us more info, we could do this right. */
+{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s, L1 },
+};
+
+#define MIPS_NUM_OPCODES \
+ ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0])))
+const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct mips_opcode *mips_opcodes =
+ (struct mips_opcode *) mips_builtin_opcodes;
+int bfd_mips_num_opcodes = MIPS_NUM_OPCODES;
+#undef MIPS_NUM_OPCODES
+
+/* Mips instructions are at maximum this many bytes long. */
+#define INSNLEN 4
+
+static void set_default_mips_dis_options
+ PARAMS ((struct disassemble_info *));
+static void parse_mips_dis_option
+ PARAMS ((const char *, unsigned int));
+static void parse_mips_dis_options
+ PARAMS ((const char *));
+static int _print_insn_mips
+ PARAMS ((bfd_vma, struct disassemble_info *, enum bfd_endian));
+static int print_insn_mips
+ PARAMS ((bfd_vma, unsigned long int, struct disassemble_info *));
+static void print_insn_args
+ PARAMS ((const char *, unsigned long, bfd_vma, struct disassemble_info *));
+#if 0
+static int print_insn_mips16
+ PARAMS ((bfd_vma, struct disassemble_info *));
+#endif
+#if 0
+static int is_newabi
+ PARAMS ((Elf32_Ehdr *));
+#endif
+#if 0
+static void print_mips16_insn_arg
+ PARAMS ((int, const struct mips_opcode *, int, bfd_boolean, int, bfd_vma,
+ struct disassemble_info *));
+#endif
+
+/* FIXME: These should be shared with gdb somehow. */
+
+struct mips_cp0sel_name {
+ unsigned int cp0reg;
+ unsigned int sel;
+ const char * const name;
+};
+
+/* The mips16 register names. */
+static const char * const mips16_reg_names[] = {
+ "s0", "s1", "v0", "v1", "a0", "a1", "a2", "a3"
+};
+
+static const char * const mips_gpr_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_gpr_names_oldabi[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_gpr_names_newabi[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_fpr_names_numeric[32] = {
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
+};
+
+static const char * const mips_fpr_names_32[32] = {
+ "fv0", "fv0f", "fv1", "fv1f", "ft0", "ft0f", "ft1", "ft1f",
+ "ft2", "ft2f", "ft3", "ft3f", "fa0", "fa0f", "fa1", "fa1f",
+ "ft4", "ft4f", "ft5", "ft5f", "fs0", "fs0f", "fs1", "fs1f",
+ "fs2", "fs2f", "fs3", "fs3f", "fs4", "fs4f", "fs5", "fs5f"
+};
+
+static const char * const mips_fpr_names_n32[32] = {
+ "fv0", "ft14", "fv1", "ft15", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "fs0", "ft8", "fs1", "ft9",
+ "fs2", "ft10", "fs3", "ft11", "fs4", "ft12", "fs5", "ft13"
+};
+
+static const char * const mips_fpr_names_64[32] = {
+ "fv0", "ft12", "fv1", "ft13", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "ft8", "ft9", "ft10", "ft11",
+ "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7"
+};
+
+static const char * const mips_cp0_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_cp0_names_mips3264[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] = {
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 29, 1, "c0_datahi" }
+};
+
+static const char * const mips_cp0_names_mips3264r2[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "c0_hwrena",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264r2[] = {
+ { 4, 1, "c0_contextconfig" },
+ { 5, 1, "c0_pagegrain" },
+ { 12, 1, "c0_intctl" },
+ { 12, 2, "c0_srsctl" },
+ { 12, 3, "c0_srsmap" },
+ { 15, 1, "c0_ebase" },
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 23, 1, "c0_tracecontrol" },
+ { 23, 2, "c0_tracecontrol2" },
+ { 23, 3, "c0_usertracedata" },
+ { 23, 4, "c0_tracebpc" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 28, 2, "c0_taglo1" },
+ { 28, 3, "c0_datalo1" },
+ { 28, 4, "c0_taglo2" },
+ { 28, 5, "c0_datalo2" },
+ { 28, 6, "c0_taglo3" },
+ { 28, 7, "c0_datalo3" },
+ { 29, 1, "c0_datahi" },
+ { 29, 2, "c0_taghi1" },
+ { 29, 3, "c0_datahi1" },
+ { 29, 4, "c0_taghi2" },
+ { 29, 5, "c0_datahi2" },
+ { 29, 6, "c0_taghi3" },
+ { 29, 7, "c0_datahi3" },
+};
+
+/* SB-1: MIPS64 (mips_cp0_names_mips3264) with minor mods. */
+static const char * const mips_cp0_names_sb1[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr_i",
+ "c0_taglo_i", "c0_taghi_i", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_sb1[] = {
+ { 16, 1, "c0_config1" },
+ { 18, 1, "c0_watchlo,1" },
+ { 19, 1, "c0_watchhi,1" },
+ { 22, 0, "c0_perftrace" },
+ { 23, 3, "c0_edebug" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 26, 1, "c0_buserr_pa" },
+ { 27, 1, "c0_cacheerr_d" },
+ { 27, 3, "c0_cacheerr_d_pa" },
+ { 28, 1, "c0_datalo_i" },
+ { 28, 2, "c0_taglo_d" },
+ { 28, 3, "c0_datalo_d" },
+ { 29, 1, "c0_datahi_i" },
+ { 29, 2, "c0_taghi_d" },
+ { 29, 3, "c0_datahi_d" },
+};
+
+static const char * const mips_hwr_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_hwr_names_mips3264r2[32] = {
+ "hwr_cpunum", "hwr_synci_step", "hwr_cc", "hwr_ccres",
+ "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+struct mips_abi_choice {
+ const char *name;
+ const char * const *gpr_names;
+ const char * const *fpr_names;
+};
+
+struct mips_abi_choice mips_abi_choices[] = {
+ { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric },
+ { "32", mips_gpr_names_oldabi, mips_fpr_names_32 },
+ { "n32", mips_gpr_names_newabi, mips_fpr_names_n32 },
+ { "64", mips_gpr_names_newabi, mips_fpr_names_64 },
+};
+
+struct mips_arch_choice {
+ const char *name;
+ int bfd_mach_valid;
+ unsigned long bfd_mach;
+ int processor;
+ int isa;
+ const char * const *cp0_names;
+ const struct mips_cp0sel_name *cp0sel_names;
+ unsigned int cp0sel_names_len;
+ const char * const *hwr_names;
+};
+
+#define bfd_mach_mips3000 3000
+#define bfd_mach_mips3900 3900
+#define bfd_mach_mips4000 4000
+#define bfd_mach_mips4010 4010
+#define bfd_mach_mips4100 4100
+#define bfd_mach_mips4111 4111
+#define bfd_mach_mips4120 4120
+#define bfd_mach_mips4300 4300
+#define bfd_mach_mips4400 4400
+#define bfd_mach_mips4600 4600
+#define bfd_mach_mips4650 4650
+#define bfd_mach_mips5000 5000
+#define bfd_mach_mips5400 5400
+#define bfd_mach_mips5500 5500
+#define bfd_mach_mips6000 6000
+#define bfd_mach_mips7000 7000
+#define bfd_mach_mips8000 8000
+#define bfd_mach_mips10000 10000
+#define bfd_mach_mips12000 12000
+#define bfd_mach_mips16 16
+#define bfd_mach_mips5 5
+#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */
+#define bfd_mach_mipsisa32 32
+#define bfd_mach_mipsisa32r2 33
+#define bfd_mach_mipsisa64 64
+#define bfd_mach_mipsisa64r2 65
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+const struct mips_arch_choice mips_arch_choices[] = {
+ { "numeric", 0, 0, 0, 0,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs.
+ Note that MIPS-3D and MDMX are not applicable to MIPS32. (See
+ _MIPS32 Architecture For Programmers Volume I: Introduction to the
+ MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95),
+ page 1. */
+ { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32,
+ ISA_MIPS32 | INSN_MIPS16,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2,
+ ISA_MIPS32R2 | INSN_MIPS16,
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */
+ { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64,
+ ISA_MIPS64 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2,
+ ISA_MIPS64R2 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX,
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1,
+ ISA_MIPS64 | INSN_MIPS3D | INSN_SB1,
+ mips_cp0_names_sb1,
+ mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1),
+ mips_hwr_names_numeric },
+
+ /* This entry, mips16, is here only for ISA/processor selection; do
+ not print its name. */
+ { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3 | INSN_MIPS16,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+};
+
+/* ISA and processor type to disassemble for, and register names to use.
+ set_default_mips_dis_options and parse_mips_dis_options fill in these
+ values. */
+static int mips_processor;
+static int mips_isa;
+static const char * const *mips_gpr_names;
+static const char * const *mips_fpr_names;
+static const char * const *mips_cp0_names;
+static const struct mips_cp0sel_name *mips_cp0sel_names;
+static int mips_cp0sel_names_len;
+static const char * const *mips_hwr_names;
+
+static const struct mips_abi_choice *choose_abi_by_name
+ PARAMS ((const char *, unsigned int));
+static const struct mips_arch_choice *choose_arch_by_name
+ PARAMS ((const char *, unsigned int));
+static const struct mips_arch_choice *choose_arch_by_number
+ PARAMS ((unsigned long));
+static const struct mips_cp0sel_name *lookup_mips_cp0sel_name
+ PARAMS ((const struct mips_cp0sel_name *, unsigned int, unsigned int,
+ unsigned int));
+
+static const struct mips_abi_choice *
+choose_abi_by_name (name, namelen)
+ const char *name;
+ unsigned int namelen;
+{
+ const struct mips_abi_choice *c;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++)
+ {
+ if (strncmp (mips_abi_choices[i].name, name, namelen) == 0
+ && strlen (mips_abi_choices[i].name) == namelen)
+ c = &mips_abi_choices[i];
+ }
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_name (name, namelen)
+ const char *name;
+ unsigned int namelen;
+{
+ const struct mips_arch_choice *c = NULL;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (strncmp (mips_arch_choices[i].name, name, namelen) == 0
+ && strlen (mips_arch_choices[i].name) == namelen)
+ c = &mips_arch_choices[i];
+ }
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_number (mach)
+ unsigned long mach;
+{
+ static unsigned long hint_bfd_mach;
+ static const struct mips_arch_choice *hint_arch_choice;
+ const struct mips_arch_choice *c;
+ unsigned int i;
+
+ /* We optimize this because even if the user specifies no
+ flags, this will be done for every instruction! */
+ if (hint_bfd_mach == mach
+ && hint_arch_choice != NULL
+ && hint_arch_choice->bfd_mach == hint_bfd_mach)
+ return hint_arch_choice;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (mips_arch_choices[i].bfd_mach_valid
+ && mips_arch_choices[i].bfd_mach == mach)
+ {
+ c = &mips_arch_choices[i];
+ hint_bfd_mach = mach;
+ hint_arch_choice = c;
+ }
+ }
+ return c;
+}
+
+void
+set_default_mips_dis_options (info)
+ struct disassemble_info *info;
+{
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names,
+ and numeric FPR, CP0 register, and HWR names. */
+ mips_isa = ISA_MIPS3;
+ mips_processor = CPU_R3000;
+ mips_gpr_names = mips_gpr_names_oldabi;
+ mips_fpr_names = mips_fpr_names_numeric;
+ mips_cp0_names = mips_cp0_names_numeric;
+ mips_cp0sel_names = NULL;
+ mips_cp0sel_names_len = 0;
+ mips_hwr_names = mips_hwr_names_numeric;
+
+ /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */
+#if 0
+ if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
+ {
+ Elf_Internal_Ehdr *header;
+
+ header = elf_elfheader (info->section->owner);
+ if (is_newabi (header))
+ mips_gpr_names = mips_gpr_names_newabi;
+ }
+#endif
+
+ /* Set ISA, architecture, and cp0 register names as best we can. */
+#if ! SYMTAB_AVAILABLE && 0
+ /* This is running out on a target machine, not in a host tool.
+ FIXME: Where does mips_target_info come from? */
+ target_processor = mips_target_info.processor;
+ mips_isa = mips_target_info.isa;
+#else
+ chosen_arch = choose_arch_by_number (info->mach);
+ if (chosen_arch != NULL)
+ {
+ mips_processor = chosen_arch->processor;
+ mips_isa = chosen_arch->isa;
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+#endif
+}
+
+void
+parse_mips_dis_option (option, len)
+ const char *option;
+ unsigned int len;
+{
+ unsigned int i, optionlen, vallen;
+ const char *val;
+ const struct mips_abi_choice *chosen_abi;
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Look for the = that delimits the end of the option name. */
+ for (i = 0; i < len; i++)
+ {
+ if (option[i] == '=')
+ break;
+ }
+ if (i == 0) /* Invalid option: no name before '='. */
+ return;
+ if (i == len) /* Invalid option: no '='. */
+ return;
+ if (i == (len - 1)) /* Invalid option: no value after '='. */
+ return;
+
+ optionlen = i;
+ val = option + (optionlen + 1);
+ vallen = len - (optionlen + 1);
+
+ if (strncmp("gpr-names", option, optionlen) == 0
+ && strlen("gpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_gpr_names = chosen_abi->gpr_names;
+ return;
+ }
+
+ if (strncmp("fpr-names", option, optionlen) == 0
+ && strlen("fpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_fpr_names = chosen_abi->fpr_names;
+ return;
+ }
+
+ if (strncmp("cp0-names", option, optionlen) == 0
+ && strlen("cp0-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ }
+ return;
+ }
+
+ if (strncmp("hwr-names", option, optionlen) == 0
+ && strlen("hwr-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ mips_hwr_names = chosen_arch->hwr_names;
+ return;
+ }
+
+ if (strncmp("reg-names", option, optionlen) == 0
+ && strlen("reg-names") == optionlen)
+ {
+ /* We check both ABI and ARCH here unconditionally, so
+ that "numeric" will do the desirable thing: select
+ numeric register names for all registers. Other than
+ that, a given name probably won't match both. */
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ {
+ mips_gpr_names = chosen_abi->gpr_names;
+ mips_fpr_names = chosen_abi->fpr_names;
+ }
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+ return;
+ }
+
+ /* Invalid option. */
+}
+
+void
+parse_mips_dis_options (options)
+ const char *options;
+{
+ const char *option_end;
+
+ if (options == NULL)
+ return;
+
+ while (*options != '\0')
+ {
+ /* Skip empty options. */
+ if (*options == ',')
+ {
+ options++;
+ continue;
+ }
+
+ /* We know that *options is neither NUL or a comma. */
+ option_end = options + 1;
+ while (*option_end != ',' && *option_end != '\0')
+ option_end++;
+
+ parse_mips_dis_option (options, option_end - options);
+
+ /* Go on to the next one. If option_end points to a comma, it
+ will be skipped above. */
+ options = option_end;
+ }
+}
+
+static const struct mips_cp0sel_name *
+lookup_mips_cp0sel_name(names, len, cp0reg, sel)
+ const struct mips_cp0sel_name *names;
+ unsigned int len, cp0reg, sel;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ if (names[i].cp0reg == cp0reg && names[i].sel == sel)
+ return &names[i];
+ return NULL;
+}
+
+/* Print insn arguments for 32/64-bit code. */
+
+static void
+print_insn_args (d, l, pc, info)
+ const char *d;
+ register unsigned long int l;
+ bfd_vma pc;
+ struct disassemble_info *info;
+{
+ int op, delta;
+ unsigned int lsb, msb, msbd;
+
+ lsb = 0;
+
+ for (; *d != '\0'; d++)
+ {
+ switch (*d)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ (*info->fprintf_func) (info->stream, "%c", *d);
+ break;
+
+ case '+':
+ /* Extension character; switch for second char. */
+ d++;
+ switch (*d)
+ {
+ case '\0':
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, incomplete extension sequence (+)"));
+ return;
+
+ case 'A':
+ lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'B':
+ msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case 'C':
+ case 'H':
+ msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ case 'D':
+ {
+ const struct mips_cp0sel_name *n;
+ unsigned int cp0reg, sel;
+
+ cp0reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
+
+ /* CP0 register including 'sel' code for mtcN (et al.), to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name(mips_cp0sel_names,
+ mips_cp0sel_names_len, cp0reg, sel);
+ if (n != NULL)
+ (*info->fprintf_func) (info->stream, "%s", n->name);
+ else
+ (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel);
+ break;
+ }
+
+ case 'E':
+ lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'F':
+ msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case 'G':
+ msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined extension sequence (+%c)"),
+ *d);
+ return;
+ }
+ break;
+
+ case 's':
+ case 'b':
+ case 'r':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]);
+ break;
+
+ case 't':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ break;
+
+ case 'i':
+ case 'u':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE);
+ break;
+
+ case 'j': /* Same as i, but sign-extended. */
+ case 'o':
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ (*info->fprintf_func) (info->stream, "%d",
+ delta);
+ break;
+
+ case 'h':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_PREFX)
+ & OP_MASK_PREFX));
+ break;
+
+ case 'k':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_CACHE)
+ & OP_MASK_CACHE));
+ break;
+
+ case 'a':
+ info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
+ | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2));
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'p':
+ /* Sign extend the displacement. */
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ info->target = (delta << 2) + pc + INSNLEN;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'd':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'U':
+ {
+ /* First check for both rd and rt being equal. */
+ unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ if (reg == ((l >> OP_SH_RT) & OP_MASK_RT))
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else
+ {
+ /* If one is zero use the other. */
+ if (reg == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else /* Bogus, result depends on processor. */
+ (*info->fprintf_func) (info->stream, "%s or %s",
+ mips_gpr_names[reg],
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ }
+ }
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case '<':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_SHAMT) & OP_MASK_SHAMT);
+ break;
+
+ case 'c':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE) & OP_MASK_CODE);
+ break;
+
+ case 'q':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE2) & OP_MASK_CODE2);
+ break;
+
+ case 'C':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_COPZ) & OP_MASK_COPZ);
+ break;
+
+ case 'B':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE20) & OP_MASK_CODE20);
+ break;
+
+ case 'J':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE19) & OP_MASK_CODE19);
+ break;
+
+ case 'S':
+ case 'V':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]);
+ break;
+
+ case 'T':
+ case 'W':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]);
+ break;
+
+ case 'E':
+ /* Coprocessor register for lwcN instructions, et al.
+
+ Note that there is no load/store cp0 instructions, and
+ that FPU (cp1) instructions disassemble this field using
+ 'T' format. Therefore, until we gain understanding of
+ cp2 register names, we can simply print the register
+ numbers. */
+ (*info->fprintf_func) (info->stream, "$%d",
+ (l >> OP_SH_RT) & OP_MASK_RT);
+ break;
+
+ case 'G':
+ /* Coprocessor register for mtcN instructions, et al. Note
+ that FPU (cp1) instructions disassemble this field using
+ 'S' format. Therefore, we only need to worry about cp0,
+ cp2, and cp3. */
+ op = (l >> OP_SH_OP) & OP_MASK_OP;
+ if (op == OP_OP_COP0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ else
+ (*info->fprintf_func) (info->stream, "$%d",
+ (l >> OP_SH_RD) & OP_MASK_RD);
+ break;
+
+ case 'K':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'N':
+ (*info->fprintf_func) (info->stream, "$fcc%d",
+ (l >> OP_SH_BCC) & OP_MASK_BCC);
+ break;
+
+ case 'M':
+ (*info->fprintf_func) (info->stream, "$fcc%d",
+ (l >> OP_SH_CCC) & OP_MASK_CCC);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_PERFREG) & OP_MASK_PERFREG);
+ break;
+
+ case 'e':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE);
+ break;
+
+ case '%':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN);
+ break;
+
+ case 'H':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_SEL) & OP_MASK_SEL);
+ break;
+
+ case 'O':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_ALN) & OP_MASK_ALN);
+ break;
+
+ case 'Q':
+ {
+ unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL;
+ if ((vsel & 0x10) == 0)
+ {
+ int fmt;
+ vsel &= 0x0f;
+ for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+ if ((vsel & 1) == 0)
+ break;
+ (*info->fprintf_func) (info->stream, "$v%d[%d]",
+ (l >> OP_SH_FT) & OP_MASK_FT,
+ vsel >> 1);
+ }
+ else if ((vsel & 0x08) == 0)
+ {
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ else
+ {
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ }
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FD) & OP_MASK_FD);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FS) & OP_MASK_FS);
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined modifier(%c)"),
+ *d);
+ return;
+ }
+ }
+}
+
+/* Check if the object uses NewABI conventions. */
+#if 0
+static int
+is_newabi (header)
+ Elf_Internal_Ehdr *header;
+{
+ /* There are no old-style ABIs which use 64-bit ELF. */
+ if (header->e_ident[EI_CLASS] == ELFCLASS64)
+ return 1;
+
+ /* If a 32-bit ELF file, n32 is a new-style ABI. */
+ if ((header->e_flags & EF_MIPS_ABI2) != 0)
+ return 1;
+
+ return 0;
+}
+#endif
+
+/* Print the mips instruction at address MEMADDR in debugged memory,
+ on using INFO. Returns length of the instruction, in bytes, which is
+ always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if
+ this is little-endian code. */
+
+static int
+print_insn_mips (memaddr, word, info)
+ bfd_vma memaddr;
+ unsigned long int word;
+ struct disassemble_info *info;
+{
+ register const struct mips_opcode *op;
+ static bfd_boolean init = 0;
+ static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
+
+ /* Build a hash table to shorten the search time. */
+ if (! init)
+ {
+ unsigned int i;
+
+ for (i = 0; i <= OP_MASK_OP; i++)
+ {
+ for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo == INSN_MACRO)
+ continue;
+ if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
+ {
+ mips_hash[i] = op;
+ break;
+ }
+ }
+ }
+
+ init = 1;
+ }
+
+ info->bytes_per_chunk = INSNLEN;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP];
+ if (op != NULL)
+ {
+ for (; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo != INSN_MACRO && (word & op->mask) == op->match)
+ {
+ register const char *d;
+
+ /* We always allow to disassemble the jalx instruction. */
+ if (! OPCODE_IS_MEMBER (op, mips_isa, mips_processor)
+ && strcmp (op->name, "jalx"))
+ continue;
+
+ /* Figure out instruction type and branch delay information. */
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_jsr;
+ else
+ info->insn_type = dis_branch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_condjsr;
+ else
+ info->insn_type = dis_condbranch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_STORE_MEMORY
+ | INSN_LOAD_MEMORY_DELAY)) != 0)
+ info->insn_type = dis_dref;
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+
+ d = op->args;
+ if (d != NULL && *d != '\0')
+ {
+ (*info->fprintf_func) (info->stream, "\t");
+ print_insn_args (d, word, memaddr, info);
+ }
+
+ return INSNLEN;
+ }
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ (*info->fprintf_func) (info->stream, "0x%x", word);
+ return INSNLEN;
+}
+
+/* In an environment where we do not know the symbol type of the
+ instruction we are forced to assume that the low order bit of the
+ instructions' address may mark it as a mips16 instruction. If we
+ are single stepping, or the pc is within the disassembled function,
+ this works. Otherwise, we need a clue. Sometimes. */
+
+static int
+_print_insn_mips (memaddr, info, endianness)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+ enum bfd_endian endianness;
+{
+ bfd_byte buffer[INSNLEN];
+ int status;
+
+ set_default_mips_dis_options (info);
+ parse_mips_dis_options (info->disassembler_options);
+
+#if 0
+#if 1
+ /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */
+ /* Only a few tools will work this way. */
+ if (memaddr & 0x01)
+ return print_insn_mips16 (memaddr, info);
+#endif
+
+#if SYMTAB_AVAILABLE
+ if (info->mach == bfd_mach_mips16
+ || (info->flavour == bfd_target_elf_flavour
+ && info->symbols != NULL
+ && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other
+ == STO_MIPS16)))
+ return print_insn_mips16 (memaddr, info);
+#endif
+#endif
+
+ status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
+ if (status == 0)
+ {
+ unsigned long insn;
+
+ if (endianness == BFD_ENDIAN_BIG)
+ insn = (unsigned long) bfd_getb32 (buffer);
+ else
+ insn = (unsigned long) bfd_getl32 (buffer);
+
+ return print_insn_mips (memaddr, insn, info);
+ }
+ else
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+}
+
+int
+print_insn_big_mips (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_BIG);
+}
+
+int
+print_insn_little_mips (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
+}
+
+/* Disassemble mips16 instructions. */
+#if 0
+static int
+print_insn_mips16 (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ int status;
+ bfd_byte buffer[2];
+ int length;
+ int insn;
+ bfd_boolean use_extend;
+ int extend = 0;
+ const struct mips_opcode *op, *opend;
+
+ info->bytes_per_chunk = 2;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ length = 2;
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Handle the extend opcode specially. */
+ use_extend = FALSE;
+ if ((insn & 0xf800) == 0xf000)
+ {
+ use_extend = TRUE;
+ extend = insn & 0x7ff;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Check for an extend opcode followed by an extend opcode. */
+ if ((insn & 0xf800) == 0xf000)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length;
+ }
+
+ length += 2;
+ }
+
+ /* FIXME: Should probably use a hash table on the major opcode here. */
+
+ opend = mips16_opcodes + bfd_mips16_num_opcodes;
+ for (op = mips16_opcodes; op < opend; op++)
+ {
+ if (op->pinfo != INSN_MACRO && (insn & op->mask) == op->match)
+ {
+ const char *s;
+
+ if (strchr (op->args, 'a') != NULL)
+ {
+ if (use_extend)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length - 2;
+ }
+
+ use_extend = FALSE;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2,
+ info);
+ if (status == 0)
+ {
+ use_extend = TRUE;
+ if (info->endian == BFD_ENDIAN_BIG)
+ extend = bfd_getb16 (buffer);
+ else
+ extend = bfd_getl16 (buffer);
+ length += 2;
+ }
+ }
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ if (op->args[0] != '\0')
+ (*info->fprintf_func) (info->stream, "\t");
+
+ for (s = op->args; *s != '\0'; s++)
+ {
+ if (*s == ','
+ && s[1] == 'w'
+ && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)
+ == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ if (*s == ','
+ && s[1] == 'v'
+ && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ)
+ == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr,
+ info);
+ }
+
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ info->branch_delay_insns = 1;
+ if (info->insn_type != dis_jsr)
+ info->insn_type = dis_branch;
+ }
+
+ return length;
+ }
+ }
+
+ if (use_extend)
+ (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000);
+ (*info->fprintf_func) (info->stream, "0x%x", insn);
+ info->insn_type = dis_noninsn;
+
+ return length;
+}
+
+/* Disassemble an operand for a mips16 instruction. */
+
+static void
+print_mips16_insn_arg (type, op, l, use_extend, extend, memaddr, info)
+ char type;
+ const struct mips_opcode *op;
+ int l;
+ bfd_boolean use_extend;
+ int extend;
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ switch (type)
+ {
+ case ',':
+ case '(':
+ case ')':
+ (*info->fprintf_func) (info->stream, "%c", type);
+ break;
+
+ case 'y':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY)]);
+ break;
+
+ case 'x':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX)]);
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ)]);
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z)]);
+ break;
+
+ case '0':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[29]);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "$pc");
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[31]);
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[((l >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32)]);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
+ break;
+
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '6':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ {
+ int immed, nbits, shift, signedp, extbits, pcrel, extu, branch;
+
+ shift = 0;
+ signedp = 0;
+ extbits = 16;
+ pcrel = 0;
+ extu = 0;
+ branch = 0;
+ switch (type)
+ {
+ case '<':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '>':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '[':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 6;
+ extu = 1;
+ break;
+ case ']':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 6;
+ extu = 1;
+ break;
+ case '4':
+ nbits = 4;
+ immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4;
+ signedp = 1;
+ extbits = 15;
+ break;
+ case '5':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 1;
+ break;
+ case 'H':
+ nbits = 5;
+ shift = 1;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 2;
+ break;
+ case 'W':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ if ((op->pinfo & MIPS16_INSN_READ_PC) == 0
+ && (op->pinfo & MIPS16_INSN_READ_SP) == 0)
+ {
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ }
+ break;
+ case 'D':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'j':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ signedp = 1;
+ break;
+ case '6':
+ nbits = 6;
+ immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+ break;
+ case '8':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ break;
+ case 'V':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ /* FIXME: This might be lw, or it might be addiu to $sp or
+ $pc. We assume it's load. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'C':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'U':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ extu = 1;
+ break;
+ case 'k':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'K':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'p':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_condbranch;
+ break;
+ case 'q':
+ nbits = 11;
+ immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_branch;
+ break;
+ case 'A':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ pcrel = 1;
+ /* FIXME: This can be lw or la. We assume it is lw. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'B':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'E':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ break;
+ default:
+ abort ();
+ }
+
+ if (! use_extend)
+ {
+ if (signedp && immed >= (1 << (nbits - 1)))
+ immed -= 1 << nbits;
+ immed <<= shift;
+ if ((type == '<' || type == '>' || type == '[' || type == ']')
+ && immed == 0)
+ immed = 8;
+ }
+ else
+ {
+ if (extbits == 16)
+ immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
+ else if (extbits == 15)
+ immed |= ((extend & 0xf) << 11) | (extend & 0x7f0);
+ else
+ immed = ((extend >> 6) & 0x1f) | (extend & 0x20);
+ immed &= (1 << extbits) - 1;
+ if (! extu && immed >= (1 << (extbits - 1)))
+ immed -= 1 << extbits;
+ }
+
+ if (! pcrel)
+ (*info->fprintf_func) (info->stream, "%d", immed);
+ else
+ {
+ bfd_vma baseaddr;
+
+ if (branch)
+ {
+ immed *= 2;
+ baseaddr = memaddr + 2;
+ }
+ else if (use_extend)
+ baseaddr = memaddr - 2;
+ else
+ {
+ int status;
+ bfd_byte buffer[2];
+
+ baseaddr = memaddr;
+
+ /* If this instruction is in the delay slot of a jr
+ instruction, the base address is the address of the
+ jr instruction. If it is in the delay slot of jalr
+ instruction, the base address is the address of the
+ jalr instruction. This test is unreliable: we have
+ no way of knowing whether the previous word is
+ instruction or data. */
+ status = (*info->read_memory_func) (memaddr - 4, buffer, 2,
+ info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf800) == 0x1800))
+ baseaddr = memaddr - 4;
+ else
+ {
+ status = (*info->read_memory_func) (memaddr - 2, buffer,
+ 2, info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf81f) == 0xe800))
+ baseaddr = memaddr - 2;
+ }
+ }
+ info->target = (baseaddr & ~((1 << shift) - 1)) + immed;
+ (*info->print_address_func) (info->target, info);
+ }
+ }
+ break;
+
+ case 'a':
+ if (! use_extend)
+ extend = 0;
+ l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
+ info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l;
+ (*info->print_address_func) (info->target, info);
+ info->insn_type = dis_jsr;
+ info->branch_delay_insns = 1;
+ break;
+
+ case 'l':
+ case 'L':
+ {
+ int need_comma, amask, smask;
+
+ need_comma = 0;
+
+ l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+
+ amask = (l >> 3) & 7;
+
+ if (amask > 0 && amask < 5)
+ {
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]);
+ if (amask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[amask + 3]);
+ need_comma = 1;
+ }
+
+ smask = (l >> 1) & 3;
+ if (smask == 3)
+ {
+ (*info->fprintf_func) (info->stream, "%s??",
+ need_comma ? "," : "");
+ need_comma = 1;
+ }
+ else if (smask > 0)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[16]);
+ if (smask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[smask + 15]);
+ need_comma = 1;
+ }
+
+ if (l & 1)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[31]);
+ need_comma = 1;
+ }
+
+ if (amask == 5 || amask == 6)
+ {
+ (*info->fprintf_func) (info->stream, "%s$f0",
+ need_comma ? "," : "");
+ if (amask == 6)
+ (*info->fprintf_func) (info->stream, "-$f1");
+ }
+ }
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func)
+ (info->stream,
+ _("# internal disassembler error, unrecognised modifier (%c)"),
+ type);
+ abort ();
+ }
+}
+#endif
+
+void
+print_mips_disassembler_options (stream)
+ FILE *stream;
+{
+ unsigned int i;
+
+ fprintf (stream, _("\n\
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n"));
+
+ fprintf (stream, _("\n\
+ gpr-names=ABI Print GPR names according to specified ABI.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ fpr-names=ABI Print FPR names according to specified ABI.\n\
+ Default: numeric.\n"));
+
+ fprintf (stream, _("\n\
+ cp0-names=ARCH Print CP0 register names according to\n\
+ specified architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ hwr-names=ARCH Print HWR names according to specified \n\
+ architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ABI Print GPR and FPR names according to\n\
+ specified ABI.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ARCH Print CP0 register and HWR names according to\n\
+ specified architecture.\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"ABI\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+ fprintf (stream, " %s", mips_abi_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, The following values are supported for \"ARCH\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+ if (*mips_arch_choices[i].name != '\0')
+ fprintf (stream, " %s", mips_arch_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n"));
+}
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 0000000..09a0299
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,2419 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "disas.h"
+#include <dirent.h>
+
+//#define DEBUG
+//#define DEBUG_COMPLETION
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+/*
+ * Supported types:
+ *
+ * 'F' filename
+ * 'B' block device name
+ * 's' string (accept optional quote)
+ * 'i' 32 bit integer
+ * 'l' target long (32 or 64 bit)
+ * '/' optional gdb-like print format (like "/10x")
+ *
+ * '?' optional type (for 'F', 's' and 'i')
+ *
+ */
+
+typedef struct term_cmd_t {
+ const char *name;
+ const char *args_type;
+ void (*handler)();
+ const char *params;
+ const char *help;
+} term_cmd_t;
+
+static CharDriverState *monitor_hd;
+
+static term_cmd_t term_cmds[];
+static term_cmd_t info_cmds[];
+
+static char term_outbuf[1024];
+static int term_outbuf_index;
+
+static void monitor_start_input(void);
+
+CPUState *mon_cpu = NULL;
+
+void term_flush(void)
+{
+ if (term_outbuf_index > 0) {
+ qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index);
+ term_outbuf_index = 0;
+ }
+}
+
+/* flush at every end of line or if the buffer is full */
+void term_puts(const char *str)
+{
+ int c;
+ for(;;) {
+ c = *str++;
+ if (c == '\0')
+ break;
+ if (c == '\n')
+ term_outbuf[term_outbuf_index++] = '\r';
+ term_outbuf[term_outbuf_index++] = c;
+ if (term_outbuf_index >= (sizeof(term_outbuf) - 1) ||
+ c == '\n')
+ term_flush();
+ }
+}
+
+void term_vprintf(const char *fmt, va_list ap)
+{
+ char buf[4096];
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ term_puts(buf);
+}
+
+void term_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ term_vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ term_vprintf(fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static int compare_cmd(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ int len;
+ len = strlen(name);
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = strchr(p, '|');
+ if (!p)
+ p = pstart + strlen(pstart);
+ if ((p - pstart) == len && !memcmp(pstart, name, len))
+ return 1;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
+{
+ term_cmd_t *cmd;
+
+ for(cmd = cmds; cmd->name != NULL; cmd++) {
+ if (!name || !strcmp(name, cmd->name))
+ term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
+ }
+}
+
+static void help_cmd(const char *name)
+{
+ if (name && !strcmp(name, "info")) {
+ help_cmd1(info_cmds, "info ", NULL);
+ } else {
+ help_cmd1(term_cmds, "", name);
+ if (name && !strcmp(name, "log")) {
+ CPULogItem *item;
+ term_printf("Log items (comma separated):\n");
+ term_printf("%-10s %s\n", "none", "remove all logs");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ term_printf("%-10s %s\n", item->name, item->help);
+ }
+ }
+ }
+}
+
+static void do_help(const char *name)
+{
+ help_cmd(name);
+}
+
+static void do_commit(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_DISKS; i++) {
+ if (bs_table[i]) {
+ bdrv_commit(bs_table[i]);
+ }
+ }
+}
+
+static void do_info(const char *item)
+{
+ term_cmd_t *cmd;
+
+ if (!item)
+ goto help;
+ for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(item, cmd->name))
+ goto found;
+ }
+ help:
+ help_cmd("info");
+ return;
+ found:
+ cmd->handler();
+}
+
+static void do_info_version(void)
+{
+ term_printf("%s\n", QEMU_VERSION);
+}
+
+static void do_info_block(void)
+{
+ bdrv_info();
+}
+
+/* get the current CPU defined by the user */
+int mon_set_cpu(int cpu_index)
+{
+ CPUState *env;
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->cpu_index == cpu_index) {
+ mon_cpu = env;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+CPUState *mon_get_cpu(void)
+{
+ if (!mon_cpu) {
+ mon_set_cpu(0);
+ }
+ return mon_cpu;
+}
+
+static void do_info_registers(void)
+{
+ CPUState *env;
+ env = mon_get_cpu();
+ if (!env)
+ return;
+#ifdef TARGET_I386
+ cpu_dump_state(env, NULL, monitor_fprintf,
+ X86_DUMP_FPU);
+#else
+ cpu_dump_state(env, NULL, monitor_fprintf,
+ 0);
+#endif
+}
+
+static void do_info_cpus(void)
+{
+ CPUState *env;
+
+ /* just to set the default cpu if not already done */
+ mon_get_cpu();
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ term_printf("%c CPU #%d:",
+ (env == mon_cpu) ? '*' : ' ',
+ env->cpu_index);
+#if defined(TARGET_I386)
+ term_printf(" pc=0x" TARGET_FMT_lx, env->eip + env->segs[R_CS].base);
+ if (env->hflags & HF_HALTED_MASK)
+ term_printf(" (halted)");
+#elif defined(TARGET_PPC)
+ term_printf(" nip=0x" TARGET_FMT_lx, env->nip);
+ if (env->halted)
+ term_printf(" (halted)");
+#elif defined(TARGET_SPARC)
+ term_printf(" pc=0x" TARGET_FMT_lx " npc=0x" TARGET_FMT_lx, env->pc, env->npc);
+ if (env->halted)
+ term_printf(" (halted)");
+#endif
+ term_printf("\n");
+ }
+}
+
+static void do_cpu_set(int index)
+{
+ if (mon_set_cpu(index) < 0)
+ term_printf("Invalid CPU index\n");
+}
+
+static void do_info_jit(void)
+{
+ dump_exec_info(NULL, monitor_fprintf);
+}
+
+static void do_info_history (void)
+{
+ int i;
+ const char *str;
+
+ i = 0;
+ for(;;) {
+ str = readline_get_history(i);
+ if (!str)
+ break;
+ term_printf("%d: '%s'\n", i, str);
+ i++;
+ }
+}
+
+static void do_quit(void)
+{
+ exit(0);
+}
+
+static int eject_device(BlockDriverState *bs, int force)
+{
+ if (bdrv_is_inserted(bs)) {
+ if (!force) {
+ if (!bdrv_is_removable(bs)) {
+ term_printf("device is not removable\n");
+ return -1;
+ }
+ if (bdrv_is_locked(bs)) {
+ term_printf("device is locked\n");
+ return -1;
+ }
+ }
+ bdrv_close(bs);
+ }
+ return 0;
+}
+
+static void do_eject(int force, const char *filename)
+{
+ BlockDriverState *bs;
+
+ bs = bdrv_find(filename);
+ if (!bs) {
+ term_printf("device not found\n");
+ return;
+ }
+ eject_device(bs, force);
+}
+
+static void do_change(const char *device, const char *filename)
+{
+ BlockDriverState *bs;
+ int i;
+ char password[256];
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ term_printf("device not found\n");
+ return;
+ }
+ if (eject_device(bs, 0) < 0)
+ return;
+ bdrv_open(bs, filename, 0);
+ if (bdrv_is_encrypted(bs)) {
+ term_printf("%s is encrypted.\n", device);
+ for(i = 0; i < 3; i++) {
+ monitor_readline("Password: ", 1, password, sizeof(password));
+ if (bdrv_set_key(bs, password) == 0)
+ break;
+ term_printf("invalid password\n");
+ }
+ }
+}
+
+static void do_screen_dump(const char *filename)
+{
+ vga_hw_screen_dump(filename);
+}
+
+static void do_log(const char *items)
+{
+ int mask;
+
+ if (!strcmp(items, "none")) {
+ mask = 0;
+ } else {
+ mask = cpu_str_to_log_mask(items);
+ if (!mask) {
+ help_cmd("log");
+ return;
+ }
+ }
+ cpu_set_log(mask);
+}
+
+static void do_savevm(const char *filename)
+{
+ if (qemu_savevm(filename) < 0)
+ term_printf("I/O error when saving VM to '%s'\n", filename);
+}
+
+static void do_loadvm(const char *filename)
+{
+ if (qemu_loadvm(filename) < 0)
+ term_printf("I/O error when loading VM from '%s'\n", filename);
+}
+
+static void do_stop(void)
+{
+ vm_stop(EXCP_INTERRUPT);
+}
+
+static void do_cont(void)
+{
+ vm_start();
+}
+
+#ifdef CONFIG_GDBSTUB
+static void do_gdbserver(int has_port, int port)
+{
+ if (!has_port)
+ port = DEFAULT_GDBSTUB_PORT;
+ if (gdbserver_start(port) < 0) {
+ qemu_printf("Could not open gdbserver socket on port %d\n", port);
+ } else {
+ qemu_printf("Waiting gdb connection on port %d\n", port);
+ }
+}
+#endif
+
+static void term_printc(int c)
+{
+ term_printf("'");
+ switch(c) {
+ case '\'':
+ term_printf("\\'");
+ break;
+ case '\\':
+ term_printf("\\\\");
+ break;
+ case '\n':
+ term_printf("\\n");
+ break;
+ case '\r':
+ term_printf("\\r");
+ break;
+ default:
+ if (c >= 32 && c <= 126) {
+ term_printf("%c", c);
+ } else {
+ term_printf("\\x%02x", c);
+ }
+ break;
+ }
+ term_printf("'");
+}
+
+static void memory_dump(int count, int format, int wsize,
+ target_ulong addr, int is_physical)
+{
+ CPUState *env;
+ int nb_per_line, l, line_size, i, max_digits, len;
+ uint8_t buf[16];
+ uint64_t v;
+
+ if (format == 'i') {
+ int flags;
+ flags = 0;
+ env = mon_get_cpu();
+ if (!env && !is_physical)
+ return;
+#ifdef TARGET_I386
+ if (wsize == 2) {
+ flags = 1;
+ } else if (wsize == 4) {
+ flags = 0;
+ } else {
+ /* as default we use the current CS size */
+ flags = 0;
+ if (env) {
+#ifdef TARGET_X86_64
+ if ((env->efer & MSR_EFER_LMA) &&
+ (env->segs[R_CS].flags & DESC_L_MASK))
+ flags = 2;
+ else
+#endif
+ if (!(env->segs[R_CS].flags & DESC_B_MASK))
+ flags = 1;
+ }
+ }
+#endif
+ monitor_disas(env, addr, count, is_physical, flags);
+ return;
+ }
+
+ len = wsize * count;
+ if (wsize == 1)
+ line_size = 8;
+ else
+ line_size = 16;
+ nb_per_line = line_size / wsize;
+ max_digits = 0;
+
+ switch(format) {
+ case 'o':
+ max_digits = (wsize * 8 + 2) / 3;
+ break;
+ default:
+ case 'x':
+ max_digits = (wsize * 8) / 4;
+ break;
+ case 'u':
+ case 'd':
+ max_digits = (wsize * 8 * 10 + 32) / 33;
+ break;
+ case 'c':
+ wsize = 1;
+ break;
+ }
+
+ while (len > 0) {
+ term_printf(TARGET_FMT_lx ":", addr);
+ l = len;
+ if (l > line_size)
+ l = line_size;
+ if (is_physical) {
+ cpu_physical_memory_rw(addr, buf, l, 0);
+ } else {
+ env = mon_get_cpu();
+ if (!env)
+ break;
+ cpu_memory_rw_debug(env, addr, buf, l, 0);
+ }
+ i = 0;
+ while (i < l) {
+ switch(wsize) {
+ default:
+ case 1:
+ v = ldub_raw(buf + i);
+ break;
+ case 2:
+ v = lduw_raw(buf + i);
+ break;
+ case 4:
+ v = (uint32_t)ldl_raw(buf + i);
+ break;
+ case 8:
+ v = ldq_raw(buf + i);
+ break;
+ }
+ term_printf(" ");
+ switch(format) {
+ case 'o':
+ term_printf("%#*" PRIo64, max_digits, v);
+ break;
+ case 'x':
+ term_printf("0x%0*" PRIx64, max_digits, v);
+ break;
+ case 'u':
+ term_printf("%*" PRIu64, max_digits, v);
+ break;
+ case 'd':
+ term_printf("%*" PRId64, max_digits, v);
+ break;
+ case 'c':
+ term_printc(v);
+ break;
+ }
+ i += wsize;
+ }
+ term_printf("\n");
+ addr += l;
+ len -= l;
+ }
+}
+
+#if TARGET_LONG_BITS == 64
+#define GET_TLONG(h, l) (((uint64_t)(h) << 32) | (l))
+#else
+#define GET_TLONG(h, l) (l)
+#endif
+
+static void do_memory_dump(int count, int format, int size,
+ uint32_t addrh, uint32_t addrl)
+{
+ target_long addr = GET_TLONG(addrh, addrl);
+ memory_dump(count, format, size, addr, 0);
+}
+
+static void do_physical_memory_dump(int count, int format, int size,
+ uint32_t addrh, uint32_t addrl)
+
+{
+ target_long addr = GET_TLONG(addrh, addrl);
+ memory_dump(count, format, size, addr, 1);
+}
+
+static void do_print(int count, int format, int size, unsigned int valh, unsigned int vall)
+{
+ target_long val = GET_TLONG(valh, vall);
+#if TARGET_LONG_BITS == 32
+ switch(format) {
+ case 'o':
+ term_printf("%#o", val);
+ break;
+ case 'x':
+ term_printf("%#x", val);
+ break;
+ case 'u':
+ term_printf("%u", val);
+ break;
+ default:
+ case 'd':
+ term_printf("%d", val);
+ break;
+ case 'c':
+ term_printc(val);
+ break;
+ }
+#else
+ switch(format) {
+ case 'o':
+ term_printf("%#" PRIo64, val);
+ break;
+ case 'x':
+ term_printf("%#" PRIx64, val);
+ break;
+ case 'u':
+ term_printf("%" PRIu64, val);
+ break;
+ default:
+ case 'd':
+ term_printf("%" PRId64, val);
+ break;
+ case 'c':
+ term_printc(val);
+ break;
+ }
+#endif
+ term_printf("\n");
+}
+
+static void do_sum(uint32_t start, uint32_t size)
+{
+ uint32_t addr;
+ uint8_t buf[1];
+ uint16_t sum;
+
+ sum = 0;
+ for(addr = start; addr < (start + size); addr++) {
+ cpu_physical_memory_rw(addr, buf, 1, 0);
+ /* BSD sum algorithm ('sum' Unix command) */
+ sum = (sum >> 1) | (sum << 15);
+ sum += buf[0];
+ }
+ term_printf("%05d\n", sum);
+}
+
+typedef struct {
+ int keycode;
+ const char *name;
+} KeyDef;
+
+static const KeyDef key_defs[] = {
+ { 0x2a, "shift" },
+ { 0x36, "shift_r" },
+
+ { 0x38, "alt" },
+ { 0xb8, "alt_r" },
+ { 0x1d, "ctrl" },
+ { 0x9d, "ctrl_r" },
+
+ { 0xdd, "menu" },
+
+ { 0x01, "esc" },
+
+ { 0x02, "1" },
+ { 0x03, "2" },
+ { 0x04, "3" },
+ { 0x05, "4" },
+ { 0x06, "5" },
+ { 0x07, "6" },
+ { 0x08, "7" },
+ { 0x09, "8" },
+ { 0x0a, "9" },
+ { 0x0b, "0" },
+ { 0x0c, "minus" },
+ { 0x0d, "equal" },
+ { 0x0e, "backspace" },
+
+ { 0x0f, "tab" },
+ { 0x10, "q" },
+ { 0x11, "w" },
+ { 0x12, "e" },
+ { 0x13, "r" },
+ { 0x14, "t" },
+ { 0x15, "y" },
+ { 0x16, "u" },
+ { 0x17, "i" },
+ { 0x18, "o" },
+ { 0x19, "p" },
+
+ { 0x1c, "ret" },
+
+ { 0x1e, "a" },
+ { 0x1f, "s" },
+ { 0x20, "d" },
+ { 0x21, "f" },
+ { 0x22, "g" },
+ { 0x23, "h" },
+ { 0x24, "j" },
+ { 0x25, "k" },
+ { 0x26, "l" },
+
+ { 0x2c, "z" },
+ { 0x2d, "x" },
+ { 0x2e, "c" },
+ { 0x2f, "v" },
+ { 0x30, "b" },
+ { 0x31, "n" },
+ { 0x32, "m" },
+
+ { 0x39, "spc" },
+ { 0x3a, "caps_lock" },
+ { 0x3b, "f1" },
+ { 0x3c, "f2" },
+ { 0x3d, "f3" },
+ { 0x3e, "f4" },
+ { 0x3f, "f5" },
+ { 0x40, "f6" },
+ { 0x41, "f7" },
+ { 0x42, "f8" },
+ { 0x43, "f9" },
+ { 0x44, "f10" },
+ { 0x45, "num_lock" },
+ { 0x46, "scroll_lock" },
+
+ { 0xb5, "kp_divide" },
+ { 0x37, "kp_multiply" },
+ { 0x4a, "kp_substract" },
+ { 0x4e, "kp_add" },
+ { 0x9c, "kp_enter" },
+ { 0x53, "kp_decimal" },
+
+ { 0x52, "kp_0" },
+ { 0x4f, "kp_1" },
+ { 0x50, "kp_2" },
+ { 0x51, "kp_3" },
+ { 0x4b, "kp_4" },
+ { 0x4c, "kp_5" },
+ { 0x4d, "kp_6" },
+ { 0x47, "kp_7" },
+ { 0x48, "kp_8" },
+ { 0x49, "kp_9" },
+
+ { 0x56, "<" },
+
+ { 0x57, "f11" },
+ { 0x58, "f12" },
+
+ { 0xb7, "print" },
+
+ { 0xc7, "home" },
+ { 0xc9, "pgup" },
+ { 0xd1, "pgdn" },
+ { 0xcf, "end" },
+
+ { 0xcb, "left" },
+ { 0xc8, "up" },
+ { 0xd0, "down" },
+ { 0xcd, "right" },
+
+ { 0xd2, "insert" },
+ { 0xd3, "delete" },
+ { 0, NULL },
+};
+
+static int get_keycode(const char *key)
+{
+ const KeyDef *p;
+ char *endp;
+ int ret;
+
+ for(p = key_defs; p->name != NULL; p++) {
+ if (!strcmp(key, p->name))
+ return p->keycode;
+ }
+ if (strstart(key, "0x", NULL)) {
+ ret = strtoul(key, &endp, 0);
+ if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
+ return ret;
+ }
+ return -1;
+}
+
+static void do_send_key(const char *string)
+{
+ char keybuf[16], *q;
+ uint8_t keycodes[16];
+ const char *p;
+ int nb_keycodes, keycode, i;
+
+ nb_keycodes = 0;
+ p = string;
+ while (*p != '\0') {
+ q = keybuf;
+ while (*p != '\0' && *p != '-') {
+ if ((q - keybuf) < sizeof(keybuf) - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ *q = '\0';
+ keycode = get_keycode(keybuf);
+ if (keycode < 0) {
+ term_printf("unknown key: '%s'\n", keybuf);
+ return;
+ }
+ keycodes[nb_keycodes++] = keycode;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ /* key down events */
+ for(i = 0; i < nb_keycodes; i++) {
+ keycode = keycodes[i];
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode & 0x7f);
+ }
+ /* key up events */
+ for(i = nb_keycodes - 1; i >= 0; i--) {
+ keycode = keycodes[i];
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode | 0x80);
+ }
+}
+
+static int mouse_button_state;
+
+static void do_mouse_move(const char *dx_str, const char *dy_str,
+ const char *dz_str)
+{
+ int dx, dy, dz;
+ dx = strtol(dx_str, NULL, 0);
+ dy = strtol(dy_str, NULL, 0);
+ dz = 0;
+ if (dz_str)
+ dz = strtol(dz_str, NULL, 0);
+ kbd_mouse_event(dx, dy, dz, mouse_button_state);
+}
+
+static void do_mouse_button(int button_state)
+{
+ mouse_button_state = button_state;
+ kbd_mouse_event(0, 0, 0, mouse_button_state);
+}
+
+static void do_ioport_read(int count, int format, int size, int addr, int has_index, int index)
+{
+ uint32_t val;
+ int suffix;
+
+ if (has_index) {
+ cpu_outb(NULL, addr & 0xffff, index & 0xff);
+ addr++;
+ }
+ addr &= 0xffff;
+
+ switch(size) {
+ default:
+ case 1:
+ val = cpu_inb(NULL, addr);
+ suffix = 'b';
+ break;
+ case 2:
+ val = cpu_inw(NULL, addr);
+ suffix = 'w';
+ break;
+ case 4:
+ val = cpu_inl(NULL, addr);
+ suffix = 'l';
+ break;
+ }
+ term_printf("port%c[0x%04x] = %#0*x\n",
+ suffix, addr, size * 2, val);
+}
+
+static void do_system_reset(void)
+{
+ qemu_system_reset_request();
+}
+
+static void do_system_powerdown(void)
+{
+ qemu_system_powerdown_request();
+}
+
+#if defined(TARGET_I386)
+static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask)
+{
+ term_printf("%08x: %08x %c%c%c%c%c%c%c%c\n",
+ addr,
+ pte & mask,
+ pte & PG_GLOBAL_MASK ? 'G' : '-',
+ pte & PG_PSE_MASK ? 'P' : '-',
+ pte & PG_DIRTY_MASK ? 'D' : '-',
+ pte & PG_ACCESSED_MASK ? 'A' : '-',
+ pte & PG_PCD_MASK ? 'C' : '-',
+ pte & PG_PWT_MASK ? 'T' : '-',
+ pte & PG_USER_MASK ? 'U' : '-',
+ pte & PG_RW_MASK ? 'W' : '-');
+}
+
+static void tlb_info(void)
+{
+ CPUState *env;
+ int l1, l2;
+ uint32_t pgd, pde, pte;
+
+ env = mon_get_cpu();
+ if (!env)
+ return;
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ term_printf("PG disabled\n");
+ return;
+ }
+ pgd = env->cr[3] & ~0xfff;
+ for(l1 = 0; l1 < 1024; l1++) {
+ cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+ pde = le32_to_cpu(pde);
+ if (pde & PG_PRESENT_MASK) {
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ print_pte((l1 << 22), pde, ~((1 << 20) - 1));
+ } else {
+ for(l2 = 0; l2 < 1024; l2++) {
+ cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+ (uint8_t *)&pte, 4);
+ pte = le32_to_cpu(pte);
+ if (pte & PG_PRESENT_MASK) {
+ print_pte((l1 << 22) + (l2 << 12),
+ pte & ~PG_PSE_MASK,
+ ~0xfff);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void mem_print(uint32_t *pstart, int *plast_prot,
+ uint32_t end, int prot)
+{
+ int prot1;
+ prot1 = *plast_prot;
+ if (prot != prot1) {
+ if (*pstart != -1) {
+ term_printf("%08x-%08x %08x %c%c%c\n",
+ *pstart, end, end - *pstart,
+ prot1 & PG_USER_MASK ? 'u' : '-',
+ 'r',
+ prot1 & PG_RW_MASK ? 'w' : '-');
+ }
+ if (prot != 0)
+ *pstart = end;
+ else
+ *pstart = -1;
+ *plast_prot = prot;
+ }
+}
+
+static void mem_info(void)
+{
+ CPUState *env;
+ int l1, l2, prot, last_prot;
+ uint32_t pgd, pde, pte, start, end;
+
+ env = mon_get_cpu();
+ if (!env)
+ return;
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ term_printf("PG disabled\n");
+ return;
+ }
+ pgd = env->cr[3] & ~0xfff;
+ last_prot = 0;
+ start = -1;
+ for(l1 = 0; l1 < 1024; l1++) {
+ cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+ pde = le32_to_cpu(pde);
+ end = l1 << 22;
+ if (pde & PG_PRESENT_MASK) {
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+ mem_print(&start, &last_prot, end, prot);
+ } else {
+ for(l2 = 0; l2 < 1024; l2++) {
+ cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+ (uint8_t *)&pte, 4);
+ pte = le32_to_cpu(pte);
+ end = (l1 << 22) + (l2 << 12);
+ if (pte & PG_PRESENT_MASK) {
+ prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+ } else {
+ prot = 0;
+ }
+ mem_print(&start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(&start, &last_prot, end, prot);
+ }
+ }
+}
+#endif
+
+static void do_info_kqemu(void)
+{
+#ifdef USE_KQEMU
+ CPUState *env;
+ int val;
+ val = 0;
+ env = mon_get_cpu();
+ if (!env) {
+ term_printf("No cpu initialized yet");
+ return;
+ }
+ val = env->kqemu_enabled;
+ term_printf("kqemu support: ");
+ switch(val) {
+ default:
+ case 0:
+ term_printf("disabled\n");
+ break;
+ case 1:
+ term_printf("enabled for user code\n");
+ break;
+ case 2:
+ term_printf("enabled for user and kernel code\n");
+ break;
+ }
+#else
+ term_printf("kqemu support: not compiled\n");
+#endif
+}
+
+#ifdef CONFIG_PROFILER
+
+int64_t kqemu_time;
+int64_t qemu_time;
+int64_t kqemu_exec_count;
+int64_t dev_time;
+int64_t kqemu_ret_int_count;
+int64_t kqemu_ret_excp_count;
+int64_t kqemu_ret_intr_count;
+
+static void do_info_profile(void)
+{
+ int64_t total;
+ total = qemu_time;
+ if (total == 0)
+ total = 1;
+ term_printf("async time %" PRId64 " (%0.3f)\n",
+ dev_time, dev_time / (double)ticks_per_sec);
+ term_printf("qemu time %" PRId64 " (%0.3f)\n",
+ qemu_time, qemu_time / (double)ticks_per_sec);
+ term_printf("kqemu time %" PRId64 " (%0.3f %0.1f%%) count=%" PRId64 " int=%" PRId64 " excp=%" PRId64 " intr=%" PRId64 "\n",
+ kqemu_time, kqemu_time / (double)ticks_per_sec,
+ kqemu_time / (double)total * 100.0,
+ kqemu_exec_count,
+ kqemu_ret_int_count,
+ kqemu_ret_excp_count,
+ kqemu_ret_intr_count);
+ qemu_time = 0;
+ kqemu_time = 0;
+ kqemu_exec_count = 0;
+ dev_time = 0;
+ kqemu_ret_int_count = 0;
+ kqemu_ret_excp_count = 0;
+ kqemu_ret_intr_count = 0;
+#ifdef USE_KQEMU
+ kqemu_record_dump();
+#endif
+}
+#else
+static void do_info_profile(void)
+{
+ term_printf("Internal profiler not compiled\n");
+}
+#endif
+
+/* Capture support */
+static LIST_HEAD (capture_list_head, CaptureState) capture_head;
+
+static void do_info_capture (void)
+{
+ int i;
+ CaptureState *s;
+
+ for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+ term_printf ("[%d]: ", i);
+ s->ops.info (s->opaque);
+ }
+}
+
+static void do_stop_capture (int n)
+{
+ int i;
+ CaptureState *s;
+
+ for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+ if (i == n) {
+ s->ops.destroy (s->opaque);
+ LIST_REMOVE (s, entries);
+ qemu_free (s);
+ return;
+ }
+ }
+}
+
+#ifdef HAS_AUDIO
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+ int bits, int nchannels);
+
+static void do_wav_capture (const char *path,
+ int has_freq, int freq,
+ int has_bits, int bits,
+ int has_channels, int nchannels)
+{
+ CaptureState *s;
+
+ s = qemu_mallocz (sizeof (*s));
+ if (!s) {
+ term_printf ("Not enough memory to add wave capture\n");
+ return;
+ }
+
+ freq = has_freq ? freq : 44100;
+ bits = has_bits ? bits : 16;
+ nchannels = has_channels ? nchannels : 2;
+
+ if (wav_start_capture (s, path, freq, bits, nchannels)) {
+ term_printf ("Faied to add wave capture\n");
+ qemu_free (s);
+ }
+ LIST_INSERT_HEAD (&capture_head, s, entries);
+}
+#endif
+
+static term_cmd_t term_cmds[] = {
+ { "help|?", "s?", do_help,
+ "[cmd]", "show the help" },
+ { "commit", "", do_commit,
+ "", "commit changes to the disk images (if -snapshot is used)" },
+ { "info", "s?", do_info,
+ "subcommand", "show various information about the system state" },
+ { "q|quit", "", do_quit,
+ "", "quit the emulator" },
+ { "eject", "-fB", do_eject,
+ "[-f] device", "eject a removable media (use -f to force it)" },
+ { "change", "BF", do_change,
+ "device filename", "change a removable media" },
+ { "screendump", "F", do_screen_dump,
+ "filename", "save screen into PPM image 'filename'" },
+ { "log", "s", do_log,
+ "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
+ { "savevm", "F", do_savevm,
+ "filename", "save the whole virtual machine state to 'filename'" },
+ { "loadvm", "F", do_loadvm,
+ "filename", "restore the whole virtual machine state from 'filename'" },
+ { "stop", "", do_stop,
+ "", "stop emulation", },
+ { "c|cont", "", do_cont,
+ "", "resume emulation", },
+#ifdef CONFIG_GDBSTUB
+ { "gdbserver", "i?", do_gdbserver,
+ "[port]", "start gdbserver session (default port=1234)", },
+#endif
+ { "x", "/l", do_memory_dump,
+ "/fmt addr", "virtual memory dump starting at 'addr'", },
+ { "xp", "/l", do_physical_memory_dump,
+ "/fmt addr", "physical memory dump starting at 'addr'", },
+ { "p|print", "/l", do_print,
+ "/fmt expr", "print expression value (use $reg for CPU register access)", },
+ { "i", "/ii.", do_ioport_read,
+ "/fmt addr", "I/O port read" },
+
+ { "sendkey", "s", do_send_key,
+ "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" },
+ { "system_reset", "", do_system_reset,
+ "", "reset the system" },
+ { "system_powerdown", "", do_system_powerdown,
+ "", "send system power down event" },
+ { "sum", "ii", do_sum,
+ "addr size", "compute the checksum of a memory region" },
+ { "usb_add", "s", do_usb_add,
+ "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
+ { "usb_del", "s", do_usb_del,
+ "device", "remove USB device 'bus.addr'" },
+ { "cpu", "i", do_cpu_set,
+ "index", "set the default CPU" },
+ { "mouse_move", "sss?", do_mouse_move,
+ "dx dy [dz]", "send mouse move events" },
+ { "mouse_button", "i", do_mouse_button,
+ "state", "change mouse button state (1=L, 2=M, 4=R)" },
+#ifdef HAS_AUDIO
+ { "wavcapture", "si?i?i?", do_wav_capture,
+ "path [frequency bits channels]",
+ "capture audio to a wave file (default frequency=44100 bits=16 channels=2)" },
+#endif
+ { "stopcapture", "i", do_stop_capture,
+ "capture index", "stop capture" },
+ { NULL, NULL, },
+};
+
+static term_cmd_t info_cmds[] = {
+ { "version", "", do_info_version,
+ "", "show the version of qemu" },
+ { "network", "", do_info_network,
+ "", "show the network state" },
+ { "block", "", do_info_block,
+ "", "show the block devices" },
+ { "registers", "", do_info_registers,
+ "", "show the cpu registers" },
+ { "cpus", "", do_info_cpus,
+ "", "show infos for each CPU" },
+ { "history", "", do_info_history,
+ "", "show the command line history", },
+ { "irq", "", irq_info,
+ "", "show the interrupts statistics (if available)", },
+ { "pic", "", pic_info,
+ "", "show i8259 (PIC) state", },
+ { "pci", "", pci_info,
+ "", "show PCI info", },
+#if defined(TARGET_I386)
+ { "tlb", "", tlb_info,
+ "", "show virtual to physical memory mappings", },
+ { "mem", "", mem_info,
+ "", "show the active virtual memory mappings", },
+#endif
+ { "jit", "", do_info_jit,
+ "", "show dynamic compiler info", },
+ { "kqemu", "", do_info_kqemu,
+ "", "show kqemu information", },
+ { "usb", "", usb_info,
+ "", "show guest USB devices", },
+ { "usbhost", "", usb_host_info,
+ "", "show host USB devices", },
+ { "profile", "", do_info_profile,
+ "", "show profiling information", },
+ { "capture", "", do_info_capture,
+ "show capture information" },
+ { NULL, NULL, },
+};
+
+/*******************************************************************/
+
+static const char *pch;
+static jmp_buf expr_env;
+
+#define MD_TLONG 0
+#define MD_I32 1
+
+typedef struct MonitorDef {
+ const char *name;
+ int offset;
+ target_long (*get_value)(struct MonitorDef *md, int val);
+ int type;
+} MonitorDef;
+
+#if defined(TARGET_I386)
+static target_long monitor_get_pc (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return env->eip + env->segs[R_CS].base;
+}
+#endif
+
+#if defined(TARGET_PPC)
+static target_long monitor_get_ccr (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ unsigned int u;
+ int i;
+
+ if (!env)
+ return 0;
+
+ u = 0;
+ for (i = 0; i < 8; i++)
+ u |= env->crf[i] << (32 - (4 * i));
+
+ return u;
+}
+
+static target_long monitor_get_msr (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return (env->msr[MSR_POW] << MSR_POW) |
+ (env->msr[MSR_ILE] << MSR_ILE) |
+ (env->msr[MSR_EE] << MSR_EE) |
+ (env->msr[MSR_PR] << MSR_PR) |
+ (env->msr[MSR_FP] << MSR_FP) |
+ (env->msr[MSR_ME] << MSR_ME) |
+ (env->msr[MSR_FE0] << MSR_FE0) |
+ (env->msr[MSR_SE] << MSR_SE) |
+ (env->msr[MSR_BE] << MSR_BE) |
+ (env->msr[MSR_FE1] << MSR_FE1) |
+ (env->msr[MSR_IP] << MSR_IP) |
+ (env->msr[MSR_IR] << MSR_IR) |
+ (env->msr[MSR_DR] << MSR_DR) |
+ (env->msr[MSR_RI] << MSR_RI) |
+ (env->msr[MSR_LE] << MSR_LE);
+}
+
+static target_long monitor_get_xer (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return (env->xer[XER_SO] << XER_SO) |
+ (env->xer[XER_OV] << XER_OV) |
+ (env->xer[XER_CA] << XER_CA) |
+ (env->xer[XER_BC] << XER_BC);
+}
+
+static target_long monitor_get_decr (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return cpu_ppc_load_decr(env);
+}
+
+static target_long monitor_get_tbu (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return cpu_ppc_load_tbu(env);
+}
+
+static target_long monitor_get_tbl (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return cpu_ppc_load_tbl(env);
+}
+#endif
+
+#if defined(TARGET_SPARC)
+#ifndef TARGET_SPARC64
+static target_long monitor_get_psr (struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return GET_PSR(env);
+}
+#endif
+
+static target_long monitor_get_reg(struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return 0;
+ return env->regwptr[val];
+}
+#endif
+
+static MonitorDef monitor_defs[] = {
+#ifdef TARGET_I386
+
+#define SEG(name, seg) \
+ { name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\
+ { name ".base", offsetof(CPUState, segs[seg].base) },\
+ { name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 },
+
+ { "eax", offsetof(CPUState, regs[0]) },
+ { "ecx", offsetof(CPUState, regs[1]) },
+ { "edx", offsetof(CPUState, regs[2]) },
+ { "ebx", offsetof(CPUState, regs[3]) },
+ { "esp|sp", offsetof(CPUState, regs[4]) },
+ { "ebp|fp", offsetof(CPUState, regs[5]) },
+ { "esi", offsetof(CPUState, regs[6]) },
+ { "edi", offsetof(CPUState, regs[7]) },
+#ifdef TARGET_X86_64
+ { "r8", offsetof(CPUState, regs[8]) },
+ { "r9", offsetof(CPUState, regs[9]) },
+ { "r10", offsetof(CPUState, regs[10]) },
+ { "r11", offsetof(CPUState, regs[11]) },
+ { "r12", offsetof(CPUState, regs[12]) },
+ { "r13", offsetof(CPUState, regs[13]) },
+ { "r14", offsetof(CPUState, regs[14]) },
+ { "r15", offsetof(CPUState, regs[15]) },
+#endif
+ { "eflags", offsetof(CPUState, eflags) },
+ { "eip", offsetof(CPUState, eip) },
+ SEG("cs", R_CS)
+ SEG("ds", R_DS)
+ SEG("es", R_ES)
+ SEG("ss", R_SS)
+ SEG("fs", R_FS)
+ SEG("gs", R_GS)
+ { "pc", 0, monitor_get_pc, },
+#elif defined(TARGET_PPC)
+ { "r0", offsetof(CPUState, gpr[0]) },
+ { "r1", offsetof(CPUState, gpr[1]) },
+ { "r2", offsetof(CPUState, gpr[2]) },
+ { "r3", offsetof(CPUState, gpr[3]) },
+ { "r4", offsetof(CPUState, gpr[4]) },
+ { "r5", offsetof(CPUState, gpr[5]) },
+ { "r6", offsetof(CPUState, gpr[6]) },
+ { "r7", offsetof(CPUState, gpr[7]) },
+ { "r8", offsetof(CPUState, gpr[8]) },
+ { "r9", offsetof(CPUState, gpr[9]) },
+ { "r10", offsetof(CPUState, gpr[10]) },
+ { "r11", offsetof(CPUState, gpr[11]) },
+ { "r12", offsetof(CPUState, gpr[12]) },
+ { "r13", offsetof(CPUState, gpr[13]) },
+ { "r14", offsetof(CPUState, gpr[14]) },
+ { "r15", offsetof(CPUState, gpr[15]) },
+ { "r16", offsetof(CPUState, gpr[16]) },
+ { "r17", offsetof(CPUState, gpr[17]) },
+ { "r18", offsetof(CPUState, gpr[18]) },
+ { "r19", offsetof(CPUState, gpr[19]) },
+ { "r20", offsetof(CPUState, gpr[20]) },
+ { "r21", offsetof(CPUState, gpr[21]) },
+ { "r22", offsetof(CPUState, gpr[22]) },
+ { "r23", offsetof(CPUState, gpr[23]) },
+ { "r24", offsetof(CPUState, gpr[24]) },
+ { "r25", offsetof(CPUState, gpr[25]) },
+ { "r26", offsetof(CPUState, gpr[26]) },
+ { "r27", offsetof(CPUState, gpr[27]) },
+ { "r28", offsetof(CPUState, gpr[28]) },
+ { "r29", offsetof(CPUState, gpr[29]) },
+ { "r30", offsetof(CPUState, gpr[30]) },
+ { "r31", offsetof(CPUState, gpr[31]) },
+ { "nip|pc", offsetof(CPUState, nip) },
+ { "lr", offsetof(CPUState, lr) },
+ { "ctr", offsetof(CPUState, ctr) },
+ { "decr", 0, &monitor_get_decr, },
+ { "ccr", 0, &monitor_get_ccr, },
+ { "msr", 0, &monitor_get_msr, },
+ { "xer", 0, &monitor_get_xer, },
+ { "tbu", 0, &monitor_get_tbu, },
+ { "tbl", 0, &monitor_get_tbl, },
+ { "sdr1", offsetof(CPUState, sdr1) },
+ { "sr0", offsetof(CPUState, sr[0]) },
+ { "sr1", offsetof(CPUState, sr[1]) },
+ { "sr2", offsetof(CPUState, sr[2]) },
+ { "sr3", offsetof(CPUState, sr[3]) },
+ { "sr4", offsetof(CPUState, sr[4]) },
+ { "sr5", offsetof(CPUState, sr[5]) },
+ { "sr6", offsetof(CPUState, sr[6]) },
+ { "sr7", offsetof(CPUState, sr[7]) },
+ { "sr8", offsetof(CPUState, sr[8]) },
+ { "sr9", offsetof(CPUState, sr[9]) },
+ { "sr10", offsetof(CPUState, sr[10]) },
+ { "sr11", offsetof(CPUState, sr[11]) },
+ { "sr12", offsetof(CPUState, sr[12]) },
+ { "sr13", offsetof(CPUState, sr[13]) },
+ { "sr14", offsetof(CPUState, sr[14]) },
+ { "sr15", offsetof(CPUState, sr[15]) },
+ /* Too lazy to put BATs and SPRs ... */
+#elif defined(TARGET_SPARC)
+ { "g0", offsetof(CPUState, gregs[0]) },
+ { "g1", offsetof(CPUState, gregs[1]) },
+ { "g2", offsetof(CPUState, gregs[2]) },
+ { "g3", offsetof(CPUState, gregs[3]) },
+ { "g4", offsetof(CPUState, gregs[4]) },
+ { "g5", offsetof(CPUState, gregs[5]) },
+ { "g6", offsetof(CPUState, gregs[6]) },
+ { "g7", offsetof(CPUState, gregs[7]) },
+ { "o0", 0, monitor_get_reg },
+ { "o1", 1, monitor_get_reg },
+ { "o2", 2, monitor_get_reg },
+ { "o3", 3, monitor_get_reg },
+ { "o4", 4, monitor_get_reg },
+ { "o5", 5, monitor_get_reg },
+ { "o6", 6, monitor_get_reg },
+ { "o7", 7, monitor_get_reg },
+ { "l0", 8, monitor_get_reg },
+ { "l1", 9, monitor_get_reg },
+ { "l2", 10, monitor_get_reg },
+ { "l3", 11, monitor_get_reg },
+ { "l4", 12, monitor_get_reg },
+ { "l5", 13, monitor_get_reg },
+ { "l6", 14, monitor_get_reg },
+ { "l7", 15, monitor_get_reg },
+ { "i0", 16, monitor_get_reg },
+ { "i1", 17, monitor_get_reg },
+ { "i2", 18, monitor_get_reg },
+ { "i3", 19, monitor_get_reg },
+ { "i4", 20, monitor_get_reg },
+ { "i5", 21, monitor_get_reg },
+ { "i6", 22, monitor_get_reg },
+ { "i7", 23, monitor_get_reg },
+ { "pc", offsetof(CPUState, pc) },
+ { "npc", offsetof(CPUState, npc) },
+ { "y", offsetof(CPUState, y) },
+#ifndef TARGET_SPARC64
+ { "psr", 0, &monitor_get_psr, },
+ { "wim", offsetof(CPUState, wim) },
+#endif
+ { "tbr", offsetof(CPUState, tbr) },
+ { "fsr", offsetof(CPUState, fsr) },
+ { "f0", offsetof(CPUState, fpr[0]) },
+ { "f1", offsetof(CPUState, fpr[1]) },
+ { "f2", offsetof(CPUState, fpr[2]) },
+ { "f3", offsetof(CPUState, fpr[3]) },
+ { "f4", offsetof(CPUState, fpr[4]) },
+ { "f5", offsetof(CPUState, fpr[5]) },
+ { "f6", offsetof(CPUState, fpr[6]) },
+ { "f7", offsetof(CPUState, fpr[7]) },
+ { "f8", offsetof(CPUState, fpr[8]) },
+ { "f9", offsetof(CPUState, fpr[9]) },
+ { "f10", offsetof(CPUState, fpr[10]) },
+ { "f11", offsetof(CPUState, fpr[11]) },
+ { "f12", offsetof(CPUState, fpr[12]) },
+ { "f13", offsetof(CPUState, fpr[13]) },
+ { "f14", offsetof(CPUState, fpr[14]) },
+ { "f15", offsetof(CPUState, fpr[15]) },
+ { "f16", offsetof(CPUState, fpr[16]) },
+ { "f17", offsetof(CPUState, fpr[17]) },
+ { "f18", offsetof(CPUState, fpr[18]) },
+ { "f19", offsetof(CPUState, fpr[19]) },
+ { "f20", offsetof(CPUState, fpr[20]) },
+ { "f21", offsetof(CPUState, fpr[21]) },
+ { "f22", offsetof(CPUState, fpr[22]) },
+ { "f23", offsetof(CPUState, fpr[23]) },
+ { "f24", offsetof(CPUState, fpr[24]) },
+ { "f25", offsetof(CPUState, fpr[25]) },
+ { "f26", offsetof(CPUState, fpr[26]) },
+ { "f27", offsetof(CPUState, fpr[27]) },
+ { "f28", offsetof(CPUState, fpr[28]) },
+ { "f29", offsetof(CPUState, fpr[29]) },
+ { "f30", offsetof(CPUState, fpr[30]) },
+ { "f31", offsetof(CPUState, fpr[31]) },
+#ifdef TARGET_SPARC64
+ { "f32", offsetof(CPUState, fpr[32]) },
+ { "f34", offsetof(CPUState, fpr[34]) },
+ { "f36", offsetof(CPUState, fpr[36]) },
+ { "f38", offsetof(CPUState, fpr[38]) },
+ { "f40", offsetof(CPUState, fpr[40]) },
+ { "f42", offsetof(CPUState, fpr[42]) },
+ { "f44", offsetof(CPUState, fpr[44]) },
+ { "f46", offsetof(CPUState, fpr[46]) },
+ { "f48", offsetof(CPUState, fpr[48]) },
+ { "f50", offsetof(CPUState, fpr[50]) },
+ { "f52", offsetof(CPUState, fpr[52]) },
+ { "f54", offsetof(CPUState, fpr[54]) },
+ { "f56", offsetof(CPUState, fpr[56]) },
+ { "f58", offsetof(CPUState, fpr[58]) },
+ { "f60", offsetof(CPUState, fpr[60]) },
+ { "f62", offsetof(CPUState, fpr[62]) },
+ { "asi", offsetof(CPUState, asi) },
+ { "pstate", offsetof(CPUState, pstate) },
+ { "cansave", offsetof(CPUState, cansave) },
+ { "canrestore", offsetof(CPUState, canrestore) },
+ { "otherwin", offsetof(CPUState, otherwin) },
+ { "wstate", offsetof(CPUState, wstate) },
+ { "cleanwin", offsetof(CPUState, cleanwin) },
+ { "fprs", offsetof(CPUState, fprs) },
+#endif
+#endif
+ { NULL },
+};
+
+static void expr_error(const char *fmt)
+{
+ term_printf(fmt);
+ term_printf("\n");
+ longjmp(expr_env, 1);
+}
+
+/* return 0 if OK, -1 if not found, -2 if no CPU defined */
+static int get_monitor_def(target_long *pval, const char *name)
+{
+ MonitorDef *md;
+ void *ptr;
+
+ for(md = monitor_defs; md->name != NULL; md++) {
+ if (compare_cmd(name, md->name)) {
+ if (md->get_value) {
+ *pval = md->get_value(md, md->offset);
+ } else {
+ CPUState *env = mon_get_cpu();
+ if (!env)
+ return -2;
+ ptr = (uint8_t *)env + md->offset;
+ switch(md->type) {
+ case MD_I32:
+ *pval = *(int32_t *)ptr;
+ break;
+ case MD_TLONG:
+ *pval = *(target_long *)ptr;
+ break;
+ default:
+ *pval = 0;
+ break;
+ }
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void next(void)
+{
+ if (pch != '\0') {
+ pch++;
+ while (isspace(*pch))
+ pch++;
+ }
+}
+
+static target_long expr_sum(void);
+
+static target_long expr_unary(void)
+{
+ target_long n;
+ char *p;
+ int ret;
+
+ switch(*pch) {
+ case '+':
+ next();
+ n = expr_unary();
+ break;
+ case '-':
+ next();
+ n = -expr_unary();
+ break;
+ case '~':
+ next();
+ n = ~expr_unary();
+ break;
+ case '(':
+ next();
+ n = expr_sum();
+ if (*pch != ')') {
+ expr_error("')' expected");
+ }
+ next();
+ break;
+ case '\'':
+ pch++;
+ if (*pch == '\0')
+ expr_error("character constant expected");
+ n = *pch;
+ pch++;
+ if (*pch != '\'')
+ expr_error("missing terminating \' character");
+ next();
+ break;
+ case '$':
+ {
+ char buf[128], *q;
+
+ pch++;
+ q = buf;
+ while ((*pch >= 'a' && *pch <= 'z') ||
+ (*pch >= 'A' && *pch <= 'Z') ||
+ (*pch >= '0' && *pch <= '9') ||
+ *pch == '_' || *pch == '.') {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = *pch;
+ pch++;
+ }
+ while (isspace(*pch))
+ pch++;
+ *q = 0;
+ ret = get_monitor_def(&n, buf);
+ if (ret == -1)
+ expr_error("unknown register");
+ else if (ret == -2)
+ expr_error("no cpu defined");
+ }
+ break;
+ case '\0':
+ expr_error("unexpected end of expression");
+ n = 0;
+ break;
+ default:
+#if TARGET_LONG_BITS == 64
+ n = strtoull(pch, &p, 0);
+#else
+ n = strtoul(pch, &p, 0);
+#endif
+ if (pch == p) {
+ expr_error("invalid char in expression");
+ }
+ pch = p;
+ while (isspace(*pch))
+ pch++;
+ break;
+ }
+ return n;
+}
+
+
+static target_long expr_prod(void)
+{
+ target_long val, val2;
+ int op;
+
+ val = expr_unary();
+ for(;;) {
+ op = *pch;
+ if (op != '*' && op != '/' && op != '%')
+ break;
+ next();
+ val2 = expr_unary();
+ switch(op) {
+ default:
+ case '*':
+ val *= val2;
+ break;
+ case '/':
+ case '%':
+ if (val2 == 0)
+ expr_error("division by zero");
+ if (op == '/')
+ val /= val2;
+ else
+ val %= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static target_long expr_logic(void)
+{
+ target_long val, val2;
+ int op;
+
+ val = expr_prod();
+ for(;;) {
+ op = *pch;
+ if (op != '&' && op != '|' && op != '^')
+ break;
+ next();
+ val2 = expr_prod();
+ switch(op) {
+ default:
+ case '&':
+ val &= val2;
+ break;
+ case '|':
+ val |= val2;
+ break;
+ case '^':
+ val ^= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static target_long expr_sum(void)
+{
+ target_long val, val2;
+ int op;
+
+ val = expr_logic();
+ for(;;) {
+ op = *pch;
+ if (op != '+' && op != '-')
+ break;
+ next();
+ val2 = expr_logic();
+ if (op == '+')
+ val += val2;
+ else
+ val -= val2;
+ }
+ return val;
+}
+
+static int get_expr(target_long *pval, const char **pp)
+{
+ pch = *pp;
+ if (setjmp(expr_env)) {
+ *pp = pch;
+ return -1;
+ }
+ while (isspace(*pch))
+ pch++;
+ *pval = expr_sum();
+ *pp = pch;
+ return 0;
+}
+
+static int get_str(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int c;
+
+ q = buf;
+ p = *pp;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0') {
+ fail:
+ *q = '\0';
+ *pp = p;
+ return -1;
+ }
+ if (*p == '\"') {
+ p++;
+ while (*p != '\0' && *p != '\"') {
+ if (*p == '\\') {
+ p++;
+ c = *p++;
+ switch(c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '\\':
+ case '\'':
+ case '\"':
+ break;
+ default:
+ qemu_printf("unsupported escape code: '\\%c'\n", c);
+ goto fail;
+ }
+ if ((q - buf) < buf_size - 1) {
+ *q++ = c;
+ }
+ } else {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ if (*p != '\"') {
+ qemu_printf("unterminated string\n");
+ goto fail;
+ }
+ p++;
+ } else {
+ while (*p != '\0' && !isspace(*p)) {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return 0;
+}
+
+static int default_fmt_format = 'x';
+static int default_fmt_size = 4;
+
+#define MAX_ARGS 16
+
+static void monitor_handle_command(const char *cmdline)
+{
+ const char *p, *pstart, *typestr;
+ char *q;
+ int c, nb_args, len, i, has_arg;
+ term_cmd_t *cmd;
+ char cmdname[256];
+ char buf[1024];
+ void *str_allocated[MAX_ARGS];
+ void *args[MAX_ARGS];
+
+#ifdef DEBUG
+ term_printf("command='%s'\n", cmdline);
+#endif
+
+ /* extract the command name */
+ p = cmdline;
+ q = cmdname;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0')
+ return;
+ pstart = p;
+ while (*p != '\0' && *p != '/' && !isspace(*p))
+ p++;
+ len = p - pstart;
+ if (len > sizeof(cmdname) - 1)
+ len = sizeof(cmdname) - 1;
+ memcpy(cmdname, pstart, len);
+ cmdname[len] = '\0';
+
+ /* find the command */
+ for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(cmdname, cmd->name))
+ goto found;
+ }
+ term_printf("unknown command: '%s'\n", cmdname);
+ return;
+ found:
+
+ for(i = 0; i < MAX_ARGS; i++)
+ str_allocated[i] = NULL;
+
+ /* parse the parameters */
+ typestr = cmd->args_type;
+ nb_args = 0;
+ for(;;) {
+ c = *typestr;
+ if (c == '\0')
+ break;
+ typestr++;
+ switch(c) {
+ case 'F':
+ case 'B':
+ case 's':
+ {
+ int ret;
+ char *str;
+
+ while (isspace(*p))
+ p++;
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ /* no optional string: NULL argument */
+ str = NULL;
+ goto add_str;
+ }
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ switch(c) {
+ case 'F':
+ term_printf("%s: filename expected\n", cmdname);
+ break;
+ case 'B':
+ term_printf("%s: block device name expected\n", cmdname);
+ break;
+ default:
+ term_printf("%s: string expected\n", cmdname);
+ break;
+ }
+ goto fail;
+ }
+ str = qemu_malloc(strlen(buf) + 1);
+ strcpy(str, buf);
+ str_allocated[nb_args] = str;
+ add_str:
+ if (nb_args >= MAX_ARGS) {
+ error_args:
+ term_printf("%s: too many arguments\n", cmdname);
+ goto fail;
+ }
+ args[nb_args++] = str;
+ }
+ break;
+ case '/':
+ {
+ int count, format, size;
+
+ while (isspace(*p))
+ p++;
+ if (*p == '/') {
+ /* format found */
+ p++;
+ count = 1;
+ if (isdigit(*p)) {
+ count = 0;
+ while (isdigit(*p)) {
+ count = count * 10 + (*p - '0');
+ p++;
+ }
+ }
+ size = -1;
+ format = -1;
+ for(;;) {
+ switch(*p) {
+ case 'o':
+ case 'd':
+ case 'u':
+ case 'x':
+ case 'i':
+ case 'c':
+ format = *p++;
+ break;
+ case 'b':
+ size = 1;
+ p++;
+ break;
+ case 'h':
+ size = 2;
+ p++;
+ break;
+ case 'w':
+ size = 4;
+ p++;
+ break;
+ case 'g':
+ case 'L':
+ size = 8;
+ p++;
+ break;
+ default:
+ goto next;
+ }
+ }
+ next:
+ if (*p != '\0' && !isspace(*p)) {
+ term_printf("invalid char in format: '%c'\n", *p);
+ goto fail;
+ }
+ if (format < 0)
+ format = default_fmt_format;
+ if (format != 'i') {
+ /* for 'i', not specifying a size gives -1 as size */
+ if (size < 0)
+ size = default_fmt_size;
+ }
+ default_fmt_size = size;
+ default_fmt_format = format;
+ } else {
+ count = 1;
+ format = default_fmt_format;
+ if (format != 'i') {
+ size = default_fmt_size;
+ } else {
+ size = -1;
+ }
+ }
+ if (nb_args + 3 > MAX_ARGS)
+ goto error_args;
+ args[nb_args++] = (void*)count;
+ args[nb_args++] = (void*)format;
+ args[nb_args++] = (void*)size;
+ }
+ break;
+ case 'i':
+ case 'l':
+ {
+ target_long val;
+ while (isspace(*p))
+ p++;
+ if (*typestr == '?' || *typestr == '.') {
+ if (*typestr == '?') {
+ if (*p == '\0')
+ has_arg = 0;
+ else
+ has_arg = 1;
+ } else {
+ if (*p == '.') {
+ p++;
+ while (isspace(*p))
+ p++;
+ has_arg = 1;
+ } else {
+ has_arg = 0;
+ }
+ }
+ typestr++;
+ if (nb_args >= MAX_ARGS)
+ goto error_args;
+ args[nb_args++] = (void *)has_arg;
+ if (!has_arg) {
+ if (nb_args >= MAX_ARGS)
+ goto error_args;
+ val = -1;
+ goto add_num;
+ }
+ }
+ if (get_expr(&val, &p))
+ goto fail;
+ add_num:
+ if (c == 'i') {
+ if (nb_args >= MAX_ARGS)
+ goto error_args;
+ args[nb_args++] = (void *)(int)val;
+ } else {
+ if ((nb_args + 1) >= MAX_ARGS)
+ goto error_args;
+#if TARGET_LONG_BITS == 64
+ args[nb_args++] = (void *)(int)((val >> 32) & 0xffffffff);
+#else
+ args[nb_args++] = (void *)0;
+#endif
+ args[nb_args++] = (void *)(int)(val & 0xffffffff);
+ }
+ }
+ break;
+ case '-':
+ {
+ int has_option;
+ /* option */
+
+ c = *typestr++;
+ if (c == '\0')
+ goto bad_type;
+ while (isspace(*p))
+ p++;
+ has_option = 0;
+ if (*p == '-') {
+ p++;
+ if (*p != c) {
+ term_printf("%s: unsupported option -%c\n",
+ cmdname, *p);
+ goto fail;
+ }
+ p++;
+ has_option = 1;
+ }
+ if (nb_args >= MAX_ARGS)
+ goto error_args;
+ args[nb_args++] = (void *)has_option;
+ }
+ break;
+ default:
+ bad_type:
+ term_printf("%s: unknown type '%c'\n", cmdname, c);
+ goto fail;
+ }
+ }
+ /* check that all arguments were parsed */
+ while (isspace(*p))
+ p++;
+ if (*p != '\0') {
+ term_printf("%s: extraneous characters at the end of line\n",
+ cmdname);
+ goto fail;
+ }
+
+ switch(nb_args) {
+ case 0:
+ cmd->handler();
+ break;
+ case 1:
+ cmd->handler(args[0]);
+ break;
+ case 2:
+ cmd->handler(args[0], args[1]);
+ break;
+ case 3:
+ cmd->handler(args[0], args[1], args[2]);
+ break;
+ case 4:
+ cmd->handler(args[0], args[1], args[2], args[3]);
+ break;
+ case 5:
+ cmd->handler(args[0], args[1], args[2], args[3], args[4]);
+ break;
+ case 6:
+ cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 7:
+ cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ default:
+ term_printf("unsupported number of arguments: %d\n", nb_args);
+ goto fail;
+ }
+ fail:
+ for(i = 0; i < MAX_ARGS; i++)
+ qemu_free(str_allocated[i]);
+ return;
+}
+
+static void cmd_completion(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ char cmd[128];
+ int len;
+
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = strchr(p, '|');
+ if (!p)
+ p = pstart + strlen(pstart);
+ len = p - pstart;
+ if (len > sizeof(cmd) - 2)
+ len = sizeof(cmd) - 2;
+ memcpy(cmd, pstart, len);
+ cmd[len] = '\0';
+ if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
+ add_completion(cmd);
+ }
+ if (*p == '\0')
+ break;
+ p++;
+ }
+}
+
+static void file_completion(const char *input)
+{
+ DIR *ffs;
+ struct dirent *d;
+ char path[1024];
+ char file[1024], file_prefix[1024];
+ int input_path_len;
+ const char *p;
+
+ p = strrchr(input, '/');
+ if (!p) {
+ input_path_len = 0;
+ pstrcpy(file_prefix, sizeof(file_prefix), input);
+ strcpy(path, ".");
+ } else {
+ input_path_len = p - input + 1;
+ memcpy(path, input, input_path_len);
+ if (input_path_len > sizeof(path) - 1)
+ input_path_len = sizeof(path) - 1;
+ path[input_path_len] = '\0';
+ pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
+ }
+#ifdef DEBUG_COMPLETION
+ term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix);
+#endif
+ ffs = opendir(path);
+ if (!ffs)
+ return;
+ for(;;) {
+ struct stat sb;
+ d = readdir(ffs);
+ if (!d)
+ break;
+ if (strstart(d->d_name, file_prefix, NULL)) {
+ memcpy(file, input, input_path_len);
+ strcpy(file + input_path_len, d->d_name);
+ /* stat the file to find out if it's a directory.
+ * In that case add a slash to speed up typing long paths
+ */
+ stat(file, &sb);
+ if(S_ISDIR(sb.st_mode))
+ strcat(file, "/");
+ add_completion(file);
+ }
+ }
+ closedir(ffs);
+}
+
+static void block_completion_it(void *opaque, const char *name)
+{
+ const char *input = opaque;
+
+ if (input[0] == '\0' ||
+ !strncmp(name, (char *)input, strlen(input))) {
+ add_completion(name);
+ }
+}
+
+/* NOTE: this parser is an approximate form of the real command parser */
+static void parse_cmdline(const char *cmdline,
+ int *pnb_args, char **args)
+{
+ const char *p;
+ int nb_args, ret;
+ char buf[1024];
+
+ p = cmdline;
+ nb_args = 0;
+ for(;;) {
+ while (isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ if (nb_args >= MAX_ARGS)
+ break;
+ ret = get_str(buf, sizeof(buf), &p);
+ args[nb_args] = qemu_strdup(buf);
+ nb_args++;
+ if (ret < 0)
+ break;
+ }
+ *pnb_args = nb_args;
+}
+
+void readline_find_completion(const char *cmdline)
+{
+ const char *cmdname;
+ char *args[MAX_ARGS];
+ int nb_args, i, len;
+ const char *ptype, *str;
+ term_cmd_t *cmd;
+ const KeyDef *key;
+
+ parse_cmdline(cmdline, &nb_args, args);
+#ifdef DEBUG_COMPLETION
+ for(i = 0; i < nb_args; i++) {
+ term_printf("arg%d = '%s'\n", i, (char *)args[i]);
+ }
+#endif
+
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
+ len = strlen(cmdline);
+ if (len > 0 && isspace(cmdline[len - 1])) {
+ if (nb_args >= MAX_ARGS)
+ return;
+ args[nb_args++] = qemu_strdup("");
+ }
+ if (nb_args <= 1) {
+ /* command completion */
+ if (nb_args == 0)
+ cmdname = "";
+ else
+ cmdname = args[0];
+ completion_index = strlen(cmdname);
+ for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+ cmd_completion(cmdname, cmd->name);
+ }
+ } else {
+ /* find the command */
+ for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[0], cmd->name))
+ goto found;
+ }
+ return;
+ found:
+ ptype = cmd->args_type;
+ for(i = 0; i < nb_args - 2; i++) {
+ if (*ptype != '\0') {
+ ptype++;
+ while (*ptype == '?')
+ ptype++;
+ }
+ }
+ str = args[nb_args - 1];
+ switch(*ptype) {
+ case 'F':
+ /* file completion */
+ completion_index = strlen(str);
+ file_completion(str);
+ break;
+ case 'B':
+ /* block device name completion */
+ completion_index = strlen(str);
+ bdrv_iterate(block_completion_it, (void *)str);
+ break;
+ case 's':
+ /* XXX: more generic ? */
+ if (!strcmp(cmd->name, "info")) {
+ completion_index = strlen(str);
+ for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+ cmd_completion(str, cmd->name);
+ }
+ } else if (!strcmp(cmd->name, "sendkey")) {
+ completion_index = strlen(str);
+ for(key = key_defs; key->name != NULL; key++) {
+ cmd_completion(str, key->name);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ for(i = 0; i < nb_args; i++)
+ qemu_free(args[i]);
+}
+
+static int term_can_read(void *opaque)
+{
+ return 128;
+}
+
+static void term_read(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+ for(i = 0; i < size; i++)
+ readline_handle_byte(buf[i]);
+}
+
+static void monitor_start_input(void);
+
+static void monitor_handle_command1(void *opaque, const char *cmdline)
+{
+ monitor_handle_command(cmdline);
+ monitor_start_input();
+}
+
+static void monitor_start_input(void)
+{
+ readline_start("(qemu) ", 0, monitor_handle_command1, NULL);
+}
+
+void monitor_init(CharDriverState *hd, int show_banner)
+{
+ monitor_hd = hd;
+ if (show_banner) {
+ term_printf("QEMU %s monitor - type 'help' for more information\n",
+ QEMU_VERSION);
+ }
+ qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL);
+ monitor_start_input();
+}
+
+/* XXX: use threads ? */
+/* modal monitor readline */
+static int monitor_readline_started;
+static char *monitor_readline_buf;
+static int monitor_readline_buf_size;
+
+static void monitor_readline_cb(void *opaque, const char *input)
+{
+ pstrcpy(monitor_readline_buf, monitor_readline_buf_size, input);
+ monitor_readline_started = 0;
+}
+
+void monitor_readline(const char *prompt, int is_password,
+ char *buf, int buf_size)
+{
+ if (is_password) {
+ qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS);
+ }
+ readline_start(prompt, is_password, monitor_readline_cb, NULL);
+ monitor_readline_buf = buf;
+ monitor_readline_buf_size = buf_size;
+ monitor_readline_started = 1;
+ while (monitor_readline_started) {
+ main_loop_wait(10);
+ }
+}
diff --git a/osdep.c b/osdep.c
new file mode 100644
index 0000000..ffa29fe
--- /dev/null
+++ b/osdep.c
@@ -0,0 +1,642 @@
+/*
+ * QEMU low level functions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "cpu.h"
+#if defined(USE_KQEMU)
+#include "vl.h"
+#endif
+
+#if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(CONFIG_USER_ONLY)
+
+#include <sys/mman.h>
+#include <sys/ipc.h>
+
+/* When not using soft mmu, libc independant functions are needed for
+ the CPU core because it needs to use alternates stacks and
+ libc/thread incompatibles settings */
+
+#include <linux/unistd.h>
+
+#define QEMU_SYSCALL0(name) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name)); \
+return __res; \
+}
+
+#define QEMU_SYSCALL1(name,arg1) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1))); \
+return __res; \
+}
+
+#define QEMU_SYSCALL2(name,arg1,arg2) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
+return __res; \
+}
+
+#define QEMU_SYSCALL3(name,arg1,arg2,arg3) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3))); \
+return __res; \
+}
+
+#define QEMU_SYSCALL4(name,arg1,arg2,arg3,arg4) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4))); \
+return __res; \
+}
+
+#define QEMU_SYSCALL5(name,arg1,arg2,arg3,arg4,arg5) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \
+return __res; \
+}
+
+#define QEMU_SYSCALL6(name,arg1,arg2,arg3,arg4,arg5,arg6) \
+{ \
+long __res; \
+__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \
+ : "=a" (__res) \
+ : "i" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \
+ "0" ((long)(arg6))); \
+return __res; \
+}
+
+int qemu_write(int fd, const void *buf, size_t n)
+{
+ QEMU_SYSCALL3(write, fd, buf, n);
+}
+
+
+
+/****************************************************************/
+/* shmat replacement */
+
+int qemu_ipc(int call, unsigned long first,
+ unsigned long second, unsigned long third,
+ void *ptr, unsigned long fifth)
+{
+ QEMU_SYSCALL6(ipc, call, first, second, third, ptr, fifth);
+}
+
+#define SHMAT 21
+
+/* we must define shmat so that a specific address will be used when
+ mapping the X11 ximage */
+void *shmat(int shmid, const void *shmaddr, int shmflg)
+{
+ void *ptr;
+ int ret;
+ /* we give an address in the right memory area */
+ if (!shmaddr)
+ shmaddr = get_mmap_addr(8192 * 1024);
+ ret = qemu_ipc(SHMAT, shmid, shmflg, (unsigned long)&ptr, (void *)shmaddr, 0);
+ if (ret < 0)
+ return NULL;
+ return ptr;
+}
+
+/****************************************************************/
+/* sigaction bypassing the threads */
+
+static int kernel_sigaction(int signum, const struct qemu_sigaction *act,
+ struct qemu_sigaction *oldact,
+ int sigsetsize)
+{
+ QEMU_SYSCALL4(rt_sigaction, signum, act, oldact, sigsetsize);
+}
+
+int qemu_sigaction(int signum, const struct qemu_sigaction *act,
+ struct qemu_sigaction *oldact)
+{
+ return kernel_sigaction(signum, act, oldact, 8);
+}
+
+/****************************************************************/
+/* memory allocation */
+
+//#define DEBUG_MALLOC
+
+#define MALLOC_BASE 0xab000000
+#define PHYS_RAM_BASE 0xac000000
+
+#define MALLOC_ALIGN 16
+#define BLOCK_HEADER_SIZE 16
+
+typedef struct MemoryBlock {
+ struct MemoryBlock *next;
+ unsigned long size; /* size of block, including header */
+} MemoryBlock;
+
+static MemoryBlock *first_free_block;
+static unsigned long malloc_addr = MALLOC_BASE;
+
+static void *malloc_get_space(size_t size)
+{
+ void *ptr;
+ size = TARGET_PAGE_ALIGN(size);
+ ptr = mmap((void *)malloc_addr, size,
+ PROT_WRITE | PROT_READ,
+ MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED)
+ return NULL;
+ malloc_addr += size;
+ return ptr;
+}
+
+void *qemu_malloc(size_t size)
+{
+ MemoryBlock *mb, *mb1, **pmb;
+ void *ptr;
+ size_t size1, area_size;
+
+ if (size == 0)
+ return NULL;
+
+ size = (size + BLOCK_HEADER_SIZE + MALLOC_ALIGN - 1) & ~(MALLOC_ALIGN - 1);
+ pmb = &first_free_block;
+ for(;;) {
+ mb = *pmb;
+ if (mb == NULL)
+ break;
+ if (size <= mb->size)
+ goto found;
+ pmb = &mb->next;
+ }
+ /* no big enough blocks found: get new space */
+ area_size = TARGET_PAGE_ALIGN(size);
+ mb = malloc_get_space(area_size);
+ if (!mb)
+ return NULL;
+ size1 = area_size - size;
+ if (size1 > 0) {
+ /* create a new free block */
+ mb1 = (MemoryBlock *)((uint8_t *)mb + size);
+ mb1->next = NULL;
+ mb1->size = size1;
+ *pmb = mb1;
+ }
+ goto the_end;
+ found:
+ /* a free block was found: use it */
+ size1 = mb->size - size;
+ if (size1 > 0) {
+ /* create a new free block */
+ mb1 = (MemoryBlock *)((uint8_t *)mb + size);
+ mb1->next = mb->next;
+ mb1->size = size1;
+ *pmb = mb1;
+ } else {
+ /* suppress the first block */
+ *pmb = mb->next;
+ }
+ the_end:
+ mb->size = size;
+ mb->next = NULL;
+ ptr = ((uint8_t *)mb + BLOCK_HEADER_SIZE);
+#ifdef DEBUG_MALLOC
+ qemu_printf("malloc: size=0x%x ptr=0x%lx\n", size, (unsigned long)ptr);
+#endif
+ return ptr;
+}
+
+void qemu_free(void *ptr)
+{
+ MemoryBlock *mb;
+
+ if (!ptr)
+ return;
+ mb = (MemoryBlock *)((uint8_t *)ptr - BLOCK_HEADER_SIZE);
+ mb->next = first_free_block;
+ first_free_block = mb;
+}
+
+/****************************************************************/
+/* virtual memory allocation */
+
+unsigned long mmap_addr = PHYS_RAM_BASE;
+
+void *get_mmap_addr(unsigned long size)
+{
+ unsigned long addr;
+ addr = mmap_addr;
+ mmap_addr += ((size + 4095) & ~4095) + 4096;
+ return (void *)addr;
+}
+
+#else
+
+#ifdef _WIN32
+#include <windows.h>
+#elif defined(_BSD)
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+
+int qemu_write(int fd, const void *buf, size_t n)
+{
+ int ret;
+ ret = write(fd, buf, n);
+ if (ret < 0)
+ return -errno;
+ else
+ return ret;
+}
+
+void *get_mmap_addr(unsigned long size)
+{
+ return NULL;
+}
+
+void qemu_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *qemu_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+#if defined(_WIN32)
+
+void *qemu_vmalloc(size_t size)
+{
+ /* FIXME: this is not exactly optimal solution since VirtualAlloc
+ has 64Kb granularity, but at least it guarantees us that the
+ memory is page aligned. */
+ return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+}
+
+void qemu_vfree(void *ptr)
+{
+ VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+#else
+
+#if defined(USE_KQEMU)
+
+#include <sys/vfs.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+void *kqemu_vmalloc(size_t size)
+{
+ static int phys_ram_fd = -1;
+ static int phys_ram_size = 0;
+ const char *tmpdir;
+ char phys_ram_file[1024];
+ void *ptr;
+ struct statfs stfs;
+
+ if (phys_ram_fd < 0) {
+ tmpdir = getenv("QEMU_TMPDIR");
+ if (!tmpdir)
+ tmpdir = "/dev/shm";
+ if (statfs(tmpdir, &stfs) == 0) {
+ int64_t free_space;
+ int ram_mb;
+
+ extern int ram_size;
+ free_space = (int64_t)stfs.f_bavail * stfs.f_bsize;
+ if ((ram_size + 8192 * 1024) >= free_space) {
+ ram_mb = (ram_size / (1024 * 1024));
+ fprintf(stderr,
+ "You do not have enough space in '%s' for the %d MB of QEMU virtual RAM.\n",
+ tmpdir, ram_mb);
+ if (strcmp(tmpdir, "/dev/shm") == 0) {
+ fprintf(stderr, "To have more space available provided you have enough RAM and swap, do as root:\n"
+ "umount /dev/shm\n"
+ "mount -t tmpfs -o size=%dm none /dev/shm\n",
+ ram_mb + 16);
+ } else {
+ fprintf(stderr,
+ "Use the '-m' option of QEMU to diminish the amount of virtual RAM or use the\n"
+ "QEMU_TMPDIR environment variable to set another directory where the QEMU\n"
+ "temporary RAM file will be opened.\n");
+ }
+ fprintf(stderr, "Or disable the accelerator module with -no-kqemu\n");
+ exit(1);
+ }
+ }
+ snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX",
+ tmpdir);
+ if (mkstemp(phys_ram_file) < 0) {
+ fprintf(stderr,
+ "warning: could not create temporary file in '%s'.\n"
+ "Use QEMU_TMPDIR to select a directory in a tmpfs filesystem.\n"
+ "Using '/tmp' as fallback.\n",
+ tmpdir);
+ snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX",
+ "/tmp");
+ if (mkstemp(phys_ram_file) < 0) {
+ fprintf(stderr, "Could not create temporary memory file '%s'\n",
+ phys_ram_file);
+ exit(1);
+ }
+ }
+ phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
+ if (phys_ram_fd < 0) {
+ fprintf(stderr, "Could not open temporary memory file '%s'\n",
+ phys_ram_file);
+ exit(1);
+ }
+ unlink(phys_ram_file);
+ }
+ size = (size + 4095) & ~4095;
+ ftruncate(phys_ram_fd, phys_ram_size + size);
+ ptr = mmap(NULL,
+ size,
+ PROT_WRITE | PROT_READ, MAP_SHARED,
+ phys_ram_fd, phys_ram_size);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "Could not map physical memory\n");
+ exit(1);
+ }
+ phys_ram_size += size;
+ return ptr;
+}
+
+void kqemu_vfree(void *ptr)
+{
+ /* may be useful some day, but currently we do not need to free */
+}
+
+#endif
+
+/* alloc shared memory pages */
+void *qemu_vmalloc(size_t size)
+{
+#if defined(USE_KQEMU)
+ if (kqemu_allowed)
+ return kqemu_vmalloc(size);
+#endif
+#ifdef _BSD
+ return valloc(size);
+#else
+ return memalign(4096, size);
+#endif
+}
+
+void qemu_vfree(void *ptr)
+{
+#if defined(USE_KQEMU)
+ if (kqemu_allowed)
+ kqemu_vfree(ptr);
+#endif
+ free(ptr);
+}
+
+#endif
+
+#endif
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ ptr = qemu_malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+char *qemu_strdup(const char *str)
+{
+ char *ptr;
+ ptr = qemu_malloc(strlen(str) + 1);
+ if (!ptr)
+ return NULL;
+ strcpy(ptr, str);
+ return ptr;
+}
+
+/****************************************************************/
+/* printf support */
+
+static inline int qemu_isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+/* from BSD ppp sources */
+int qemu_vsnprintf(char *buf, int buflen, const char *fmt, va_list args)
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg;
+ unsigned long val = 0;
+ const char *f;
+ char *str, *buf0;
+ char num[32];
+ static const char hexchars[] = "0123456789abcdef";
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = prec = 0;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (qemu_isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (qemu_isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ /* modifiers */
+ switch(c) {
+ case 'l':
+ c = *++fmt;
+ break;
+ default:
+ break;
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec > 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+void qemu_vprintf(const char *fmt, va_list ap)
+{
+ char buf[1024];
+ int len;
+
+ len = qemu_vsnprintf(buf, sizeof(buf), fmt, ap);
+ qemu_write(1, buf, len);
+}
+
+void qemu_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ qemu_vprintf(fmt, ap);
+ va_end(ap);
+}
+
diff --git a/osdep.h b/osdep.h
new file mode 100644
index 0000000..29a261c
--- /dev/null
+++ b/osdep.h
@@ -0,0 +1,55 @@
+#ifndef QEMU_OSDEP_H
+#define QEMU_OSDEP_H
+
+#include <stdarg.h>
+
+int qemu_vsnprintf(char *buf, int buflen, const char *fmt, va_list args);
+void qemu_vprintf(const char *fmt, va_list ap);
+void qemu_printf(const char *fmt, ...);
+
+void *qemu_malloc(size_t size);
+void *qemu_mallocz(size_t size);
+void qemu_free(void *ptr);
+char *qemu_strdup(const char *str);
+
+void *qemu_vmalloc(size_t size);
+void qemu_vfree(void *ptr);
+
+void *get_mmap_addr(unsigned long size);
+
+/* specific kludges for OS compatibility (should be moved elsewhere) */
+#if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(CONFIG_USER_ONLY)
+
+/* disabled pthread version of longjmp which prevent us from using an
+ alternative signal stack */
+extern void __longjmp(jmp_buf env, int val);
+#define longjmp __longjmp
+
+#include <signal.h>
+
+struct siginfo;
+
+/* NOTE: it works only because the glibc sigset_t is >= kernel sigset_t */
+struct qemu_sigaction {
+ union {
+ void (*_sa_handler)(int);
+ void (*_sa_sigaction)(int, struct siginfo *, void *);
+ } _u;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ sigset_t sa_mask; /* mask last for extensibility */
+};
+
+int qemu_sigaction(int signum, const struct qemu_sigaction *act,
+ struct qemu_sigaction *oldact);
+
+#undef sigaction
+#undef sa_handler
+#undef sa_sigaction
+#define sigaction qemu_sigaction
+#define sa_handler _u._sa_handler
+#define sa_sigaction _u._sa_sigaction
+
+#endif
+
+#endif
diff --git a/pc-bios/Makefile b/pc-bios/Makefile
new file mode 100644
index 0000000..7ae0ff0
--- /dev/null
+++ b/pc-bios/Makefile
@@ -0,0 +1,24 @@
+#
+# NOTE: only compilable with x86 cross compile tools
+#
+include ../config-host.mak
+
+DEFINES=
+
+TARGETS=
+ifeq ($(ARCH),i386)
+TARGETS+=linux_boot.bin
+endif
+
+all: $(TARGETS)
+
+linux_boot.bin: linux_boot.o
+ ld --oformat binary -Ttext 0 -o $@ $<
+ chmod a-x $@
+
+%.o: %.S
+ $(CC) $(DEFINES) -c -o $@ $<
+
+clean:
+ rm -f $(TARGETS) *.o *~
+
diff --git a/pc-bios/README b/pc-bios/README
new file mode 100644
index 0000000..fc85eb4
--- /dev/null
+++ b/pc-bios/README
@@ -0,0 +1,16 @@
+- The PC BIOS comes from the Bochs project
+ (http://bochs.sourceforge.net/). A patch from bios.diff was applied.
+
+- The VGA BIOS and the Cirrus VGA BIOS come from the LGPL VGA bios
+ project (http://www.nongnu.org/vgabios/).
+
+- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is
+ available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm.
+
+- video.x is a PowerMac NDRV compatible driver for a VGA frame
+ buffer. It comes from the Mac-on-Linux project
+ (http://www.maconlinux.org/).
+
+- OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable
+ firmware implementation. The goal is to implement a 100% IEEE
+ 1275-1994 (referred to as Open Firmware) compliant firmware.
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
new file mode 100644
index 0000000..64be517
--- /dev/null
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/bios.diff b/pc-bios/bios.diff
new file mode 100644
index 0000000..e875927
--- /dev/null
+++ b/pc-bios/bios.diff
@@ -0,0 +1,270 @@
+Index: apmbios.S
+===================================================================
+RCS file: /cvsroot/bochs/bochs/bios/apmbios.S,v
+retrieving revision 1.4
+diff -u -w -r1.4 apmbios.S
+--- apmbios.S 26 Dec 2005 10:35:51 -0000 1.4
++++ apmbios.S 3 May 2006 21:22:46 -0000
+@@ -225,6 +225,7 @@
+ APMSYM(05):
+ cmp al, #0x05
+ jne APMSYM(07)
++ sti
+ hlt
+ jmp APMSYM(ok)
+
+Index: rombios.c
+===================================================================
+RCS file: /cvsroot/bochs/bochs/bios/rombios.c,v
+retrieving revision 1.160
+diff -u -w -r1.160 rombios.c
+--- rombios.c 25 Jan 2006 17:51:49 -0000 1.160
++++ rombios.c 3 May 2006 21:22:48 -0000
+@@ -1816,6 +1816,7 @@
+ {
+ printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
+ BIOS_BUILD_DATE, bios_cvs_version_string);
++#if 0
+ printf(
+ #ifdef BX_APM
+ "apmbios "
+@@ -1827,6 +1828,9 @@
+ "eltorito "
+ #endif
+ "\n\n");
++#else
++ printf("apmbios pcibios eltorito \n\n");
++#endif
+ }
+
+ //--------------------------------------------------------------------------
+@@ -3999,6 +4003,29 @@
+ }
+ #endif
+
++
++void set_e820_range(ES, DI, start, end, type)
++ Bit16u ES;
++ Bit16u DI;
++ Bit32u start;
++ Bit32u end;
++ Bit16u type;
++{
++ write_word(ES, DI, start);
++ write_word(ES, DI+2, start >> 16);
++ write_word(ES, DI+4, 0x00);
++ write_word(ES, DI+6, 0x00);
++
++ end -= start;
++ write_word(ES, DI+8, end);
++ write_word(ES, DI+10, end >> 16);
++ write_word(ES, DI+12, 0x0000);
++ write_word(ES, DI+14, 0x0000);
++
++ write_word(ES, DI+16, type);
++ write_word(ES, DI+18, 0x0);
++}
++
+ void
+ int15_function32(regs, ES, DS, FLAGS)
+ pushad_regs_t regs; // REGS pushed via pushad
+@@ -4063,19 +4090,8 @@
+ switch(regs.u.r16.bx)
+ {
+ case 0:
+- write_word(ES, regs.u.r16.di, 0x00);
+- write_word(ES, regs.u.r16.di+2, 0x00);
+- write_word(ES, regs.u.r16.di+4, 0x00);
+- write_word(ES, regs.u.r16.di+6, 0x00);
+-
+- write_word(ES, regs.u.r16.di+8, 0xFC00);
+- write_word(ES, regs.u.r16.di+10, 0x0009);
+- write_word(ES, regs.u.r16.di+12, 0x0000);
+- write_word(ES, regs.u.r16.di+14, 0x0000);
+-
+- write_word(ES, regs.u.r16.di+16, 0x1);
+- write_word(ES, regs.u.r16.di+18, 0x0);
+-
++ set_e820_range(ES, regs.u.r16.di,
++ 0x0000000L, 0x0009fc00L, 1);
+ regs.u.r32.ebx = 1;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+@@ -4083,6 +4099,24 @@
+ return;
+ break;
+ case 1:
++ set_e820_range(ES, regs.u.r16.di,
++ 0x0009fc00L, 0x000a0000L, 2);
++ regs.u.r32.ebx = 2;
++ regs.u.r32.eax = 0x534D4150;
++ regs.u.r32.ecx = 0x14;
++ CLEAR_CF();
++ return;
++ break;
++ case 2:
++ set_e820_range(ES, regs.u.r16.di,
++ 0x000e8000L, 0x00100000L, 2);
++ regs.u.r32.ebx = 3;
++ regs.u.r32.eax = 0x534D4150;
++ regs.u.r32.ecx = 0x14;
++ CLEAR_CF();
++ return;
++ break;
++ case 3:
+ extended_memory_size = inb_cmos(0x35);
+ extended_memory_size <<= 8;
+ extended_memory_size |= inb_cmos(0x34);
+@@ -4092,9 +4126,9 @@
+ extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
+ }
+ extended_memory_size *= 1024;
+- extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
++ extended_memory_size += (16L * 1024 * 1024);
+
+- if(extended_memory_size <= 15728640)
++ if(extended_memory_size <= (16L * 1024 * 1024))
+ {
+ extended_memory_size = inb_cmos(0x31);
+ extended_memory_size <<= 8;
+@@ -4102,28 +4136,23 @@
+ extended_memory_size *= 1024;
+ }
+
+- write_word(ES, regs.u.r16.di, 0x0000);
+- write_word(ES, regs.u.r16.di+2, 0x0010);
+- write_word(ES, regs.u.r16.di+4, 0x0000);
+- write_word(ES, regs.u.r16.di+6, 0x0000);
+-
+- write_word(ES, regs.u.r16.di+8, extended_memory_size);
+- extended_memory_size >>= 16;
+- write_word(ES, regs.u.r16.di+10, extended_memory_size);
+- extended_memory_size >>= 16;
+- write_word(ES, regs.u.r16.di+12, extended_memory_size);
+- extended_memory_size >>= 16;
+- write_word(ES, regs.u.r16.di+14, extended_memory_size);
+-
+- write_word(ES, regs.u.r16.di+16, 0x1);
+- write_word(ES, regs.u.r16.di+18, 0x0);
+-
+- regs.u.r32.ebx = 0;
++ set_e820_range(ES, regs.u.r16.di,
++ 0x00100000L, extended_memory_size, 1);
++ regs.u.r32.ebx = 4;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
++ case 4:
++ /* 256KB BIOS area at the end of 4 GB */
++ set_e820_range(ES, regs.u.r16.di,
++ 0xfffc0000L, 0x00000000L, 2);
++ regs.u.r32.ebx = 0;
++ regs.u.r32.eax = 0x534D4150;
++ regs.u.r32.ecx = 0x14;
++ CLEAR_CF();
++ return;
+ default: /* AX=E820, DX=534D4150, BX unrecognized */
+ goto int15_unimplemented;
+ break;
+@@ -8713,6 +8742,7 @@
+ mov al, #0x80
+ bios32_end:
+ popf
++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu
+ retf
+
+ .align 16
+@@ -8823,17 +8853,17 @@
+ pci_pro_fail:
+ pop edi
+ pop esi
+- sti
+ popf
+ stc
++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu
+ retf
+ pci_pro_ok:
+ xor ah, ah
+ pop edi
+ pop esi
+- sti
+ popf
+ clc
++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu
+ retf
+
+ pci_pro_select_reg:
+@@ -8971,7 +9001,7 @@
+ jmp pci_real_ok
+ pci_real_f0d: ;; write configuration dword
+ cmp al, #0x0d
+- jne pci_real_unknown
++ jne pci_real_f0e
+ call pci_real_select_reg
+ push dx
+ mov dx, #0x0cfc
+@@ -8979,6 +9009,46 @@
+ out dx, eax
+ pop dx
+ jmp pci_real_ok
++pci_real_f0e: ;; get irq routing options
++ cmp al, #0x0e
++ jne pci_real_unknown
++ SEG ES
++ cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
++ jb pci_real_too_small
++ SEG ES
++ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
++ pushf
++ push ds
++ push es
++ push cx
++ push si
++ push di
++ cld
++ mov si, #pci_routing_table_structure_start
++ push cs
++ pop ds
++ SEG ES
++ mov cx, [di+2]
++ SEG ES
++ mov es, [di+4]
++ mov di, cx
++ mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
++ rep
++ movsb
++ pop di
++ pop si
++ pop cx
++ pop es
++ pop ds
++ popf
++ mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
++ jmp pci_real_ok
++pci_real_too_small:
++ SEG ES
++ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
++ mov ah, #0x89
++ jmp pci_real_fail
++
+ pci_real_unknown:
+ mov ah, #0x81
+ pci_real_fail:
+@@ -9019,6 +9089,7 @@
+ dw 0,0 ;; Miniport data
+ db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
+ db 0x07 ;; checksum
++pci_routing_table_structure_start:
+ ;; first slot entry PCI-to-ISA (embedded)
+ db 0 ;; pci bus number
+ db 0x08 ;; pci device number (bit 7-3)
+@@ -9097,6 +9168,7 @@
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 5 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
++pci_routing_table_structure_end:
+
+ pci_irq_list:
+ db 11, 10, 9, 5;
diff --git a/pc-bios/linux_boot.S b/pc-bios/linux_boot.S
new file mode 100644
index 0000000..22fcd4b
--- /dev/null
+++ b/pc-bios/linux_boot.S
@@ -0,0 +1,29 @@
+/*
+ * QEMU Boot sector to launch a preloaded Linux kernel
+ * Copyright (c) 2004 Fabrice Bellard
+ */
+
+#define LOAD_SEG 0x9000
+
+.code16
+.text
+ .globl _start
+
+_start:
+ cli
+ cld
+ mov $LOAD_SEG, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $0x8ffe, %sp
+ ljmp $LOAD_SEG + 0x20, $0
+
+1:
+ .fill 510 - (1b - _start), 1, 0
+
+ /* boot sector signature */
+ .byte 0x55
+ .byte 0xaa
diff --git a/pc-bios/linux_boot.bin b/pc-bios/linux_boot.bin
new file mode 100644
index 0000000..80f7b5f
--- /dev/null
+++ b/pc-bios/linux_boot.bin
Binary files differ
diff --git a/pc-bios/ohw.diff b/pc-bios/ohw.diff
new file mode 100644
index 0000000..4fb5422
--- /dev/null
+++ b/pc-bios/ohw.diff
@@ -0,0 +1,1843 @@
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bios.h OpenHackWare-release-0.4/src/bios.h
+--- OpenHackWare-release-0.4.org/src/bios.h 2005-04-06 23:20:22.000000000 +0200
++++ OpenHackWare-release-0.4/src/bios.h 2005-07-07 01:10:20.000000000 +0200
+@@ -64,6 +64,7 @@
+ ARCH_CHRP,
+ ARCH_MAC99,
+ ARCH_POP,
++ ARCH_HEATHROW,
+ };
+
+ /* Hardware definition(s) */
+@@ -174,6 +175,7 @@
+ int bd_ioctl (bloc_device_t *bd, int func, void *args);
+ uint32_t bd_seclen (bloc_device_t *bd);
+ void bd_close (bloc_device_t *bd);
++void bd_reset_all(void);
+ uint32_t bd_seclen (bloc_device_t *bd);
+ uint32_t bd_maxbloc (bloc_device_t *bd);
+ void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum,
+@@ -183,12 +185,12 @@
+ part_t *bd_probe (int boot_device);
+ bloc_device_t *bd_get (int device);
+ void bd_put (bloc_device_t *bd);
+-void bd_set_boot_part (bloc_device_t *bd, part_t *partition);
++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum);
+ part_t **_bd_parts (bloc_device_t *bd);
+
+ void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1,
+ uint32_t io_base2, uint32_t io_base3,
+- void *OF_private);
++ void *OF_private0, void *OF_private1);
+ void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1,
+ void *OF_private);
+
+@@ -399,17 +401,23 @@
+ uint16_t min_grant, uint16_t max_latency);
+ void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses);
+ void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn,
+- uint32_t *regions, uint32_t *sizes);
++ uint32_t *regions, uint32_t *sizes,
++ int irq_line);
+ void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size,
+ void *private_data);
++void OF_finalize_pci_ide (void *dev,
++ uint32_t io_base0, uint32_t io_base1,
++ uint32_t io_base2, uint32_t io_base3);
+ int OF_register_bus (const unsigned char *name, uint32_t address,
+ const unsigned char *type);
+ int OF_register_serial (const unsigned char *bus, const unsigned char *name,
+ uint32_t io_base, int irq);
+ int OF_register_stdio (const unsigned char *dev_in,
+ const unsigned char *dev_out);
+-void OF_vga_register (const unsigned char *name, uint32_t address,
+- int width, int height, int depth);
++void OF_vga_register (const unsigned char *name, unused uint32_t address,
++ int width, int height, int depth,
++ unsigned long vga_bios_addr,
++ unsigned long vga_bios_size);
+ void *OF_blockdev_register (void *parent, void *private,
+ const unsigned char *type,
+ const unsigned char *name, int devnum,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bloc.c OpenHackWare-release-0.4/src/bloc.c
+--- OpenHackWare-release-0.4.org/src/bloc.c 2005-04-06 23:21:00.000000000 +0200
++++ OpenHackWare-release-0.4/src/bloc.c 2005-07-08 00:28:26.000000000 +0200
+@@ -55,6 +55,7 @@
+ /* Partitions */
+ part_t *parts, *bparts;
+ part_t *boot_part;
++ int bpartnum;
+ /* Chain */
+ bloc_device_t *next;
+ };
+@@ -66,6 +67,7 @@
+
+ static int ide_initialize (bloc_device_t *bd, int device);
+ static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum);
++static int ide_reset (bloc_device_t *bd);
+
+ static int mem_initialize (bloc_device_t *bd, int device);
+ static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum);
+@@ -212,6 +214,17 @@
+ {
+ }
+
++void bd_reset_all(void)
++{
++ bloc_device_t *bd;
++ for (bd = bd_list; bd != NULL; bd = bd->next) {
++ if (bd->init == &ide_initialize) {
++ /* reset IDE drive because Darwin wants all IDE devices to be reset */
++ ide_reset(bd);
++ }
++ }
++}
++
+ uint32_t bd_seclen (bloc_device_t *bd)
+ {
+ return bd->seclen;
+@@ -223,10 +236,12 @@
+ }
+
+ /* XXX: to be suppressed */
+-void bd_set_boot_part (bloc_device_t *bd, part_t *partition)
++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum)
+ {
++ dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum);
+ if (bd->boot_part == NULL) {
+ bd->boot_part = partition;
++ bd->bpartnum = partnum;
+ }
+ }
+
+@@ -240,6 +255,13 @@
+ return &bd->bparts;
+ }
+
++void bd_set_boot_device (bloc_device_t *bd)
++{
++#if defined (USE_OPENFIRMWARE)
++ OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot");
++#endif
++}
++
+ part_t *bd_probe (int boot_device)
+ {
+ char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', };
+@@ -272,9 +294,7 @@
+ tmp = part_probe(bd, force_raw);
+ if (boot_device == bd->device) {
+ boot_part = tmp;
+-#if defined (USE_OPENFIRMWARE)
+- OF_blockdev_set_boot_device(bd->OF_private, 2, "\\\\ofwboot");
+-#endif
++ bd_set_boot_device(bd);
+ }
+ }
+
+@@ -717,34 +737,29 @@
+ /* IDE PCI access for pc */
+ static uint8_t ide_pci_port_read (bloc_device_t *bd, int port)
+ {
+- eieio();
+-
+- return *(uint8_t *)(bd->io_base + port);
++ uint8_t value;
++ value = inb(bd->io_base + port);
++ return value;
+ }
+
+ static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value)
+ {
+- *(uint8_t *)(bd->io_base + port) = value;
+- eieio();
++ outb(bd->io_base + port, value);
+ }
+
+ static uint32_t ide_pci_data_readl (bloc_device_t *bd)
+ {
+- eieio();
+-
+- return *((uint32_t *)bd->io_base);
++ return inl(bd->io_base);
+ }
+
+ static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val)
+ {
+- *(uint32_t *)(bd->io_base) = val;
+- eieio();
++ outl(bd->io_base, val);
+ }
+
+ static void ide_pci_control_write (bloc_device_t *bd, uint32_t val)
+ {
+- *((uint8_t *)bd->tmp) = val;
+- eieio();
++ outb(bd->tmp + 2, val);
+ }
+
+ static ide_ops_t ide_pci_pc_ops = {
+@@ -761,7 +776,7 @@
+
+ void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1,
+ uint32_t io_base2, uint32_t io_base3,
+- unused void *OF_private)
++ void *OF_private0, void *OF_private1)
+ {
+ if (ide_pci_ops == NULL) {
+ ide_pci_ops = malloc(sizeof(ide_ops_t));
+@@ -770,19 +785,19 @@
+ memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t));
+ }
+ if ((io_base0 != 0 || io_base1 != 0) &&
+- ide_pci_ops->base[0] == 0 && ide_pci_ops->base[1] == 0) {
++ ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) {
+ ide_pci_ops->base[0] = io_base0;
+- ide_pci_ops->base[1] = io_base1;
++ ide_pci_ops->base[2] = io_base1;
+ #ifdef USE_OPENFIRMWARE
+- ide_pci_ops->OF_private[0] = OF_private;
++ ide_pci_ops->OF_private[0] = OF_private0;
+ #endif
+ }
+ if ((io_base2 != 0 || io_base3 != 0) &&
+- ide_pci_ops->base[2] == 0 && ide_pci_ops->base[3] == 0) {
+- ide_pci_ops->base[2] = io_base2;
++ ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) {
++ ide_pci_ops->base[1] = io_base2;
+ ide_pci_ops->base[3] = io_base3;
+ #ifdef USE_OPENFIRMWARE
+- ide_pci_ops->OF_private[1] = OF_private;
++ ide_pci_ops->OF_private[1] = OF_private1;
+ #endif
+ }
+ }
+@@ -935,6 +950,8 @@
+ }
+
+ static void atapi_pad_req (void *buffer, int len);
++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer,
++ int maxlen);
+ static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum);
+
+ static int ide_initialize (bloc_device_t *bd, int device)
+@@ -1035,9 +1052,7 @@
+ DPRINTF("INQUIRY\n");
+ len = spc_inquiry_req(&atapi_buffer, 36);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, 36);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status);
+@@ -1053,9 +1068,7 @@
+ DPRINTF("READ_CAPACITY\n");
+ len = mmc_read_capacity_req(&atapi_buffer);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, 8);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status);
+@@ -1105,6 +1118,22 @@
+ memset(p + len, 0, 12 - len);
+ }
+
++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer,
++ int maxlen)
++{
++ int i;
++ /* select drive */
++ if (bd->drv == 0)
++ ide_port_write(bd, 0x06, 0x40);
++ else
++ ide_port_write(bd, 0x06, 0x50);
++ ide_port_write(bd, 0x04, maxlen & 0xff);
++ ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff);
++ ide_port_write(bd, 0x07, 0xA0);
++ for (i = 0; i < 3; i++)
++ ide_data_writel(bd, ldswap32(&buffer[i]));
++}
++
+ static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum)
+ {
+ uint32_t atapi_buffer[4];
+@@ -1112,16 +1141,9 @@
+ uint32_t status, value;
+ int i, len;
+
+- /* select drive */
+- if (bd->drv == 0)
+- ide_port_write(bd, 0x06, 0x40);
+- else
+- ide_port_write(bd, 0x06, 0x50);
+ len = mmc_read12_req(atapi_buffer, secnum, 1);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, bd->seclen);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI READ12 : status %0x != 0x48\n", status);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/apple.c OpenHackWare-release-0.4/src/libpart/apple.c
+--- OpenHackWare-release-0.4.org/src/libpart/apple.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/apple.c 2005-07-03 16:17:41.000000000 +0200
+@@ -199,14 +199,18 @@
+ if (len == 0) {
+ /* Place holder. Skip it */
+ DPRINTF("%s placeholder part\t%d\n", __func__, i);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Void", type, 32) == 0) {
+ /* Void partition. Skip it */
+ DPRINTF("%s Void part\t%d [%s]\n", __func__, i, type);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Free", type, 32) == 0) {
+ /* Free space. Skip it */
+ DPRINTF("%s Free part (%d)\n", __func__, i);
+ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_partition_map", type, 32) == 0 ||
+ strncmp("Apple_Partition_Map", type, 32) == 0
+ #if 0 // Is this really used or is it just a mistake ?
+@@ -226,7 +230,7 @@
+ */
+ }
+ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Driver", type, 32) == 0 ||
+ strncmp("Apple_Driver43", type, 32) == 0 ||
+ strncmp("Apple_Driver43_CD", type, 32) == 0 ||
+@@ -236,8 +240,12 @@
+ strncmp("Apple_Driver_IOKit", type, 32) == 0) {
+ /* Drivers. don't care for now */
+ DPRINTF("%s Drivers part\t%d [%s]\n", __func__, i, type);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DRIVER;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Patches", type, 32) == 0) {
+ /* Patches: don't care for now */
++ part->flags = PART_TYPE_APPLE | PART_FLAG_PATCH;
++ part_register(bd, part, name, i);
+ DPRINTF("%s Patches part\t%d [%s]\n", __func__, i, type);
+ } else if (strncmp("Apple_HFS", type, 32) == 0 ||
+ strncmp("Apple_MFS", type, 32) == 0 ||
+@@ -256,9 +264,8 @@
+ count = partmap->bloc_cnt * HFS_BLOCSIZE;
+ if (partmap->boot_size == 0 || partmap->boot_load == 0) {
+ printf("Not a bootable partition %d %d (%p %p)\n",
+- partmap->boot_size, partmap->boot_load,boot_part, part);
+- if (boot_part == NULL)
+- boot_part = part;
++ partmap->boot_size, partmap->boot_load,
++ boot_part, part);
+ part->flags = PART_TYPE_APPLE | PART_FLAG_FS;
+ } else {
+ part->boot_start.bloc = partmap->boot_start;
+@@ -278,8 +285,8 @@
+ boot_part = part;
+ part->flags = PART_TYPE_APPLE | PART_FLAG_FS | PART_FLAG_BOOT;
+ }
+- printf("Partition: %d %s st %0x size %0x",
+- i, name, partmap->start_bloc, partmap->bloc_cnt);
++ printf("Partition: %d '%s' '%s' st %0x size %0x",
++ i, name, type, partmap->start_bloc, partmap->bloc_cnt);
+ #ifndef DEBUG
+ printf("\n");
+ #endif
+@@ -290,11 +297,13 @@
+ part->boot_load, part->boot_entry);
+ DPRINTF(" load %0x entry %0x %0x\n",
+ partmap->boot_load2, partmap->boot_entry2, HFS_BLOCSIZE);
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else {
+ memcpy(tmp, type, 32);
+ tmp[32] = '\0';
+ ERROR("Unknown partition type [%s]\n", tmp);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ }
+ }
+ error:
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/core.c OpenHackWare-release-0.4/src/libpart/core.c
+--- OpenHackWare-release-0.4.org/src/libpart/core.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/core.c 2005-07-03 16:17:41.000000000 +0200
+@@ -126,7 +126,7 @@
+ }
+
+ int part_register (bloc_device_t *bd, part_t *partition,
+- const unsigned char *name)
++ const unsigned char *name, int partnum)
+ {
+ part_t **cur;
+
+@@ -134,6 +134,7 @@
+ partition->bd = bd;
+ partition->next = NULL;
+ partition->name = strdup(name);
++ partition->partnum = partnum;
+ for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next)
+ continue;
+ *cur = partition;
+@@ -141,29 +142,15 @@
+ return 0;
+ }
+
+-static inline int set_boot_part (bloc_device_t *bd, int partnum)
+-{
+- part_t *cur;
+-
+- cur = part_get(bd, partnum);
+- if (cur == NULL)
+- return -1;
+- bd_set_boot_part(bd, cur);
+-
+- return 0;
+-}
+-
+ part_t *part_get (bloc_device_t *bd, int partnum)
+ {
+ part_t **listp, *cur;
+- int i;
+
+ listp = _bd_parts(bd);
+- cur = *listp;
+- for (i = 0; i != partnum; i++) {
+- if (cur == NULL)
++
++ for (cur = *listp; cur != NULL; cur = cur->next) {
++ if (cur->partnum == partnum)
+ break;
+- cur = cur->next;
+ }
+
+ return cur;
+@@ -192,17 +179,20 @@
+ part_set_blocsize(bd, part, 512);
+ part->bd = bd;
+ part->flags = PART_TYPE_RAW | PART_FLAG_BOOT;
+- part_register(bd, part, "Raw");
++ part_register(bd, part, "Raw", 0);
+
+ return part;
+ }
+
++bloc_device_t *part_get_bd (part_t *part)
++{
++ return part->bd;
++}
++
+ part_t *part_probe (bloc_device_t *bd, int set_raw)
+ {
+- part_t *part0, *boot_part, **cur;
++ part_t *part0 = NULL, *boot_part, **cur;
+
+- /* Register the 0 partition: raw partition containing the whole disk */
+- part0 = part_get_raw(bd);
+ /* Try to find a valid boot partition */
+ boot_part = Apple_probe_partitions(bd);
+ if (boot_part == NULL) {
+@@ -210,10 +200,13 @@
+ if (boot_part == NULL && arch == ARCH_PREP)
+ boot_part = PREP_find_partition(bd);
+ if (boot_part == NULL && set_raw != 0) {
+- boot_part = part0;
+- set_boot_part(bd, 0);
++ dprintf("Use bloc device as raw partition\n");
+ }
+ }
++ if (_bd_parts(bd) == NULL) {
++ /* Register the 0 partition: raw partition containing the whole disk */
++ part0 = part_get_raw(bd);
++ }
+ /* Probe filesystem on each found partition */
+ for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) {
+ const unsigned char *map, *type;
+@@ -248,23 +241,28 @@
+ type = "unknown";
+ break;
+ }
+- DPRINTF("Probe filesystem on %s %s partition '%s' %s\n",
++ dprintf("Probe filesystem on %s %s partition '%s' %s %p\n",
+ type, map, (*cur)->name,
+- ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "");
++ ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "", *cur);
+ if (((*cur)->flags) & PART_FLAG_FS) {
+ if (((*cur)->flags) & PART_FLAG_BOOT)
+ (*cur)->fs = fs_probe(*cur, 1);
+ else
+ (*cur)->fs = fs_probe(*cur, 0);
++ } else if (((*cur)->flags) & PART_TYPE_RAW) {
++ (*cur)->fs = fs_probe(*cur, 2);
+ } else {
+ (*cur)->fs = fs_probe(*cur, 2);
+ }
+- if (((*cur)->flags) & PART_FLAG_BOOT) {
+- bd_set_boot_part(bd, *cur);
+ fs_get_bootfile((*cur)->fs);
++ if (((*cur)->flags) & PART_FLAG_BOOT) {
++ dprintf("Partition is bootable (%d)\n", (*cur)->partnum);
++ bd_set_boot_part(bd, *cur, (*cur)->partnum);
++ if (boot_part == NULL)
++ boot_part = *cur;
+ }
+ }
+- DPRINTF("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs,
++ dprintf("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs,
+ part_fs(boot_part), part0);
+
+ return boot_part;
+@@ -279,6 +277,7 @@
+ part->boot_size.offset = 0;
+ part->boot_load = 0;
+ part->boot_entry = 0;
++ part->flags |= PART_FLAG_BOOT;
+
+ return 0;
+ }
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/isofs.c OpenHackWare-release-0.4/src/libpart/isofs.c
+--- OpenHackWare-release-0.4.org/src/libpart/isofs.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/isofs.c 2005-07-03 16:17:41.000000000 +0200
+@@ -242,7 +242,7 @@
+ part->boot_start.bloc, part->boot_size.bloc,
+ part->boot_load, part->boot_entry);
+ part->flags = PART_TYPE_ISO9660 | PART_FLAG_BOOT;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i + 1);
+ fs_raw_set_bootfile(part, part->boot_start.bloc,
+ part->boot_start.offset,
+ part->boot_size.bloc,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/libpart.h OpenHackWare-release-0.4/src/libpart/libpart.h
+--- OpenHackWare-release-0.4.org/src/libpart/libpart.h 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/libpart.h 2005-07-03 16:17:41.000000000 +0200
+@@ -30,6 +30,7 @@
+
+ struct part_t {
+ bloc_device_t *bd;
++ int partnum;
+ uint32_t start; /* Partition first bloc */
+ uint32_t size; /* Partition size, in blocs */
+ uint32_t spb;
+@@ -54,7 +55,7 @@
+ };
+
+ int part_register (bloc_device_t *bd, part_t *partition,
+- const unsigned char *name);
++ const unsigned char *name, int partnum);
+ void part_set_blocsize (bloc_device_t *bd, part_t *part, uint32_t blocsize);
+ void part_private_set (part_t *part, void *private);
+ void *part_private_get (part_t *part);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/prep.c OpenHackWare-release-0.4/src/libpart/prep.c
+--- OpenHackWare-release-0.4.org/src/libpart/prep.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/prep.c 2005-07-03 16:17:41.000000000 +0200
+@@ -164,7 +164,7 @@
+ part->boot_load = 0;
+ part->boot_entry = boot_offset - part->bloc_size;
+ part->flags = PART_TYPE_PREP | PART_FLAG_BOOT;
+- part_register(bd, part, "PREP boot");
++ part_register(bd, part, "PREP boot", i);
+ fs_raw_set_bootfile(part, part->boot_start.bloc,
+ part->boot_start.offset,
+ part->boot_size.bloc,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/main.c OpenHackWare-release-0.4/src/main.c
+--- OpenHackWare-release-0.4.org/src/main.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/main.c 2005-06-07 23:48:39.000000000 +0200
+@@ -364,20 +364,24 @@
+ void *load_base, *load_entry, *last_alloc, *load_end;
+ uint32_t memsize, boot_image_size, cmdline_size, ramdisk_size;
+ uint32_t boot_base, boot_nb;
+- int boot_device;
++ int boot_device, i;
++ static const uint32_t isa_base_tab[3] = {
++ 0x80000000, /* PREP */
++ 0xFE000000, /* Grackle (Heathrow) */
++ 0xF2000000, /* UniNorth (Mac99) */
++ };
+
+ /* Retrieve NVRAM configuration */
+- nvram_retry:
++ for(i = 0; i < 3; i++) {
++ isa_io_base = isa_base_tab[i];
+ nvram = NVRAM_get_config(&memsize, &boot_device,
+ &boot_image, &boot_image_size,
+ &cmdline, &cmdline_size,
+ &ramdisk, &ramdisk_size);
+- if (nvram == NULL) {
+- /* Retry with another isa_io_base */
+- if (isa_io_base == 0x80000000) {
+- isa_io_base = 0xF2000000;
+- goto nvram_retry;
++ if (nvram)
++ break;
+ }
++ if (i == 3) {
+ ERROR("Unable to load configuration from NVRAM. Aborting...\n");
+ return -1;
+ }
+@@ -402,7 +406,7 @@
+ cpu_name = CPU_get_name(pvr);
+ OF_register_cpu(cpu_name, 0, pvr,
+ 200 * 1000 * 1000, 200 * 1000 * 1000,
+- 100 * 1000 * 1000, 10 * 1000 * 1000,
++ 100 * 1000 * 1000, 100 * 1000 * 1000,
+ 0x0092);
+ }
+ OF_register_memory(memsize, 512 * 1024 /* TOFIX */);
+@@ -433,9 +437,12 @@
+ vga_puts(copyright);
+ vga_puts("\n");
+
++#if 0
+ /* QEMU is quite incoherent: d is cdrom, not second drive */
++ /* XXX: should probe CD-ROM position */
+ if (boot_device == 'd')
+ boot_device = 'e';
++#endif
+ /* Open boot device */
+ boot_part = bd_probe(boot_device);
+ if (boot_device == 'm') {
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/nvram.c OpenHackWare-release-0.4/src/nvram.c
+--- OpenHackWare-release-0.4.org/src/nvram.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/nvram.c 2005-06-04 23:44:03.000000000 +0200
+@@ -334,6 +334,7 @@
+ ret = NVRAM_chrp_format(nvram);
+ break;
+ case ARCH_MAC99:
++ case ARCH_HEATHROW: /* XXX: may be incorrect */
+ ret = NVRAM_mac99_format(nvram);
+ break;
+ case ARCH_POP:
+@@ -409,13 +410,12 @@
+ arch = ARCH_MAC99;
+ } else if (strcmp(sign, "POP") == 0) {
+ arch = ARCH_POP;
++ } else if (strcmp(sign, "HEATHROW") == 0) {
++ arch = ARCH_HEATHROW;
+ } else {
+ ERROR("Unknown PPC architecture: '%s'\n", sign);
+ return NULL;
+ }
+- /* HACK */
+- if (arch == ARCH_CHRP)
+- arch = ARCH_MAC99;
+ lword = NVRAM_get_lword(nvram, 0x30);
+ *RAM_size = lword;
+ byte = NVRAM_get_byte(nvram, 0x34);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/of.c OpenHackWare-release-0.4/src/of.c
+--- OpenHackWare-release-0.4.org/src/of.c 2005-04-06 23:17:26.000000000 +0200
++++ OpenHackWare-release-0.4/src/of.c 2005-07-07 23:30:08.000000000 +0200
+@@ -489,7 +489,7 @@
+ ERROR("%s can't alloc new node '%s' name\n", __func__, name);
+ return NULL;
+ }
+- new->prop_address = OF_prop_int_new(env, new, "address", address);
++ new->prop_address = OF_prop_int_new(env, new, "unit-address", address);
+ if (new->prop_address == NULL) {
+ free(new->prop_name->value);
+ free(new->prop_name);
+@@ -1017,6 +1017,33 @@
+ string, strlen(string) + 1);
+ }
+
++/* convert '\1' char to '\0' */
++static OF_prop_t *OF_prop_string_new1 (OF_env_t *env, OF_node_t *node,
++ const unsigned char *name,
++ const unsigned char *string)
++{
++ int len, i;
++ OF_prop_t *ret;
++ unsigned char *str;
++
++ if (strchr(string, '\1') == NULL) {
++ return OF_prop_string_new(env, node, name, string);
++ } else {
++ len = strlen(string) + 1;
++ str = malloc(len);
++ if (!str)
++ return NULL;
++ memcpy(str, string, len);
++ for(i = 0; i < len; i++)
++ if (str[i] == '\1')
++ str[i] = '\0';
++ ret = OF_property_new(env, node, name,
++ str, len);
++ free(str);
++ return ret;
++ }
++}
++
+ __attribute__ (( section (".OpenFirmware") ))
+ static OF_prop_t *OF_prop_int_new (OF_env_t *env, OF_node_t *node,
+ const unsigned char *name, uint32_t value)
+@@ -1421,15 +1448,12 @@
+ __attribute__ (( section (".OpenFirmware") ))
+ int OF_init (void)
+ {
+- const unsigned char compat_str[] =
+ #if 0
+ "PowerMac3,1\0MacRISC\0Power Macintosh\0";
+ "PowerMac1,2\0MacRISC\0Power Macintosh\0";
+ "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0";
+ "AAPL,PowerMac3,0\0MacRISC\0Power Macintosh\0";
+ "AAPL,Gossamer\0MacRISC\0Power Macintosh\0";
+-#else
+- "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0";
+ #endif
+ OF_env_t *OF_env;
+ OF_node_t *als, *opt, *chs, *pks;
+@@ -1455,15 +1479,21 @@
+ return -1;
+ }
+ OF_prop_string_new(OF_env, OF_node_root, "device_type", "bootrom");
+-#if 0
+- OF_prop_string_new(OF_env, OF_node_root,
+- "model", "PPC Open Hack'Ware " BIOS_VERSION);
+-#else
++ if (arch == ARCH_HEATHROW) {
++ const unsigned char compat_str[] =
++ "PowerMac1,1\0MacRISC\0Power Macintosh";
++ OF_property_new(OF_env, OF_node_root, "compatible",
++ compat_str, sizeof(compat_str));
+ OF_prop_string_new(OF_env, OF_node_root,
+- "model", compat_str);
+-#endif
++ "model", "Power Macintosh");
++ } else {
++ const unsigned char compat_str[] =
++ "PowerMac3,1\0MacRISC\0Power Macintosh";
+ OF_property_new(OF_env, OF_node_root, "compatible",
+ compat_str, sizeof(compat_str));
++ OF_prop_string_new(OF_env, OF_node_root,
++ "model", "PowerMac3,1");
++ }
+ #if 0
+ OF_prop_string_new(OF_env, OF_node_root, "copyright", copyright);
+ #else
+@@ -1561,14 +1591,15 @@
+ range.size = 0x00800000;
+ OF_property_new(OF_env, rom, "ranges", &range, sizeof(OF_range_t));
+ OF_prop_int_new(OF_env, rom, "#address-cells", 1);
++
+ /* "/rom/boot-rom@fff00000" node */
+- brom = OF_node_new(OF_env, OF_node_root, "boot-rom", 0xfff00000);
++ brom = OF_node_new(OF_env, rom, "boot-rom", 0xfff00000);
+ if (brom == NULL) {
+ ERROR("Cannot create 'boot-rom'\n");
+ return -1;
+ }
+ regs.address = 0xFFF00000;
+- regs.size = 0x00010000;
++ regs.size = 0x00100000;
+ OF_property_new(OF_env, brom, "reg", &regs, sizeof(OF_regprop_t));
+ OF_prop_string_new(OF_env, brom, "write-characteristic", "flash");
+ OF_prop_string_new(OF_env, brom, "BootROM-build-date",
+@@ -1577,7 +1608,7 @@
+ OF_prop_string_new(OF_env, brom, "copyright", copyright);
+ OF_prop_string_new(OF_env, brom, "model", BIOS_str);
+ OF_prop_int_new(OF_env, brom, "result", 0);
+-#if 0
++#if 1
+ {
+ /* Hack taken 'as-is' from PearPC */
+ unsigned char info[] = {
+@@ -1596,7 +1627,9 @@
+ OF_node_put(OF_env, brom);
+ OF_node_put(OF_env, rom);
+ }
++#if 0
+ /* From here, hardcoded hacks to get a Mac-like machine */
++ /* XXX: Core99 does not seem to like this NVRAM tree */
+ /* "/nvram@fff04000" node */
+ {
+ OF_regprop_t regs;
+@@ -1617,6 +1650,7 @@
+ OF_prop_int_new(OF_env, chs, "nvram", OF_pack_handle(OF_env, nvr));
+ OF_node_put(OF_env, nvr);
+ }
++#endif
+ /* "/pseudo-hid" : hid emulation as Apple does */
+ {
+ OF_node_t *hid;
+@@ -1663,7 +1697,27 @@
+ }
+ OF_node_put(OF_env, hid);
+ }
++ if (arch == ARCH_MAC99) {
++ OF_node_t *unin;
++ OF_regprop_t regs;
+
++ unin = OF_node_new(OF_env, OF_node_root,
++ "uni-n", 0xf8000000);
++ if (unin == NULL) {
++ ERROR("Cannot create 'uni-n'\n");
++ return -1;
++ }
++ OF_prop_string_new(OF_env, unin, "device-type", "memory-controller");
++ OF_prop_string_new(OF_env, unin, "model", "AAPL,UniNorth");
++ OF_prop_string_new(OF_env, unin, "compatible", "uni-north");
++ regs.address = 0xf8000000;
++ regs.size = 0x01000000;
++ OF_property_new(OF_env, unin, "reg", &regs, sizeof(regs));
++ OF_prop_int_new(OF_env, unin, "#address-cells", 1);
++ OF_prop_int_new(OF_env, unin, "#size-cells", 1);
++ OF_prop_int_new(OF_env, unin, "device-rev", 3);
++ OF_node_put(OF_env, unin);
++ }
+
+ #if 1 /* This is mandatory for claim to work
+ * but I don't know where it should really be (in cpu ?)
+@@ -1693,7 +1747,9 @@
+
+ /* "/options/boot-args" node */
+ {
+- const unsigned char *args = "-v rootdev cdrom";
++ // const unsigned char *args = "-v rootdev cdrom";
++ //const unsigned char *args = "-v io=0xffffffff";
++ const unsigned char *args = "-v";
+ /* Ask MacOS X to print debug messages */
+ // OF_prop_string_new(OF_env, chs, "machargs", args);
+ // OF_prop_string_new(OF_env, opt, "boot-command", args);
+@@ -2013,17 +2069,17 @@
+ OF_prop_int_new(OF_env, node, "min-grant", min_grant);
+ OF_prop_int_new(OF_env, node, "max-latency", max_latency);
+ if (dev->type != NULL)
+- OF_prop_string_new(OF_env, node, "device_type", dev->type);
++ OF_prop_string_new1(OF_env, node, "device_type", dev->type);
+ if (dev->compat != NULL)
+- OF_prop_string_new(OF_env, node, "compatible", dev->compat);
++ OF_prop_string_new1(OF_env, node, "compatible", dev->compat);
+ if (dev->model != NULL)
+- OF_prop_string_new(OF_env, node, "model", dev->model);
++ OF_prop_string_new1(OF_env, node, "model", dev->model);
+ if (dev->acells != 0)
+ OF_prop_int_new(OF_env, node, "#address-cells", dev->acells);
+ if (dev->scells != 0)
+- OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->acells);
++ OF_prop_int_new(OF_env, node, "#size-cells", dev->scells);
+ if (dev->icells != 0)
+- OF_prop_int_new(OF_env, node, "#size-cells", dev->acells);
++ OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->icells);
+ dprintf("Done %p %p\n", parent, node);
+
+ return node;
+@@ -2040,8 +2096,9 @@
+ OF_env_t *OF_env;
+ pci_range_t ranges[3];
+ OF_regprop_t regs[1];
+- OF_node_t *pci_host;
++ OF_node_t *pci_host, *als;
+ int nranges;
++ unsigned char buffer[OF_NAMELEN_MAX];
+
+ OF_env = OF_env_main;
+ dprintf("register PCI host '%s' '%s' '%s' '%s'\n",
+@@ -2052,6 +2109,17 @@
+ ERROR("Cannot create pci host\n");
+ return NULL;
+ }
++
++ als = OF_node_get(OF_env, "aliases");
++ if (als == NULL) {
++ ERROR("Cannot get 'aliases'\n");
++ return NULL;
++ }
++ sprintf(buffer, "/%s", dev->name);
++ OF_prop_string_set(OF_env, als, "pci", buffer);
++ OF_node_put(OF_env, als);
++
++
+ regs[0].address = cfg_base;
+ regs[0].size = cfg_len;
+ OF_property_new(OF_env, pci_host, "reg", regs, sizeof(OF_regprop_t));
+@@ -2136,6 +2204,11 @@
+ return pci_dev;
+ }
+
++/* XXX: suppress that, used for interrupt map init */
++OF_node_t *pci_host_node;
++uint32_t pci_host_interrupt_map[7 * 32];
++int pci_host_interrupt_map_len = 0;
++
+ void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses)
+ {
+ OF_env_t *OF_env;
+@@ -2145,10 +2218,12 @@
+ regs[0].address = first_bus;
+ regs[0].size = nb_busses;
+ OF_property_new(OF_env, dev, "bus-range", regs, sizeof(OF_regprop_t));
++ pci_host_node = dev;
+ }
+
+ void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn,
+- uint32_t *regions, uint32_t *sizes)
++ uint32_t *regions, uint32_t *sizes,
++ int irq_line)
+ {
+ OF_env_t *OF_env;
+ pci_reg_prop_t pregs[6], rregs[6];
+@@ -2156,6 +2231,7 @@
+ int i, j, k;
+
+ OF_env = OF_env_main;
++ /* XXX: only useful for VGA card in fact */
+ if (regions[0] != 0x00000000)
+ OF_prop_int_set(OF_env, dev, "address", regions[0] & ~0x0000000F);
+ for (i = 0, j = 0, k = 0; i < 6; i++) {
+@@ -2222,7 +2298,22 @@
+ } else {
+ OF_property_new(OF_env, dev, "assigned-addresses", NULL, 0);
+ }
+-#if 0
++ if (irq_line >= 0) {
++ int i;
++ OF_prop_int_new(OF_env, dev, "interrupts", 1);
++ i = pci_host_interrupt_map_len;
++ pci_host_interrupt_map[i++] = (devfn << 8) & 0xf800;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0; /* pic handle will be patched later */
++ pci_host_interrupt_map[i++] = irq_line;
++ if (arch != ARCH_HEATHROW) {
++ pci_host_interrupt_map[i++] = 1;
++ }
++ pci_host_interrupt_map_len = i;
++ }
++#if 1
+ {
+ OF_prop_t *prop_name = ((OF_node_t *)dev)->prop_name;
+
+@@ -2390,6 +2481,54 @@
+ return 0;
+ }
+
++static void keylargo_ata(OF_node_t *mio, uint32_t base_address,
++ uint32_t base, int irq1, int irq2,
++ uint16_t pic_phandle)
++{
++ OF_env_t *OF_env = OF_env_main;
++ OF_node_t *ata;
++ OF_regprop_t regs[2];
++
++ ata = OF_node_new(OF_env, mio, "ata-4", base);
++ if (ata == NULL) {
++ ERROR("Cannot create 'ata-4'\n");
++ return;
++ }
++ OF_prop_string_new(OF_env, ata, "device_type", "ata");
++#if 1
++ OF_prop_string_new(OF_env, ata, "compatible", "key2largo-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++ OF_prop_string_new(OF_env, ata, "cable-type", "80-conductor");
++#else
++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++#endif
++ OF_prop_int_new(OF_env, ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, ata, "#size-cells", 0);
++ regs[0].address = base;
++ regs[0].size = 0x00001000;
++#if 0 // HACK: Don't set up DMA registers
++ regs[1].address = 0x00008A00;
++ regs[1].size = 0x00001000;
++ OF_property_new(OF_env, ata, "reg",
++ regs, 2 * sizeof(OF_regprop_t));
++#else
++ OF_property_new(OF_env, ata, "reg",
++ regs, sizeof(OF_regprop_t));
++#endif
++ OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
++ regs[0].address = irq1;
++ regs[0].size = 0x00000001;
++ regs[1].address = irq2;
++ regs[1].size = 0x00000000;
++ OF_property_new(OF_env, ata, "interrupts",
++ regs, 2 * sizeof(OF_regprop_t));
++ if (base == 0x1f000)
++ ide_pci_pmac_register(base_address + base, 0x00000000, ata);
++ else
++ ide_pci_pmac_register(0x00000000, base_address + base, ata);
++}
++
+ void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size,
+ void *private_data)
+ {
+@@ -2398,6 +2537,8 @@
+ pci_reg_prop_t pregs[2];
+ OF_node_t *mio, *chs, *als;
+ uint16_t pic_phandle;
++ int rec_len;
++ OF_prop_t *mio_reg;
+
+ OF_DPRINTF("mac-io: %p\n", dev);
+ OF_env = OF_env_main;
+@@ -2416,10 +2557,14 @@
+ mio = dev;
+ mio->private_data = private_data;
+ pregs[0].addr.hi = 0x00000000;
+- pregs[0].addr.mid = 0x82013810;
++ pregs[0].addr.mid = 0x00000000;
+ pregs[0].addr.lo = 0x00000000;
+ pregs[0].size_hi = base_address;
+ pregs[0].size_lo = size;
++ mio_reg = OF_property_get(OF_env, mio, "reg");
++ if (mio_reg && mio_reg->vlen >= 5 * 4) {
++ pregs[0].addr.mid = ((pci_reg_prop_t *)mio_reg->value)->addr.hi;
++ }
+ OF_property_new(OF_env, mio, "ranges",
+ &pregs, sizeof(pci_reg_prop_t));
+ #if 0
+@@ -2431,8 +2576,32 @@
+ OF_property_new(OF_env, mio, "assigned-addresses",
+ &pregs, sizeof(pci_reg_prop_t));
+ #endif
++
++ if (arch == ARCH_HEATHROW) {
++ /* Heathrow PIC */
++ OF_regprop_t regs;
++ OF_node_t *mpic;
++ const char compat_str[] = "heathrow\0mac-risc";
++
++ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x10);
++ if (mpic == NULL) {
++ ERROR("Cannot create 'mpic'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, mpic, "device_type", "interrupt-controller");
++ OF_property_new(OF_env, mpic, "compatible", compat_str, sizeof(compat_str));
++ OF_prop_int_new(OF_env, mpic, "#interrupt-cells", 1);
++ regs.address = 0x10;
++ regs.size = 0x20;
++ OF_property_new(OF_env, mpic, "reg",
++ &regs, sizeof(regs));
++ OF_property_new(OF_env, mpic, "interrupt-controller", NULL, 0);
++ pic_phandle = OF_pack_handle(OF_env, mpic);
++ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle);
++ OF_node_put(OF_env, mpic);
++ rec_len = 6;
++ } else {
+ /* OpenPIC */
+- {
+ OF_regprop_t regs[4];
+ OF_node_t *mpic;
+ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x40000);
+@@ -2455,8 +2624,37 @@
+ pic_phandle = OF_pack_handle(OF_env, mpic);
+ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle);
+ OF_node_put(OF_env, mpic);
++ rec_len = 7;
+ }
+-#if 1
++
++ /* patch pci host table */
++ /* XXX: do it after the PCI init */
++ {
++ int i;
++ uint32_t tab[4];
++
++ for(i = 0; i < pci_host_interrupt_map_len; i += rec_len)
++ pci_host_interrupt_map[i + 4] = pic_phandle;
++#if 0
++ dprintf("interrupt-map:\n");
++ for(i = 0; i < pci_host_interrupt_map_len; i++) {
++ dprintf(" %08x", pci_host_interrupt_map[i]);
++ if ((i % rec_len) == (rec_len - 1))
++ dprintf("\n");
++ }
++ dprintf("\n");
++#endif
++ OF_property_new(OF_env, pci_host_node, "interrupt-map",
++ pci_host_interrupt_map,
++ pci_host_interrupt_map_len * sizeof(uint32_t));
++ tab[0] = 0xf800;
++ tab[1] = 0;
++ tab[2] = 0;
++ tab[3] = 0;
++ OF_property_new(OF_env, pci_host_node, "interrupt-map-mask",
++ tab, 4 * sizeof(uint32_t));
++ }
++#if 0
+ /* escc is usefull to get MacOS X debug messages */
+ {
+ OF_regprop_t regs[8];
+@@ -2645,85 +2843,12 @@
+ OF_node_put(OF_env, scc);
+ }
+ #endif
+- /* IDE controller */
+- {
+- OF_node_t *ata;
+- OF_regprop_t regs[2];
+- ata = OF_node_new(OF_env, mio, "ata-4", 0x1f000);
+- if (ata == NULL) {
+- ERROR("Cannot create 'ata-4'\n");
+- goto out;
+- }
+- OF_prop_string_new(OF_env, ata, "device_type", "ata");
+-#if 1
+- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#else
+- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#endif
+- OF_prop_int_new(OF_env, ata, "#address-cells", 1);
+- OF_prop_int_new(OF_env, ata, "#size-cells", 0);
+- regs[0].address = 0x0001F000;
+- regs[0].size = 0x00001000;
+-#if 0 // HACK: Don't set up DMA registers
+- regs[1].address = 0x00008A00;
+- regs[1].size = 0x00001000;
+- OF_property_new(OF_env, ata, "reg",
+- regs, 2 * sizeof(OF_regprop_t));
+-#else
+- OF_property_new(OF_env, ata, "reg",
+- regs, sizeof(OF_regprop_t));
+-#endif
+- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
+- regs[0].address = 0x00000013;
+- regs[0].size = 0x00000001;
+- regs[1].address = 0x0000000B;
+- regs[1].size = 0x00000000;
+- OF_property_new(OF_env, ata, "interrupts",
+- regs, 2 * sizeof(OF_regprop_t));
+- ide_pci_pmac_register(base_address + 0x1f000, 0x00000000, ata);
+-
+- }
+- {
+- OF_node_t *ata;
+- OF_regprop_t regs[2];
+- ata = OF_node_new(OF_env, mio, "ata-4", 0x20000);
+- if (ata == NULL) {
+- ERROR("Cannot create 'ata-4'\n");
+- goto out;
+- }
+- OF_prop_string_new(OF_env, ata, "device_type", "ata");
+-#if 1
+- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#else
+- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#endif
+- OF_prop_int_new(OF_env, ata, "#address-cells", 1);
+- OF_prop_int_new(OF_env, ata, "#size-cells", 0);
+- regs[0].address = 0x00020000;
+- regs[0].size = 0x00001000;
+-#if 0 // HACK: Don't set up DMA registers
+- regs[1].address = 0x00008A00;
+- regs[1].size = 0x00001000;
+- OF_property_new(OF_env, ata, "reg",
+- regs, 2 * sizeof(OF_regprop_t));
+-#else
+- OF_property_new(OF_env, ata, "reg",
+- regs, sizeof(OF_regprop_t));
+-#endif
+- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
+- regs[0].address = 0x00000014;
+- regs[0].size = 0x00000001;
+- regs[1].address = 0x0000000B;
+- regs[1].size = 0x00000000;
+- OF_property_new(OF_env, ata, "interrupts",
+- regs, 2 * sizeof(OF_regprop_t));
+- ide_pci_pmac_register(0x00000000, base_address + 0x20000, ata);
+-
++ /* Keylargo IDE controller: need some work (DMA problem ?) */
++ if (arch == ARCH_MAC99) {
++ keylargo_ata(mio, base_address, 0x1f000, 0x13, 0xb, pic_phandle);
++ keylargo_ata(mio, base_address, 0x20000, 0x14, 0xb, pic_phandle);
+ }
++#if 0
+ /* Timer */
+ {
+ OF_node_t *tmr;
+@@ -2746,10 +2871,11 @@
+ regs, sizeof(OF_regprop_t));
+ OF_node_put(OF_env, tmr);
+ }
++#endif
+ /* VIA-PMU */
+ {
+ /* Controls adb, RTC and power-mgt (forget it !) */
+- OF_node_t *via, *adb, *rtc;
++ OF_node_t *via, *adb;
+ OF_regprop_t regs[1];
+ #if 0 // THIS IS A HACK AND IS COMPLETELY ABSURD !
+ // (but needed has Qemu doesn't emulate via-pmu).
+@@ -2773,14 +2899,21 @@
+ regs[0].size = 0x00002000;
+ OF_property_new(OF_env, via, "reg", regs, sizeof(OF_regprop_t));
+ OF_prop_int_new(OF_env, via, "interrupt-parent", pic_phandle);
++ if (arch == ARCH_HEATHROW) {
++ OF_prop_int_new(OF_env, via, "interrupts", 0x12);
++ } else {
+ regs[0].address = 0x00000019;
+ regs[0].size = 0x00000001;
+ OF_property_new(OF_env, via, "interrupts",
+ regs, sizeof(OF_regprop_t));
++ }
++ /* force usage of OF bus speeds */
++ OF_prop_int_new(OF_env, via, "BusSpeedCorrect", 1);
+ #if 0
+ OF_prop_int_new(OF_env, via, "pmu-version", 0x00D0740C);
+ #endif
+-#if 1
++ {
++ OF_node_t *kbd, *mouse;
+ /* ADB pseudo-device */
+ adb = OF_node_new(OF_env, via, "adb", OF_ADDRESS_NONE);
+ if (adb == NULL) {
+@@ -2797,9 +2930,26 @@
+ OF_prop_int_new(OF_env, adb, "#size-cells", 0);
+ OF_pack_get_path(OF_env, tmp, 512, adb);
+ OF_prop_string_new(OF_env, als, "adb", tmp);
+- /* XXX: add "keyboard@2" and "mouse@3" */
+- OF_node_put(OF_env, adb);
+-#endif
++
++ kbd = OF_node_new(OF_env, adb, "keyboard", 2);
++ if (kbd == NULL) {
++ ERROR("Cannot create 'kbd'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, kbd, "device_type", "keyboard");
++ OF_prop_int_new(OF_env, kbd, "reg", 2);
++
++ mouse = OF_node_new(OF_env, adb, "mouse", 3);
++ if (mouse == NULL) {
++ ERROR("Cannot create 'mouse'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, mouse, "device_type", "mouse");
++ OF_prop_int_new(OF_env, mouse, "reg", 3);
++ OF_prop_int_new(OF_env, mouse, "#buttons", 3);
++ }
++ {
++ OF_node_t *rtc;
+
+ rtc = OF_node_new(OF_env, via, "rtc", OF_ADDRESS_NONE);
+ if (rtc == NULL) {
+@@ -2813,14 +2963,68 @@
+ OF_prop_string_new(OF_env, rtc, "compatible", "rtc");
+ #endif
+ OF_node_put(OF_env, rtc);
+- OF_node_put(OF_env, via);
+ }
++ // OF_node_put(OF_env, via);
++ }
++ {
++ OF_node_t *pmgt;
++ pmgt = OF_node_new(OF_env, mio, "power-mgt", OF_ADDRESS_NONE);
++ OF_prop_string_new(OF_env, pmgt, "device_type", "power-mgt");
++ OF_prop_string_new(OF_env, pmgt, "compatible", "cuda");
++ OF_prop_string_new(OF_env, pmgt, "mgt-kind", "min-consumption-pwm-led");
++ OF_node_put(OF_env, pmgt);
++ }
++
++ if (arch == ARCH_HEATHROW) {
++ /* NVRAM */
++ OF_node_t *nvr;
++ OF_regprop_t regs;
++ nvr = OF_node_new(OF_env, mio, "nvram", 0x60000);
++ OF_prop_string_new(OF_env, nvr, "device_type", "nvram");
++ regs.address = 0x60000;
++ regs.size = 0x00020000;
++ OF_property_new(OF_env, nvr, "reg", &regs, sizeof(regs));
++ OF_prop_int_new(OF_env, nvr, "#bytes", 0x2000);
++ OF_node_put(OF_env, nvr);
++ }
++
+ out:
+ // OF_node_put(OF_env, mio);
+ OF_node_put(OF_env, chs);
+ OF_node_put(OF_env, als);
+ }
+
++void OF_finalize_pci_ide (void *dev,
++ uint32_t io_base0, uint32_t io_base1,
++ uint32_t io_base2, uint32_t io_base3)
++{
++ OF_env_t *OF_env = OF_env_main;
++ OF_node_t *pci_ata = dev;
++ OF_node_t *ata, *atas[2];
++ int i;
++
++ OF_prop_int_new(OF_env, pci_ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, pci_ata, "#size-cells", 0);
++
++ /* XXX: Darwin handles only one device */
++ for(i = 0; i < 1; i++) {
++ ata = OF_node_new(OF_env, pci_ata, "ata-4", i);
++ if (ata == NULL) {
++ ERROR("Cannot create 'ata-4'\n");
++ return;
++ }
++ OF_prop_string_new(OF_env, ata, "device_type", "ata");
++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++ OF_prop_int_new(OF_env, ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, ata, "#size-cells", 0);
++ OF_prop_int_new(OF_env, ata, "reg", i);
++ atas[i] = ata;
++ }
++ ide_pci_pc_register(io_base0, io_base1, io_base2, io_base3,
++ atas[0], atas[1]);
++}
++
+ /*****************************************************************************/
+ /* Fake package */
+ static void OF_method_fake (OF_env_t *OF_env)
+@@ -2862,11 +3066,11 @@
+ /* As we get a 1:1 mapping, do nothing */
+ ihandle = popd(OF_env);
+ args = (void *)popd(OF_env);
+- address = popd(OF_env);
+- virt = popd(OF_env);
+- size = popd(OF_env);
+ popd(OF_env);
+- OF_DPRINTF("Translate address %0x %0x %0x %0x\n", ihandle, address,
++ size = popd(OF_env);
++ virt = popd(OF_env);
++ address = popd(OF_env);
++ OF_DPRINTF("Map %0x %0x %0x %0x\n", ihandle, address,
+ virt, size);
+ pushd(OF_env, 0);
+ }
+@@ -3270,7 +3474,7 @@
+ OF_prop_string_new(OF_env, dsk, "device_type", "block");
+ OF_prop_string_new(OF_env, dsk, "category", type);
+ OF_prop_int_new(OF_env, dsk, "device_id", devnum);
+- OF_prop_int_new(OF_env, dsk, "reg", 0);
++ OF_prop_int_new(OF_env, dsk, "reg", devnum);
+ OF_method_new(OF_env, dsk, "open", &OF_blockdev_open);
+ OF_method_new(OF_env, dsk, "seek", &OF_blockdev_seek);
+ OF_method_new(OF_env, dsk, "read", &OF_blockdev_read);
+@@ -3432,7 +3636,8 @@
+ }
+
+ void OF_vga_register (const unsigned char *name, unused uint32_t address,
+- int width, int height, int depth)
++ int width, int height, int depth,
++ unsigned long vga_bios_addr, unsigned long vga_bios_size)
+ {
+ OF_env_t *OF_env;
+ unsigned char tmp[OF_NAMELEN_MAX];
+@@ -3504,6 +3709,18 @@
+ OF_prop_string_new(OF_env, als, "display", tmp);
+ OF_node_put(OF_env, als);
+ /* XXX: may also need read-rectangle */
++
++ if (vga_bios_size >= 8) {
++ const uint8_t *p;
++ int size;
++ /* check the QEMU VGA BIOS header */
++ p = (const uint8_t *)vga_bios_addr;
++ if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') {
++ size = *(uint32_t *)(p + 4);
++ OF_property_new(OF_env, disp, "driver,AAPL,MacOS,PowerPC",
++ p + 8, size);
++ }
++ }
+ out:
+ OF_node_put(OF_env, disp);
+ }
+@@ -4451,7 +4668,10 @@
+ break;
+ case 0x233441d3: /* MacOS X 10.2 and OpenDarwin 1.41 */
+ /* Create "memory-map" pseudo device */
+- popd(OF_env);
++ {
++ OF_node_t *map;
++ uint32_t phandle;
++
+ /* Find "/packages" */
+ chs = OF_pack_find_by_name(OF_env, OF_node_root, "/chosen");
+ if (chs == NULL) {
+@@ -4459,10 +4679,6 @@
+ ERROR("Cannot get '/chosen'\n");
+ break;
+ }
+- {
+-#if 1
+- OF_node_t *map;
+- uint32_t phandle;
+ map = OF_node_new(OF_env, chs, "memory-map", OF_ADDRESS_NONE);
+ if (map == NULL) {
+ pushd(OF_env, -1);
+@@ -4473,11 +4689,8 @@
+ OF_node_put(OF_env, map);
+ OF_node_put(OF_env, chs);
+ pushd(OF_env, phandle);
+- }
+-#else
+- pushd(OF_env, 0);
+-#endif
+ pushd(OF_env, 0);
++ }
+ break;
+ case 0x32a2d18e: /* MacOS X 10.2 and OpenDarwin 6.02 */
+ /* Return screen ihandle */
+@@ -4540,9 +4753,10 @@
+ case 0x4ad41f2d:
+ /* Yaboot: wait 10 ms: sure ! */
+ break;
++
+ default:
+ /* ERROR */
+- printf("Script:\n%s\n", FString);
++ printf("Script: len=%d\n%s\n", (int)strlen(FString), FString);
+ printf("Call %0x NOT IMPLEMENTED !\n", crc);
+ bug();
+ break;
+@@ -4581,6 +4795,7 @@
+ {
+ OF_CHECK_NBARGS(OF_env, 0);
+ /* Should free all OF resources */
++ bd_reset_all();
+ #if defined (DEBUG_BIOS)
+ {
+ uint16_t loglevel = 0x02 | 0x10 | 0x80;
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/pci.c OpenHackWare-release-0.4/src/pci.c
+--- OpenHackWare-release-0.4.org/src/pci.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/pci.c 2005-07-07 23:27:37.000000000 +0200
+@@ -99,8 +99,8 @@
+ uint16_t min_grant;
+ uint16_t max_latency;
+ uint8_t irq_line;
+- uint32_t regions[6];
+- uint32_t sizes[6];
++ uint32_t regions[7]; /* the region 6 is the PCI ROM */
++ uint32_t sizes[7];
+ pci_device_t *next;
+ };
+
+@@ -158,6 +158,7 @@
+
+ /* IRQ numbers assigned to PCI IRQs */
+ static uint8_t prep_pci_irqs[4] = { 9, 11, 9, 11 };
++static uint8_t heathrow_pci_irqs[4] = { 0x15, 0x16, 0x17, 0x18 };
+ static uint8_t pmac_pci_irqs[4] = { 8, 9, 10, 11 };
+
+ /* PREP PCI host */
+@@ -399,6 +400,79 @@
+ &uninorth_config_readl, &uninorth_config_writel,
+ };
+
++/* Grackle PCI host */
++
++static uint32_t grackle_cfg_address (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = 0x80000000 | (bus << 16) | (devfn << 8) | (offset & 0xfc);
++ stswap32((uint32_t *)bridge->cfg_addr, addr);
++ return bridge->cfg_data + (offset & 3);
++}
++
++static uint8_t grackle_config_readb (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return *((uint8_t *)addr);
++}
++
++static void grackle_config_writeb (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint8_t val)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ *((uint8_t *)addr) = val;
++}
++
++static uint16_t grackle_config_readw (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return ldswap16((uint16_t *)addr);
++}
++
++static void grackle_config_writew (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint16_t val)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ stswap16((uint16_t *)addr, val);
++}
++
++static uint32_t grackle_config_readl (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return ldswap32((uint32_t *)addr);
++}
++
++static void grackle_config_writel (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint32_t val)
++{
++ uint32_t addr;
++
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ stswap32((uint32_t *)addr, val);
++}
++
++static pci_ops_t grackle_pci_ops = {
++ &grackle_config_readb, &grackle_config_writeb,
++ &grackle_config_readw, &grackle_config_writew,
++ &grackle_config_readl, &grackle_config_writel,
++};
++
+ static inline uint8_t pci_config_readb (pci_bridge_t *bridge,
+ uint8_t bus, uint8_t devfn,
+ uint8_t offset)
+@@ -466,12 +540,22 @@
+ },
+ };
+
++static int ide_config_cb2 (pci_device_t *device)
++{
++ OF_finalize_pci_ide(device->common.OF_private,
++ device->regions[0] & ~0x0000000F,
++ device->regions[1] & ~0x0000000F,
++ device->regions[2] & ~0x0000000F,
++ device->regions[3] & ~0x0000000F);
++ return 0;
++}
++
+ static pci_dev_t ide_devices[] = {
+ {
+- 0x8086, 0x0100,
+- NULL, "Qemu IDE", "Qemu IDE", "ide",
++ 0x1095, 0x0646, /* CMD646 IDE controller */
++ "pci-ide", "pci-ata", NULL, NULL,
+ 0, 0, 0,
+- NULL, NULL,
++ ide_config_cb2, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+@@ -481,7 +565,9 @@
+ },
+ };
+
+-static int ide_config_cb (pci_device_t *device)
++#if 0
++/* should base it on PCI ID, not on arch */
++static int ide_config_cb (unused pci_device_t *device)
+ {
+ printf("Register IDE controller\n");
+ switch (arch) {
+@@ -491,14 +577,8 @@
+ device->common.OF_private);
+ break;
+ default:
+- ide_pci_pc_register(device->regions[0] & ~0x0000000F,
+- device->regions[1] & ~0x0000000F,
+- device->regions[2] & ~0x0000000F,
+- device->regions[3] & ~0x0000000F,
+- device->common.OF_private);
+ break;
+ }
+-
+ return 0;
+ }
+
+@@ -512,16 +592,12 @@
+ device->common.OF_private);
+ break;
+ default:
+- ide_pci_pc_register(device->regions[0] & ~0x0000000F,
+- device->regions[1] & ~0x0000000F,
+- device->regions[2] & ~0x0000000F,
+- device->regions[3] & ~0x0000000F,
+- device->common.OF_private);
+ break;
+ }
+
+ return 0;
+ }
++#endif
+
+ static pci_subclass_t mass_subclass[] = {
+ {
+@@ -530,7 +606,7 @@
+ },
+ {
+ 0x01, "IDE controller", "ide", ide_devices, NULL,
+- &ide_config_cb, NULL,
++ NULL, NULL,
+ },
+ {
+ 0x02, "Floppy disk controller", NULL, NULL, NULL,
+@@ -546,7 +622,7 @@
+ },
+ {
+ 0x05, "ATA controller", "ata", NULL, NULL,
+- &ata_config_cb, NULL,
++ NULL, NULL,
+ },
+ {
+ 0x80, "misc mass-storage controller", NULL, NULL, NULL,
+@@ -646,7 +722,9 @@
+ /* VGA 640x480x16 */
+ OF_vga_register(device->common.device->name,
+ device->regions[0] & ~0x0000000F,
+- vga_width, vga_height, vga_depth);
++ vga_width, vga_height, vga_depth,
++ device->regions[6] & ~0x0000000F,
++ device->sizes[6]);
+ }
+ vga_console_register();
+
+@@ -750,6 +828,13 @@
+ NULL, &PREP_pci_ops,
+ };
+
++pci_dev_t grackle_fake_bridge = {
++ 0xFFFF, 0xFFFF,
++ "pci", "pci-bridge", "DEC,21154", "DEC,21154.pci-bridge",
++ -1, -1, -1,
++ NULL, &grackle_pci_ops,
++};
++
+ static pci_dev_t hbrg_devices[] = {
+ {
+ 0x106B, 0x0020, NULL,
+@@ -758,8 +843,8 @@
+ NULL, &uninorth_agp_fake_bridge,
+ },
+ {
+- 0x106B, 0x001F,
+- NULL, "pci", "AAPL,UniNorth", "uni-north",
++ 0x106B, 0x001F, NULL,
++ "pci", "AAPL,UniNorth", "uni-north",
+ 3, 2, 1,
+ NULL, &uninorth_fake_bridge,
+ },
+@@ -770,10 +855,10 @@
+ NULL, &uninorth_fake_bridge,
+ },
+ {
+- 0x1011, 0x0026, NULL,
+- "pci-bridge", NULL, NULL,
++ 0x1057, 0x0002, "pci",
++ "pci", "MOT,MPC106", "grackle",
+ 3, 2, 1,
+- NULL, &PREP_pci_ops,
++ NULL, &grackle_fake_bridge,
+ },
+ {
+ 0x1057, 0x4801, NULL,
+@@ -1443,7 +1528,14 @@
+ }
+
+ static const pci_dev_t misc_pci[] = {
+- /* Apple Mac-io controller */
++ /* Paddington Mac I/O */
++ {
++ 0x106B, 0x0017,
++ "mac-io", "mac-io", "AAPL,343S1211", "paddington\1heathrow",
++ 1, 1, 1,
++ &macio_config_cb, NULL,
++ },
++ /* KeyLargo Mac I/O */
+ {
+ 0x106B, 0x0022,
+ "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo",
+@@ -1599,7 +1691,7 @@
+ uint8_t min_grant, uint8_t max_latency,
+ int irq_line)
+ {
+- uint32_t cmd;
++ uint32_t cmd, addr;
+ int i;
+
+ device->min_grant = min_grant;
+@@ -1611,22 +1703,28 @@
+ printf("MAP PCI device %d:%d to IRQ %d\n",
+ device->bus, device->devfn, irq_line);
+ }
+- for (i = 0; i < 6; i++) {
++ for (i = 0; i < 7; i++) {
+ if ((device->regions[i] & ~0xF) != 0x00000000 &&
+ (device->regions[i] & ~0xF) != 0xFFFFFFF0) {
+ printf("Map PCI device %d:%d %d to %0x %0x (%s)\n",
+ device->bus, device->devfn, i,
+ device->regions[i], device->sizes[i],
+- device->regions[i] & 0x00000001 ? "I/O" : "memory");
++ (device->regions[i] & 0x00000001) && i != 6 ? "I/O" :
++ "memory");
++ if (i != 6) {
+ cmd = pci_config_readl(bridge, device->bus, device->devfn, 0x04);
+ if (device->regions[i] & 0x00000001)
+ cmd |= 0x00000001;
+ else
+ cmd |= 0x00000002;
+ pci_config_writel(bridge, device->bus, device->devfn, 0x04, cmd);
++ }
++ if (i == 6)
++ addr = 0x30; /* PCI ROM */
++ else
++ addr = 0x10 + (i * sizeof(uint32_t));
+ pci_config_writel(bridge, device->bus, device->devfn,
+- 0x10 + (i * sizeof(uint32_t)),
+- device->regions[i]);
++ addr, device->regions[i]);
+ }
+ }
+ }
+@@ -1900,7 +1998,7 @@
+ goto out;
+ }
+ ret = (pci_u_t *)newd;
+- max_areas = 6;
++ max_areas = 7;
+ /* register PCI device in OF tree */
+ if (bridge->dev.common.type == PCI_FAKE_BRIDGE) {
+ newd->common.OF_private =
+@@ -1927,6 +2025,9 @@
+ /* Handle 64 bits memory mapping */
+ continue;
+ }
++ if (i == 6)
++ addr = 0x30; /* PCI ROM */
++ else
+ addr = 0x10 + (i * sizeof(uint32_t));
+ /* Get region size
+ * Note: we assume it's always a power of 2
+@@ -1935,7 +2036,7 @@
+ smask = pci_config_readl(bridge, bus, devfn, addr);
+ if (smask == 0x00000000 || smask == 0xFFFFFFFF)
+ continue;
+- if (smask & 0x00000001) {
++ if ((smask & 0x00000001) != 0 && i != 6) {
+ /* I/O space */
+ base = io_base;
+ /* Align to a minimum of 256 bytes (arbitrary) */
+@@ -1947,6 +2048,8 @@
+ /* Align to a minimum of 64 kB (arbitrary) */
+ min_align = 1 << 16;
+ amask = 0x0000000F;
++ if (i == 6)
++ smask |= 1; /* PCI ROM enable */
+ }
+ omask = smask & amask;
+ smask &= ~amask;
+@@ -1980,7 +2083,10 @@
+ if (irq_pin > 0) {
+ /* assign the IRQ */
+ irq_pin = ((devfn >> 3) + irq_pin - 1) & 3;
+- if (arch == ARCH_PREP) {
++ /* XXX: should base it on the PCI bridge type, not the arch */
++ switch(arch) {
++ case ARCH_PREP:
++ {
+ int elcr_port, val;
+ irq_line = prep_pci_irqs[irq_pin];
+ /* set the IRQ to level-sensitive */
+@@ -1988,14 +2094,22 @@
+ val = inb(elcr_port);
+ val |= 1 << (irq_line & 7);
+ outb(elcr_port, val);
+- } else {
++ }
++ break;
++ case ARCH_MAC99:
+ irq_line = pmac_pci_irqs[irq_pin];
++ break;
++ case ARCH_HEATHROW:
++ irq_line = heathrow_pci_irqs[irq_pin];
++ break;
++ default:
++ break;
+ }
+ }
+ update_device:
+ pci_update_device(bridge, newd, min_grant, max_latency, irq_line);
+ OF_finalize_pci_device(newd->common.OF_private, bus, devfn,
+- newd->regions, newd->sizes);
++ newd->regions, newd->sizes, irq_line);
+ /* Call special inits if needed */
+ if (dev->config_cb != NULL)
+ (*dev->config_cb)(newd);
+@@ -2049,6 +2163,32 @@
+ case ARCH_CHRP:
+ /* TODO */
+ break;
++ case ARCH_HEATHROW:
++ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp);
++ if (dev == NULL)
++ return -1;
++ fake_host = pci_add_host(hostp, dev,
++ (0x06 << 24) | (0x00 << 16) | (0xFF << 8));
++ if (fake_host == NULL)
++ return -1;
++ fake_host->dev.common.type = PCI_FAKE_HOST;
++ dev = &grackle_fake_bridge;
++ if (dev == NULL)
++ goto free_fake_host;
++ fake_bridge = pci_add_bridge(fake_host, 0, 0, dev,
++ (0x06 << 24) | (0x04 << 16) | (0xFF << 8),
++ cfg_base, cfg_len,
++ cfg_base + 0x7ec00000,
++ cfg_base + 0x7ee00000,
++ mem_base, mem_len,
++ io_base, io_len,
++ rbase, rlen,
++ 0,
++ &grackle_pci_ops);
++ if (fake_bridge == NULL)
++ goto free_fake_host;
++ fake_bridge->dev.common.type = PCI_FAKE_BRIDGE;
++ break;
+ case ARCH_MAC99:
+ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp);
+ if (dev == NULL)
+@@ -2167,6 +2307,30 @@
+ case ARCH_CHRP:
+ /* TODO */
+ break;
++ case ARCH_HEATHROW:
++ cfg_base = 0x80000000;
++ cfg_len = 0x7f000000;
++ mem_base = 0x80000000;
++ mem_len = 0x01000000;
++ io_base = 0xfe000000;
++ io_len = 0x00800000;
++#if 1
++ rbase = 0xfd000000;
++ rlen = 0x01000000;
++#else
++ rbase = 0x00000000;
++ rlen = 0x01000000;
++#endif
++ if (pci_check_host(&pci_main, cfg_base, cfg_len,
++ mem_base, mem_len, io_base, io_len, rbase, rlen,
++ 0x1057, 0x0002) == 0) {
++ isa_io_base = io_base;
++ busnum++;
++ }
++ for (curh = pci_main; curh->next != NULL; curh = curh->next)
++ continue;
++ pci_check_devices(curh);
++ break;
+ case ARCH_MAC99:
+ /* We are supposed to have 3 host bridges:
+ * - the uninorth AGP bridge at 0xF0000000
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
new file mode 100644
index 0000000..7a729aa
--- /dev/null
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/ppc_rom.bin b/pc-bios/ppc_rom.bin
new file mode 100644
index 0000000..f7cd8a8
--- /dev/null
+++ b/pc-bios/ppc_rom.bin
Binary files differ
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
new file mode 100644
index 0000000..34f9a9f
--- /dev/null
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
new file mode 100644
index 0000000..17cb63f
--- /dev/null
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/pc-bios/vgabios.diff b/pc-bios/vgabios.diff
new file mode 100644
index 0000000..661c032
--- /dev/null
+++ b/pc-bios/vgabios.diff
@@ -0,0 +1,896 @@
+Index: Makefile
+===================================================================
+RCS file: /sources/vgabios/vgabios/Makefile,v
+retrieving revision 1.17
+diff -u -w -r1.17 Makefile
+--- Makefile 6 Mar 2005 13:06:47 -0000 1.17
++++ Makefile 14 Jun 2006 00:51:06 -0000
+@@ -22,7 +22,7 @@
+ cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin
+
+ clean:
+- /bin/rm -f biossums *.o *.s *.ld86 \
++ /bin/rm -f biossums vbetables-gen vbetables.h *.o *.s *.ld86 \
+ temp.awk.* vgabios*.orig _vgabios_* _vgabios-debug_* core vgabios*.bin vgabios*.txt $(RELEASE).bin *.bak
+
+ dist-clean: clean
+@@ -79,3 +79,9 @@
+
+ biossums: biossums.c
+ $(CC) -o biossums biossums.c
++
++vbetables-gen: vbetables-gen.c
++ $(CC) -o vbetables-gen vbetables-gen.c
++
++vbetables.h: vbetables-gen
++ ./vbetables-gen > $@
+Index: clext.c
+===================================================================
+RCS file: /sources/vgabios/vgabios/clext.c,v
+retrieving revision 1.10
+diff -u -w -r1.10 clext.c
+--- clext.c 25 Mar 2006 10:19:15 -0000 1.10
++++ clext.c 14 Jun 2006 00:51:06 -0000
+@@ -544,6 +544,13 @@
+ cirrus_set_video_mode_extended:
+ call cirrus_switch_mode
+ pop ax ;; mode
++ test al, #0x80
++ jnz cirrus_set_video_mode_extended_1
++ push ax
++ mov ax, #0xffff ; set to 0xff to keep win 2K happy
++ call cirrus_clear_vram
++ pop ax
++cirrus_set_video_mode_extended_1:
+ and al, #0x7f
+
+ push ds
+@@ -1011,6 +1018,13 @@
+ jnz cirrus_vesa_02h_3
+ call cirrus_enable_16k_granularity
+ cirrus_vesa_02h_3:
++ test bx, #0x8000 ;; no clear
++ jnz cirrus_vesa_02h_4
++ push ax
++ xor ax,ax
++ call cirrus_clear_vram
++ pop ax
++cirrus_vesa_02h_4:
+ pop ax
+ push ds
+ #ifdef CIRRUS_VESA3_PMINFO
+@@ -1479,6 +1493,38 @@
+ pop bx
+ ret
+
++cirrus_clear_vram:
++ pusha
++ push es
++ mov si, ax
++
++ call cirrus_enable_16k_granularity
++ call cirrus_extbios_85h
++ shl al, #2
++ mov bl, al
++ xor ah,ah
++cirrus_clear_vram_1:
++ mov al, #0x09
++ mov dx, #0x3ce
++ out dx, ax
++ push ax
++ mov cx, #0xa000
++ mov es, cx
++ xor di, di
++ mov ax, si
++ mov cx, #8192
++ cld
++ rep
++ stosw
++ pop ax
++ inc ah
++ cmp ah, bl
++ jne cirrus_clear_vram_1
++
++ pop es
++ popa
++ ret
++
+ cirrus_extbios_handlers:
+ ;; 80h
+ dw cirrus_extbios_80h
+Index: vbe.c
+===================================================================
+RCS file: /sources/vgabios/vgabios/vbe.c,v
+retrieving revision 1.48
+diff -u -w -r1.48 vbe.c
+--- vbe.c 26 Dec 2005 19:50:26 -0000 1.48
++++ vbe.c 14 Jun 2006 00:51:07 -0000
+@@ -118,21 +118,114 @@
+ .word VBE_VESA_MODE_END_OF_LIST
+ #endif
+
++ .align 2
+ vesa_pm_start:
+ dw vesa_pm_set_window - vesa_pm_start
+- dw vesa_pm_set_display_strt - vesa_pm_start
++ dw vesa_pm_set_display_start - vesa_pm_start
+ dw vesa_pm_unimplemented - vesa_pm_start
+- dw 0
++ dw vesa_pm_io_ports_table - vesa_pm_start
++vesa_pm_io_ports_table:
++ dw VBE_DISPI_IOPORT_INDEX
++ dw VBE_DISPI_IOPORT_INDEX + 1
++ dw VBE_DISPI_IOPORT_DATA
++ dw VBE_DISPI_IOPORT_DATA + 1
++ dw 0xffff
++ dw 0xffff
+
+ USE32
+ vesa_pm_set_window:
+- mov ax, #0x4f05
+- int #0x10
++ cmp bx, #0x00
++ je vesa_pm_set_display_window1
++ mov ax, #0x0100
++ ret
++vesa_pm_set_display_window1:
++ mov ax, dx
++ push dx
++ push ax
++ mov dx, # VBE_DISPI_IOPORT_INDEX
++ mov ax, # VBE_DISPI_INDEX_BANK
++ out dx, ax
++ pop ax
++ mov dx, # VBE_DISPI_IOPORT_DATA
++ out dx, ax
++ pop dx
++ mov ax, #0x004f
+ ret
+
+ vesa_pm_set_display_start:
+- mov ax, #0x4f07
+- int #0x10
++ cmp bl, #0x80
++ je vesa_pm_set_display_start1
++ cmp bl, #0x00
++ je vesa_pm_set_display_start1
++ mov ax, #0x0100
++ ret
++vesa_pm_set_display_start1:
++; convert offset to (X, Y) coordinate
++; (would be simpler to change Bochs VBE API...)
++ push eax
++ push ecx
++ push edx
++ push esi
++ push edi
++ shl edx, #16
++ and ecx, #0xffff
++ or ecx, edx
++ shl ecx, #2
++ mov eax, ecx
++
++ push eax
++ mov dx, # VBE_DISPI_IOPORT_INDEX
++ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
++ out dx, ax
++ mov dx, # VBE_DISPI_IOPORT_DATA
++ in ax, dx
++ movzx ecx, ax
++
++ mov dx, # VBE_DISPI_IOPORT_INDEX
++ mov ax, # VBE_DISPI_INDEX_BPP
++ out dx, ax
++ mov dx, # VBE_DISPI_IOPORT_DATA
++ in ax, dx
++ movzx esi, ax
++ pop eax
++
++ add esi, #7
++ shr esi, #3
++ imul ecx, esi
++ xor edx, edx
++ div ecx
++ mov edi, eax
++ mov eax, edx
++ xor edx, edx
++ div esi
++
++ push dx
++ push ax
++ mov dx, # VBE_DISPI_IOPORT_INDEX
++ mov ax, # VBE_DISPI_INDEX_X_OFFSET
++ out dx, ax
++ pop ax
++ mov dx, # VBE_DISPI_IOPORT_DATA
++ out dx, ax
++ pop dx
++
++ mov ax, di
++ push dx
++ push ax
++ mov dx, # VBE_DISPI_IOPORT_INDEX
++ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
++ out dx, ax
++ pop ax
++ mov dx, # VBE_DISPI_IOPORT_DATA
++ out dx, ax
++ pop dx
++
++ pop edi
++ pop esi
++ pop edx
++ pop ecx
++ pop eax
++ mov ax, #0x004f
+ ret
+
+ vesa_pm_unimplemented:
+@@ -835,6 +928,64 @@
+ ASM_END
+
+
++Bit16u vbe_biosfn_read_video_state_size()
++{
++ return 9 * 2;
++}
++
++void vbe_biosfn_save_video_state(ES, BX)
++ Bit16u ES; Bit16u BX;
++{
++ Bit16u enable, i;
++
++ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
++ enable = inw(VBE_DISPI_IOPORT_DATA);
++ write_word(ES, BX, enable);
++ BX += 2;
++ if (!(enable & VBE_DISPI_ENABLED))
++ return;
++ for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
++ if (i != VBE_DISPI_INDEX_ENABLE) {
++ outw(VBE_DISPI_IOPORT_INDEX, i);
++ write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA));
++ BX += 2;
++ }
++ }
++}
++
++
++void vbe_biosfn_restore_video_state(ES, BX)
++ Bit16u ES; Bit16u BX;
++{
++ Bit16u enable, i;
++
++ enable = read_word(ES, BX);
++ BX += 2;
++
++ if (!(enable & VBE_DISPI_ENABLED)) {
++ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
++ outw(VBE_DISPI_IOPORT_DATA, enable);
++ } else {
++ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
++ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
++ BX += 2;
++ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
++ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
++ BX += 2;
++ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
++ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
++ BX += 2;
++ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
++ outw(VBE_DISPI_IOPORT_DATA, enable);
++
++ for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
++ outw(VBE_DISPI_IOPORT_INDEX, i);
++ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
++ BX += 2;
++ }
++ }
++}
++
+ /** Function 04h - Save/Restore State
+ *
+ * Input:
+@@ -849,10 +1000,48 @@
+ * BX = Number of 64-byte blocks to hold the state buffer (if DL=00h)
+ *
+ */
+-void vbe_biosfn_save_restore_state(AX, DL, CX, ES, BX)
++void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX)
++Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX;
+ {
+-}
++ Bit16u ss=get_SS();
++ Bit16u result, val;
+
++ result = 0x4f;
++ switch(GET_DL()) {
++ case 0x00:
++ val = biosfn_read_video_state_size2(CX);
++#ifdef DEBUG
++ printf("VGA state size=%x\n", val);
++#endif
++ if (CX & 8)
++ val += vbe_biosfn_read_video_state_size();
++ write_word(ss, BX, val);
++ break;
++ case 0x01:
++ val = read_word(ss, BX);
++ val = biosfn_save_video_state(CX, ES, val);
++#ifdef DEBUG
++ printf("VGA save_state offset=%x\n", val);
++#endif
++ if (CX & 8)
++ vbe_biosfn_save_video_state(ES, val);
++ break;
++ case 0x02:
++ val = read_word(ss, BX);
++ val = biosfn_restore_video_state(CX, ES, val);
++#ifdef DEBUG
++ printf("VGA restore_state offset=%x\n", val);
++#endif
++ if (CX & 8)
++ vbe_biosfn_restore_video_state(ES, val);
++ break;
++ default:
++ // function failed
++ result = 0x100;
++ break;
++ }
++ write_word(ss, AX, result);
++}
+
+ /** Function 05h - Display Window Control
+ *
+@@ -1090,7 +1279,7 @@
+ */
+ ASM_START
+ vbe_biosfn_return_protected_mode_interface:
+- test bx, bx
++ test bl, bl
+ jnz _fail
+ mov di, #0xc000
+ mov es, di
+Index: vbe.h
+===================================================================
+RCS file: /sources/vgabios/vgabios/vbe.h,v
+retrieving revision 1.24
+diff -u -w -r1.24 vbe.h
+--- vbe.h 9 May 2004 20:31:31 -0000 1.24
++++ vbe.h 14 Jun 2006 00:51:07 -0000
+@@ -14,7 +14,7 @@
+ void vbe_biosfn_return_controller_information(AX, ES, DI);
+ void vbe_biosfn_return_mode_information(AX, CX, ES, DI);
+ void vbe_biosfn_set_mode(AX, BX, ES, DI);
+-void vbe_biosfn_save_restore_state(AX, DL, CX, ES, BX);
++void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX);
+ void vbe_biosfn_set_get_palette_data(AX);
+ void vbe_biosfn_return_protected_mode_interface(AX);
+
+@@ -151,6 +151,12 @@
+ Bit8u Reserved[189];
+ } ModeInfoBlock;
+
++typedef struct ModeInfoListItem
++{
++ Bit16u mode;
++ ModeInfoBlockCompact info;
++} ModeInfoListItem;
++
+ // VBE Return Status Info
+ // AL
+ #define VBE_RETURN_STATUS_SUPPORTED 0x4F
+@@ -193,6 +199,10 @@
+ #define VBE_VESA_MODE_1280X1024X1555 0x119
+ #define VBE_VESA_MODE_1280X1024X565 0x11A
+ #define VBE_VESA_MODE_1280X1024X888 0x11B
++#define VBE_VESA_MODE_1600X1200X8 0x11C
++#define VBE_VESA_MODE_1600X1200X1555 0x11D
++#define VBE_VESA_MODE_1600X1200X565 0x11E
++#define VBE_VESA_MODE_1600X1200X888 0x11F
+
+ // BOCHS/PLEX86 'own' mode numbers
+ #define VBE_OWN_MODE_320X200X8888 0x140
+@@ -202,6 +212,12 @@
+ #define VBE_OWN_MODE_1024X768X8888 0x144
+ #define VBE_OWN_MODE_1280X1024X8888 0x145
+ #define VBE_OWN_MODE_320X200X8 0x146
++#define VBE_OWN_MODE_1600X1200X8888 0x147
++#define VBE_OWN_MODE_1152X864X8 0x148
++#define VBE_OWN_MODE_1152X864X1555 0x149
++#define VBE_OWN_MODE_1152X864X565 0x14a
++#define VBE_OWN_MODE_1152X864X888 0x14b
++#define VBE_OWN_MODE_1152X864X8888 0x14c
+
+ #define VBE_VESA_MODE_END_OF_LIST 0xFFFF
+
+@@ -259,7 +275,7 @@
+ // like 0xE0000000
+
+
+- #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 4
++ #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8
+
+ #define VBE_DISPI_BANK_ADDRESS 0xA0000
+ #define VBE_DISPI_BANK_SIZE_KB 64
+Index: vgabios.c
+===================================================================
+RCS file: /sources/vgabios/vgabios/vgabios.c,v
+retrieving revision 1.64
+diff -u -w -r1.64 vgabios.c
+--- vgabios.c 25 Mar 2006 10:19:16 -0000 1.64
++++ vgabios.c 14 Jun 2006 00:51:07 -0000
+@@ -109,8 +109,8 @@
+ static void biosfn_write_string();
+ static void biosfn_read_state_info();
+ static void biosfn_read_video_state_size();
+-static void biosfn_save_video_state();
+-static void biosfn_restore_video_state();
++static Bit16u biosfn_save_video_state();
++static Bit16u biosfn_restore_video_state();
+ extern Bit8u video_save_pointer_table[];
+
+ // This is for compiling with gcc2 and gcc3
+@@ -748,12 +748,7 @@
+ vbe_biosfn_set_mode(&AX,BX,ES,DI);
+ break;
+ case 0x04:
+- //FIXME
+-#ifdef DEBUG
+- unimplemented();
+-#endif
+- // function failed
+- AX=0x100;
++ vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX);
+ break;
+ case 0x09:
+ //FIXME
+@@ -3138,23 +3133,215 @@
+ }
+
+ // --------------------------------------------------------------------------------------------
+-static void biosfn_read_video_state_size (CX,ES,BX) Bit16u CX;Bit16u ES;Bit16u BX;
++// --------------------------------------------------------------------------------------------
++static Bit16u biosfn_read_video_state_size2 (CX)
++ Bit16u CX;
+ {
+-#ifdef DEBUG
+- unimplemented();
+-#endif
++ Bit16u size;
++ size = 0;
++ if (CX & 1) {
++ size += 0x46;
++ }
++ if (CX & 2) {
++ size += (5 + 8 + 5) * 2 + 6;
++ }
++ if (CX & 4) {
++ size += 3 + 256 * 3 + 1;
+ }
+-static void biosfn_save_video_state (CX,ES,BX) Bit16u CX;Bit16u ES;Bit16u BX;
++ return size;
++}
++
++static void biosfn_read_video_state_size (CX, BX)
++ Bit16u CX; Bit16u *BX;
+ {
+-#ifdef DEBUG
+- unimplemented();
+-#endif
++ Bit16u ss=get_SS();
++ write_word(ss, BX, biosfn_read_video_state_size2(CX));
+ }
+-static void biosfn_restore_video_state (CX,ES,BX) Bit16u CX;Bit16u ES;Bit16u BX;
++
++static Bit16u biosfn_save_video_state (CX,ES,BX)
++ Bit16u CX;Bit16u ES;Bit16u BX;
+ {
+-#ifdef DEBUG
+- unimplemented();
+-#endif
++ Bit16u i, v, crtc_addr, ar_index;
++
++ crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS);
++ if (CX & 1) {
++ write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++;
++ write_byte(ES, BX, inb(crtc_addr)); BX++;
++ write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++;
++ inb(VGAREG_ACTL_RESET);
++ ar_index = inb(VGAREG_ACTL_ADDRESS);
++ write_byte(ES, BX, ar_index); BX++;
++ write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++;
++
++ for(i=1;i<=4;i++){
++ outb(VGAREG_SEQU_ADDRESS, i);
++ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
++ }
++ outb(VGAREG_SEQU_ADDRESS, 0);
++ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
++
++ for(i=0;i<=0x18;i++) {
++ outb(crtc_addr,i);
++ write_byte(ES, BX, inb(crtc_addr+1)); BX++;
++ }
++
++ for(i=0;i<=0x13;i++) {
++ inb(VGAREG_ACTL_RESET);
++ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
++ write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++;
++ }
++ inb(VGAREG_ACTL_RESET);
++
++ for(i=0;i<=8;i++) {
++ outb(VGAREG_GRDC_ADDRESS,i);
++ write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++;
++ }
++
++ write_word(ES, BX, crtc_addr); BX+= 2;
++
++ /* XXX: read plane latches */
++ write_byte(ES, BX, 0); BX++;
++ write_byte(ES, BX, 0); BX++;
++ write_byte(ES, BX, 0); BX++;
++ write_byte(ES, BX, 0); BX++;
++ }
++ if (CX & 2) {
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++;
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2;
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2;
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2;
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++;
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2;
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++;
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++;
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++;
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2;
++ for(i=0;i<8;i++) {
++ write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i));
++ BX += 2;
++ }
++ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2;
++ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++;
++ /* current font */
++ write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2;
++ write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2;
++ write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2;
++ write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2;
++ }
++ if (CX & 4) {
++ /* XXX: check this */
++ write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */
++ write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */
++ write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++;
++ // Set the whole dac always, from 0
++ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
++ for(i=0;i<256*3;i++) {
++ write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++;
++ }
++ write_byte(ES, BX, 0); BX++; /* color select register */
++ }
++ return BX;
++}
++
++static Bit16u biosfn_restore_video_state (CX,ES,BX)
++ Bit16u CX;Bit16u ES;Bit16u BX;
++{
++ Bit16u i, crtc_addr, v, addr1, ar_index;
++
++ if (CX & 1) {
++ // Reset Attribute Ctl flip-flop
++ inb(VGAREG_ACTL_RESET);
++
++ crtc_addr = read_word(ES, BX + 0x40);
++ addr1 = BX;
++ BX += 5;
++
++ for(i=1;i<=4;i++){
++ outb(VGAREG_SEQU_ADDRESS, i);
++ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
++ }
++ outb(VGAREG_SEQU_ADDRESS, 0);
++ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
++
++ // Disable CRTC write protection
++ outw(crtc_addr,0x0011);
++ // Set CRTC regs
++ for(i=0;i<=0x18;i++) {
++ if (i != 0x11) {
++ outb(crtc_addr,i);
++ outb(crtc_addr+1, read_byte(ES, BX));
++ }
++ BX++;
++ }
++ // select crtc base address
++ v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01;
++ if (crtc_addr = 0x3d4)
++ v |= 0x01;
++ outb(VGAREG_WRITE_MISC_OUTPUT, v);
++
++ // enable write protection if needed
++ outb(crtc_addr, 0x11);
++ outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11));
++
++ // Set Attribute Ctl
++ ar_index = read_byte(ES, addr1 + 0x03);
++ inb(VGAREG_ACTL_RESET);
++ for(i=0;i<=0x13;i++) {
++ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
++ outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++;
++ }
++ outb(VGAREG_ACTL_ADDRESS, ar_index);
++ inb(VGAREG_ACTL_RESET);
++
++ for(i=0;i<=8;i++) {
++ outb(VGAREG_GRDC_ADDRESS,i);
++ outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++;
++ }
++ BX += 2; /* crtc_addr */
++ BX += 4; /* plane latches */
++
++ outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++;
++ outb(crtc_addr, read_byte(ES, addr1)); addr1++;
++ outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++;
++ addr1++;
++ outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++;
++ }
++ if (CX & 2) {
++ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++;
++ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2;
++ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2;
++ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2;
++ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++;
++ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2;
++ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++;
++ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++;
++ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++;
++ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2;
++ for(i=0;i<8;i++) {
++ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX));
++ BX += 2;
++ }
++ write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2;
++ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++;
++ /* current font */
++ write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2;
++ write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2;
++ write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2;
++ write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2;
++ }
++ if (CX & 4) {
++ BX++;
++ v = read_byte(ES, BX); BX++;
++ outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++;
++ // Set the whole dac always, from 0
++ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
++ for(i=0;i<256*3;i++) {
++ outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++;
++ }
++ BX++;
++ outb(VGAREG_DAC_WRITE_ADDRESS, v);
++ }
++ return BX;
+ }
+
+ // ============================================================================================
+diff -u -w vbetables-gen.c
+--- vbetables-gen.c 1970-01-01 01:00:00.000000000 +0100
++++ vbetables-gen.c 2006-06-14 00:52:18.000000000 +0200
+@@ -0,0 +1,217 @@
++/* Generate the VGABIOS VBE Tables */
++#include <stdlib.h>
++#include <stdio.h>
++
++typedef struct {
++ int width;
++ int height;
++ int depth;
++ int mode;
++} ModeInfo;
++
++ModeInfo modes[] = {
++ /* standard VESA modes */
++{ 640, 400, 8 , 0x100},
++{ 640, 480, 8 , 0x101},
++{ 800, 600, 4 , 0x102},
++{ 800, 600, 8 , 0x103},
++ //{ 1024, 768, 4 , 0x104},
++{ 1024, 768, 8 , 0x105},
++ //{ 1280, 1024, 4 , 0x106},
++{ 1280, 1024, 8 , 0x107},
++{ 320, 200, 15 , 0x10D},
++{ 320, 200, 16 , 0x10E},
++{ 320, 200, 24 , 0x10F},
++{ 640, 480, 15 , 0x110},
++{ 640, 480, 16 , 0x111},
++{ 640, 480, 24 , 0x112},
++{ 800, 600, 15 , 0x113},
++{ 800, 600, 16 , 0x114},
++{ 800, 600, 24 , 0x115},
++{ 1024, 768, 15 , 0x116},
++{ 1024, 768, 16 , 0x117},
++{ 1024, 768, 24 , 0x118},
++{ 1280, 1024, 15 , 0x119},
++{ 1280, 1024, 16 , 0x11A},
++{ 1280, 1024, 24 , 0x11B},
++{ 1600, 1200, 8 , 0x11C},
++{ 1600, 1200, 15 , 0x11D},
++{ 1600, 1200, 16 , 0x11E},
++{ 1600, 1200, 24 , 0x11F},
++
++ /* BOCHS/PLE, 86 'own' mode numbers */
++{ 320, 200, 32 , 0x140},
++{ 640, 400, 32 , 0x141},
++{ 640, 480, 32 , 0x142},
++{ 800, 600, 32 , 0x143},
++{ 1024, 768, 32 , 0x144},
++{ 1280, 1024, 32 , 0x145},
++{ 320, 200, 8 , 0x146},
++{ 1600, 1200, 32 , 0x147},
++{ 1152, 864, 8 , 0x148},
++{ 1152, 864, 15 , 0x149},
++{ 1152, 864, 16 , 0x14a},
++{ 1152, 864, 24 , 0x14b},
++{ 1152, 864, 32 , 0x14c},
++{ 0, },
++};
++
++int main(int argc, char **argv)
++{
++ const ModeInfo *pm;
++ int pitch, r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos;
++ const char *str;
++
++ printf("/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT */\n");
++ printf("static ModeInfoListItem mode_info_list[]=\n");
++ printf("{\n");
++ for(pm = modes; pm->mode != 0; pm++) {
++ printf("{ 0x%04x, /* %dx%dx%d */\n",
++ pm->mode, pm->width, pm->height, pm->depth);
++ printf("{ /*Bit16u ModeAttributes*/ %s,\n",
++ "VBE_MODE_ATTRIBUTE_SUPPORTED | "
++ "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | "
++ "VBE_MODE_ATTRIBUTE_COLOR_MODE | "
++ "VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE | "
++ "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE");
++
++ printf("/*Bit8u WinAAttributes*/ %s,\n",
++ "VBE_WINDOW_ATTRIBUTE_RELOCATABLE | "
++ "VBE_WINDOW_ATTRIBUTE_READABLE | "
++ "VBE_WINDOW_ATTRIBUTE_WRITEABLE");
++
++ printf("/*Bit8u WinBAttributes*/ %d,\n", 0);
++
++ printf("/*Bit16u WinGranularity*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
++
++ printf("/*Bit16u WinSize*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
++
++ printf("/*Bit16u WinASegment*/ %s,\n", "VGAMEM_GRAPH");
++
++ printf("/*Bit16u WinBSegment*/ 0x%04x,\n", 0);
++
++ printf("/*Bit32u WinFuncPtr*/ %d,\n", 0);
++
++ if (pm->depth == 4)
++ pitch = (pm->width + 7) / 8;
++ else
++ pitch = pm->width * ((pm->depth + 7) / 8);
++ printf("/*Bit16u BytesPerScanLine*/ %d,\n", pitch);
++
++ // Mandatory information for VBE 1.2 and above
++ printf("/*Bit16u XResolution*/ %d,\n", pm->width);
++ printf("/*Bit16u YResolution*/ %d,\n", pm->height);
++ printf("/*Bit8u XCharSize*/ %d,\n", 8);
++ printf("/*Bit8u YCharSize*/ %d,\n", 16);
++ if (pm->depth == 4) {
++ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 4);
++ printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth);
++ } else {
++ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 1);
++ printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth);
++ }
++ printf("/*Bit8u NumberOfBanks*/ %d,\n",
++ (pm->height * pitch + 65535) / 65536);
++
++ if (pm->depth == 4)
++ str = "VBE_MEMORYMODEL_PLANAR";
++ else if (pm->depth == 8)
++ str = "VBE_MEMORYMODEL_PACKED_PIXEL";
++ else
++ str = "VBE_MEMORYMODEL_DIRECT_COLOR";
++ printf("/*Bit8u MemoryModel*/ %s,\n", str);
++ printf("/*Bit8u BankSize*/ %d,\n", 0);
++ /* XXX: check */
++ printf("/*Bit8u NumberOfImagePages*/ %d,\n", 0);
++ printf("/*Bit8u Reserved_page*/ %d,\n", 0);
++
++ // Direct Color fields (required for direct/6 and YUV/7 memory models)
++ switch(pm->depth) {
++ case 15:
++ r_size = 5;
++ r_pos = 10;
++ g_size = 5;
++ g_pos = 5;
++ b_size = 5;
++ b_pos = 0;
++ a_size = 1;
++ a_pos = 15;
++ break;
++ case 16:
++ r_size = 5;
++ r_pos = 11;
++ g_size = 6;
++ g_pos = 5;
++ b_size = 5;
++ b_pos = 0;
++ a_size = 0;
++ a_pos = 0;
++ break;
++ case 24:
++ r_size = 8;
++ r_pos = 16;
++ g_size = 8;
++ g_pos = 8;
++ b_size = 8;
++ b_pos = 0;
++ a_size = 0;
++ a_pos = 0;
++ break;
++ case 32:
++ r_size = 8;
++ r_pos = 16;
++ g_size = 8;
++ g_pos = 8;
++ b_size = 8;
++ b_pos = 0;
++ a_size = 8;
++ a_pos = 24;
++ break;
++ default:
++ r_size = 0;
++ r_pos = 0;
++ g_size = 0;
++ g_pos = 0;
++ b_size = 0;
++ b_pos = 0;
++ a_size = 0;
++ a_pos = 0;
++ break;
++ }
++
++ printf("/*Bit8u RedMaskSize*/ %d,\n", r_size);
++ printf("/*Bit8u RedFieldPosition*/ %d,\n", r_pos);
++ printf("/*Bit8u GreenMaskSize*/ %d,\n", g_size);
++ printf("/*Bit8u GreenFieldPosition*/ %d,\n", g_pos);
++ printf("/*Bit8u BlueMaskSize*/ %d,\n", b_size);
++ printf("/*Bit8u BlueFieldPosition*/ %d,\n", b_pos);
++ printf("/*Bit8u RsvdMaskSize*/ %d,\n", a_size);
++ printf("/*Bit8u RsvdFieldPosition*/ %d,\n", a_pos);
++ printf("/*Bit8u DirectColorModeInfo*/ %d,\n", 0);
++
++// Mandatory information for VBE 2.0 and above
++ printf("/*Bit32u PhysBasePtr*/ %s,\n",
++ "VBE_DISPI_LFB_PHYSICAL_ADDRESS");
++ printf("/*Bit32u OffScreenMemOffset*/ %d,\n", 0);
++ printf("/*Bit16u OffScreenMemSize*/ %d,\n", 0);
++ // Mandatory information for VBE 3.0 and above
++ printf("/*Bit16u LinBytesPerScanLine*/ %d,\n", pitch);
++ printf("/*Bit8u BnkNumberOfPages*/ %d,\n", 0);
++ printf("/*Bit8u LinNumberOfPages*/ %d,\n", 0);
++ printf("/*Bit8u LinRedMaskSize*/ %d,\n", r_size);
++ printf("/*Bit8u LinRedFieldPosition*/ %d,\n", r_pos);
++ printf("/*Bit8u LinGreenMaskSize*/ %d,\n", g_size);
++ printf("/*Bit8u LinGreenFieldPosition*/ %d,\n", g_pos);
++ printf("/*Bit8u LinBlueMaskSize*/ %d,\n", b_size);
++ printf("/*Bit8u LinBlueFieldPosition*/ %d,\n", b_pos);
++ printf("/*Bit8u LinRsvdMaskSize*/ %d,\n", a_size);
++ printf("/*Bit8u LinRsvdFieldPosition*/ %d,\n", a_pos);
++ printf("/*Bit32u MaxPixelClock*/ %d,\n", 0);
++ printf("} },\n");
++ }
++ printf("{ VBE_VESA_MODE_END_OF_LIST,\n");
++ printf("{ 0,\n");
++ printf("} },\n");
++ printf("};\n");
++ return 0;
++}
diff --git a/pc-bios/video.x b/pc-bios/video.x
new file mode 100644
index 0000000..761aa0c
--- /dev/null
+++ b/pc-bios/video.x
Binary files differ
diff --git a/ppc-dis.c b/ppc-dis.c
new file mode 100644
index 0000000..f6fad88
--- /dev/null
+++ b/ppc-dis.c
@@ -0,0 +1,3246 @@
+/* ppc-dis.c -- Disassemble PowerPC instructions
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+#include "dis-asm.h"
+
+/* ppc.h -- Header file for PowerPC opcode table
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* The opcode table is an array of struct powerpc_opcode. */
+
+struct powerpc_opcode
+{
+ /* The opcode name. */
+ const char *name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ uint32_t opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ uint32_t mask;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The defined values
+ are listed below. */
+ uint32_t flags;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[8];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+extern const struct powerpc_opcode powerpc_opcodes[];
+extern const int powerpc_num_opcodes;
+
+/* Values defined for the flags field of a struct powerpc_opcode. */
+
+/* Opcode is defined for the PowerPC architecture. */
+#define PPC_OPCODE_PPC (01)
+
+/* Opcode is defined for the POWER (RS/6000) architecture. */
+#define PPC_OPCODE_POWER (02)
+
+/* Opcode is defined for the POWER2 (Rios 2) architecture. */
+#define PPC_OPCODE_POWER2 (04)
+
+/* Opcode is only defined on 32 bit architectures. */
+#define PPC_OPCODE_32 (010)
+
+/* Opcode is only defined on 64 bit architectures. */
+#define PPC_OPCODE_64 (020)
+
+/* Opcode is supported by the Motorola PowerPC 601 processor. The 601
+ is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
+ but it also supports many additional POWER instructions. */
+#define PPC_OPCODE_601 (040)
+
+/* A macro to extract the major opcode from an instruction. */
+#define PPC_OP(i) (((i) >> 26) & 0x3f)
+
+/* The operands table is an array of struct powerpc_operand. */
+
+struct powerpc_operand
+{
+ /* The number of bits in the operand. */
+ int bits;
+
+ /* How far the operand is left shifted in the instruction. */
+ int shift;
+
+ /* Insertion function. This is used by the assembler. To insert an
+ operand value into an instruction, check this field.
+
+ If it is NULL, execute
+ i |= (op & ((1 << o->bits) - 1)) << o->shift;
+ (i is the instruction which we are filling in, o is a pointer to
+ this structure, and op is the opcode value; this assumes twos
+ complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction and the operand value. It will return the new value
+ of the instruction. If the ERRMSG argument is not NULL, then if
+ the operand value is illegal, *ERRMSG will be set to a warning
+ string (the operand will be inserted in any case). If the
+ operand value is legal, *ERRMSG will be unchanged (most operands
+ can accept any value). */
+ unsigned long (*insert)(uint32_t instruction, int32_t op,
+ const char **errmsg);
+
+ /* Extraction function. This is used by the disassembler. To
+ extract this operand type from an instruction, check this field.
+
+ If it is NULL, compute
+ op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+ if ((o->flags & PPC_OPERAND_SIGNED) != 0
+ && (op & (1 << (o->bits - 1))) != 0)
+ op -= 1 << o->bits;
+ (i is the instruction, o is a pointer to this structure, and op
+ is the result; this assumes twos complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction value. It will return the value of the operand. If
+ the INVALID argument is not NULL, *INVALID will be set to
+ non-zero if this operand type can not actually be extracted from
+ this operand (i.e., the instruction does not match). If the
+ operand is valid, *INVALID will not be changed. */
+ long (*extract) (uint32_t instruction, int *invalid);
+
+ /* One bit syntax flags. */
+ uint32_t flags;
+};
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the powerpc_opcodes table. */
+
+extern const struct powerpc_operand powerpc_operands[];
+
+/* Values defined for the flags field of a struct powerpc_operand. */
+
+/* This operand takes signed values. */
+#define PPC_OPERAND_SIGNED (01)
+
+/* This operand takes signed values, but also accepts a full positive
+ range of values when running in 32 bit mode. That is, if bits is
+ 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode,
+ this flag is ignored. */
+#define PPC_OPERAND_SIGNOPT (02)
+
+/* This operand does not actually exist in the assembler input. This
+ is used to support extended mnemonics such as mr, for which two
+ operands fields are identical. The assembler should call the
+ insert function with any op value. The disassembler should call
+ the extract function, ignore the return value, and check the value
+ placed in the valid argument. */
+#define PPC_OPERAND_FAKE (04)
+
+/* The next operand should be wrapped in parentheses rather than
+ separated from this one by a comma. This is used for the load and
+ store instructions which want their operands to look like
+ reg,displacement(reg)
+ */
+#define PPC_OPERAND_PARENS (010)
+
+/* This operand may use the symbolic names for the CR fields, which
+ are
+ lt 0 gt 1 eq 2 so 3 un 3
+ cr0 0 cr1 1 cr2 2 cr3 3
+ cr4 4 cr5 5 cr6 6 cr7 7
+ These may be combined arithmetically, as in cr2*4+gt. These are
+ only supported on the PowerPC, not the POWER. */
+#define PPC_OPERAND_CR (020)
+
+/* This operand names a register. The disassembler uses this to print
+ register names with a leading 'r'. */
+#define PPC_OPERAND_GPR (040)
+
+/* This operand names a floating point register. The disassembler
+ prints these with a leading 'f'. */
+#define PPC_OPERAND_FPR (0100)
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_RELATIVE (0200)
+
+/* This operand is an absolute branch address. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_ABSOLUTE (0400)
+
+/* This operand is optional, and is zero if omitted. This is used for
+ the optional BF and L fields in the comparison instructions. The
+ assembler must count the number of operands remaining on the line,
+ and the number of operands remaining for the opcode, and decide
+ whether this operand is present or not. The disassembler should
+ print this operand out only if it is not zero. */
+#define PPC_OPERAND_OPTIONAL (01000)
+
+/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand
+ is omitted, then for the next operand use this operand value plus
+ 1, ignoring the next operand field for the opcode. This wretched
+ hack is needed because the Power rotate instructions can take
+ either 4 or 5 operands. The disassembler should print this operand
+ out regardless of the PPC_OPERAND_OPTIONAL field. */
+#define PPC_OPERAND_NEXT (02000)
+
+/* This operand should be regarded as a negative number for the
+ purposes of overflow checking (i.e., the normal most negative
+ number is disallowed and one more than the normal most positive
+ number is allowed). This flag will only be set for a signed
+ operand. */
+#define PPC_OPERAND_NEGATIVE (04000)
+
+/* The POWER and PowerPC assemblers use a few macros. We keep them
+ with the operands table for simplicity. The macro table is an
+ array of struct powerpc_macro. */
+
+struct powerpc_macro
+{
+ /* The macro name. */
+ const char *name;
+
+ /* The number of operands the macro takes. */
+ unsigned int operands;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The values are the
+ same as those for the struct powerpc_opcode flags field. */
+ uint32_t flags;
+
+ /* A format string to turn the macro into a normal instruction.
+ Each %N in the string is replaced with operand number N (zero
+ based). */
+ const char *format;
+};
+
+extern const struct powerpc_macro powerpc_macros[];
+extern const int powerpc_num_macros;
+
+/* ppc-opc.c -- PowerPC opcode list
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This file holds the PowerPC opcode table. The opcode table
+ includes almost all of the extended instruction mnemonics. This
+ permits the disassembler to use them, and simplifies the assembler
+ logic, at the cost of increasing the table size. The table is
+ strictly constant data, so the compiler should be able to put it in
+ the .text section.
+
+ This file also holds the operand table. All knowledge about
+ inserting operands into instructions and vice-versa is kept in this
+ file. */
+
+/* Local insertion and extraction functions. */
+
+static unsigned long insert_bat (uint32_t, int32_t, const char **);
+static long extract_bat(uint32_t, int *);
+static unsigned long insert_bba(uint32_t, int32_t, const char **);
+static long extract_bba(uint32_t, int *);
+static unsigned long insert_bd(uint32_t, int32_t, const char **);
+static long extract_bd(uint32_t, int *);
+static unsigned long insert_bdm(uint32_t, int32_t, const char **);
+static long extract_bdm(uint32_t, int *);
+static unsigned long insert_bdp(uint32_t, int32_t, const char **);
+static long extract_bdp(uint32_t, int *);
+static unsigned long insert_bo(uint32_t, int32_t, const char **);
+static long extract_bo(uint32_t, int *);
+static unsigned long insert_boe(uint32_t, int32_t, const char **);
+static long extract_boe(uint32_t, int *);
+static unsigned long insert_ds(uint32_t, int32_t, const char **);
+static long extract_ds(uint32_t, int *);
+static unsigned long insert_li(uint32_t, int32_t, const char **);
+static long extract_li(uint32_t, int *);
+static unsigned long insert_mbe(uint32_t, int32_t, const char **);
+static long extract_mbe(uint32_t, int *);
+static unsigned long insert_mb6(uint32_t, int32_t, const char **);
+static long extract_mb6(uint32_t, int *);
+static unsigned long insert_nb(uint32_t, int32_t, const char **);
+static long extract_nb(uint32_t, int *);
+static unsigned long insert_nsi(uint32_t, int32_t, const char **);
+static long extract_nsi(uint32_t, int *);
+static unsigned long insert_ral(uint32_t, int32_t, const char **);
+static unsigned long insert_ram(uint32_t, int32_t, const char **);
+static unsigned long insert_ras(uint32_t, int32_t, const char **);
+static unsigned long insert_rbs(uint32_t, int32_t, const char **);
+static long extract_rbs(uint32_t, int *);
+static unsigned long insert_sh6(uint32_t, int32_t, const char **);
+static long extract_sh6(uint32_t, int *);
+static unsigned long insert_spr(uint32_t, int32_t, const char **);
+static long extract_spr(uint32_t, int *);
+static unsigned long insert_tbr(uint32_t, int32_t, const char **);
+static long extract_tbr(uint32_t, int *);
+
+/* The operands table.
+
+ The fields are bits, shift, signed, insert, extract, flags. */
+
+const struct powerpc_operand powerpc_operands[] =
+{
+ /* The zero index is used to indicate the end of the list of
+ operands. */
+#define UNUSED (0)
+ { 0, 0, 0, 0, 0 },
+
+ /* The BA field in an XL form instruction. */
+#define BA (1)
+#define BA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+ /* The BA field in an XL form instruction when it must be the same
+ as the BT field in the same instruction. */
+#define BAT (2)
+ { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE },
+
+ /* The BB field in an XL form instruction. */
+#define BB (3)
+#define BB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_CR },
+
+ /* The BB field in an XL form instruction when it must be the same
+ as the BA field in the same instruction. */
+#define BBA (4)
+ { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE },
+
+ /* The BD field in a B form instruction. The lower two bits are
+ forced to zero. */
+#define BD (5)
+ { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when absolute addressing is
+ used. */
+#define BDA (6)
+ { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDM (7)
+ { 16, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used
+ and absolute address is used. */
+#define BDMA (8)
+ { 16, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDP (9)
+ { 16, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used
+ and absolute addressing is used. */
+#define BDPA (10)
+ { 16, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BF field in an X or XL form instruction. */
+#define BF (11)
+ { 3, 23, 0, 0, PPC_OPERAND_CR },
+
+ /* An optional BF field. This is used for comparison instructions,
+ in which an omitted BF field is taken as zero. */
+#define OBF (12)
+ { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The BFA field in an X or XL form instruction. */
+#define BFA (13)
+ { 3, 18, 0, 0, PPC_OPERAND_CR },
+
+ /* The BI field in a B form or XL form instruction. */
+#define BI (14)
+#define BI_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+ /* The BO field in a B form instruction. Certain values are
+ illegal. */
+#define BO (15)
+#define BO_MASK (0x1f << 21)
+ { 5, 21, insert_bo, extract_bo, 0 },
+
+ /* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. */
+#define BOE (16)
+ { 5, 21, insert_boe, extract_boe, 0 },
+
+ /* The BT field in an X or XL form instruction. */
+#define BT (17)
+ { 5, 21, 0, 0, PPC_OPERAND_CR },
+
+ /* The condition register number portion of the BI field in a B form
+ or XL form instruction. This is used for the extended
+ conditional branch mnemonics, which set the lower two bits of the
+ BI field. This field is optional. */
+#define CR (18)
+ { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The D field in a D form instruction. This is a displacement off
+ a register, and implies that the next operand is a register in
+ parentheses. */
+#define D (19)
+ { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The DS field in a DS form instruction. This is like D, but the
+ lower two bits are forced to zero. */
+#define DS (20)
+ { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The FL1 field in a POWER SC form instruction. */
+#define FL1 (21)
+ { 4, 12, 0, 0, 0 },
+
+ /* The FL2 field in a POWER SC form instruction. */
+#define FL2 (22)
+ { 3, 2, 0, 0, 0 },
+
+ /* The FLM field in an XFL form instruction. */
+#define FLM (23)
+ { 8, 17, 0, 0, 0 },
+
+ /* The FRA field in an X or A form instruction. */
+#define FRA (24)
+#define FRA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRB field in an X or A form instruction. */
+#define FRB (25)
+#define FRB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRC field in an A form instruction. */
+#define FRC (26)
+#define FRC_MASK (0x1f << 6)
+ { 5, 6, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRS field in an X form instruction or the FRT field in a D, X
+ or A form instruction. */
+#define FRS (27)
+#define FRT (FRS)
+ { 5, 21, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FXM field in an XFX instruction. */
+#define FXM (28)
+#define FXM_MASK (0xff << 12)
+ { 8, 12, 0, 0, 0 },
+
+ /* The L field in a D or X form instruction. */
+#define L (29)
+ { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL },
+
+ /* The LEV field in a POWER SC form instruction. */
+#define LEV (30)
+ { 7, 5, 0, 0, 0 },
+
+ /* The LI field in an I form instruction. The lower two bits are
+ forced to zero. */
+#define LI (31)
+ { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The LI field in an I form instruction when used as an absolute
+ address. */
+#define LIA (32)
+ { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The MB field in an M form instruction. */
+#define MB (33)
+#define MB_MASK (0x1f << 6)
+ { 5, 6, 0, 0, 0 },
+
+ /* The ME field in an M form instruction. */
+#define ME (34)
+#define ME_MASK (0x1f << 1)
+ { 5, 1, 0, 0, 0 },
+
+ /* The MB and ME fields in an M form instruction expressed a single
+ operand which is a bitmask indicating which bits to select. This
+ is a two operand form using PPC_OPERAND_NEXT. See the
+ description in opcode/ppc.h for what this means. */
+#define MBE (35)
+ { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+ { 32, 0, insert_mbe, extract_mbe, 0 },
+
+ /* The MB or ME field in an MD or MDS form instruction. The high
+ bit is wrapped to the low end. */
+#define MB6 (37)
+#define ME6 (MB6)
+#define MB6_MASK (0x3f << 5)
+ { 6, 5, insert_mb6, extract_mb6, 0 },
+
+ /* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+#define NB (38)
+ { 6, 11, insert_nb, extract_nb, 0 },
+
+ /* The NSI field in a D form instruction. This is the same as the
+ SI field, only negated. */
+#define NSI (39)
+ { 16, 0, insert_nsi, extract_nsi,
+ PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
+ /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */
+#define RA (40)
+#define RA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+#define RAL (41)
+ { 5, 16, insert_ral, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in an lmw instruction, which has special value
+ restrictions. */
+#define RAM (42)
+ { 5, 16, insert_ram, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+#define RAS (43)
+ { 5, 16, insert_ras, 0, PPC_OPERAND_GPR },
+
+ /* The RB field in an X, XO, M, or MDS form instruction. */
+#define RB (44)
+#define RB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_GPR },
+
+ /* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. */
+#define RBS (45)
+ { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE },
+
+ /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form
+ instruction or the RT field in a D, DS, X, XFX or XO form
+ instruction. */
+#define RS (46)
+#define RT (RS)
+#define RT_MASK (0x1f << 21)
+ { 5, 21, 0, 0, PPC_OPERAND_GPR },
+
+ /* The SH field in an X or M form instruction. */
+#define SH (47)
+#define SH_MASK (0x1f << 11)
+ { 5, 11, 0, 0, 0 },
+
+ /* The SH field in an MD form instruction. This is split. */
+#define SH6 (48)
+#define SH6_MASK ((0x1f << 11) | (1 << 1))
+ { 6, 1, insert_sh6, extract_sh6, 0 },
+
+ /* The SI field in a D form instruction. */
+#define SI (49)
+ { 16, 0, 0, 0, PPC_OPERAND_SIGNED },
+
+ /* The SI field in a D form instruction when we accept a wide range
+ of positive values. */
+#define SISIGNOPT (50)
+ { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+
+ /* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+#define SPR (51)
+#define SPR_MASK (0x3ff << 11)
+ { 10, 11, insert_spr, extract_spr, 0 },
+
+ /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */
+#define SPRBAT (52)
+#define SPRBAT_MASK (0x3 << 17)
+ { 2, 17, 0, 0, 0 },
+
+ /* The SPRG register number in an XFX form m[ft]sprg instruction. */
+#define SPRG (53)
+#define SPRG_MASK (0x3 << 16)
+ { 2, 16, 0, 0, 0 },
+
+ /* The SR field in an X form instruction. */
+#define SR (54)
+ { 4, 16, 0, 0, 0 },
+
+ /* The SV field in a POWER SC form instruction. */
+#define SV (55)
+ { 14, 2, 0, 0, 0 },
+
+ /* The TBR field in an XFX form instruction. This is like the SPR
+ field, but it is optional. */
+#define TBR (56)
+ { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL },
+
+ /* The TO field in a D or X form instruction. */
+#define TO (57)
+#define TO_MASK (0x1f << 21)
+ { 5, 21, 0, 0, 0 },
+
+ /* The U field in an X form instruction. */
+#define U (58)
+ { 4, 12, 0, 0, 0 },
+
+ /* The UI field in a D form instruction. */
+#define UI (59)
+ { 16, 0, 0, 0, 0 },
+};
+
+/* The functions used to insert and extract complicated operands. */
+
+/* The BA field in an XL form instruction when it must be the same as
+ the BT field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BT field into the BA field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bat (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static long
+extract_bat (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BB field in an XL form instruction when it must be the same as
+ the BA field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BA field into the BB field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bba (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 16) & 0x1f) << 11);
+}
+
+static long
+extract_bba (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BD field in a B form instruction. The lower two bits are
+ forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bd (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_bd (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the - modifier is used.
+ This modifier means that the branch is not expected to be taken.
+ We must set the y bit of the BO field to 1 if the offset is
+ negative. When extracting, we require that the y bit be 1 and that
+ the offset be positive, since if the y bit is 0 we just want to
+ print the normal form of the instruction. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdm (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if ((value & 0x8000) != 0)
+ insn |= 1 << 21;
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdm (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn & (1 << 21)) == 0
+ || (insn & (1 << 15)) == 0))
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the + modifier is used.
+ This is like BDM, above, except that the branch is expected to be
+ taken. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdp (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if ((value & 0x8000) == 0)
+ insn |= 1 << 21;
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdp (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn & (1 << 21)) == 0
+ || (insn & (1 << 15)) != 0))
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* Check for legal values of a BO field. */
+
+static int
+valid_bo (int32_t value)
+{
+ /* Certain encodings have bits that are required to be zero. These
+ are (z must be zero, y may be anything):
+ 001zy
+ 011zy
+ 1z00y
+ 1z01y
+ 1z1zz
+ */
+ switch (value & 0x14)
+ {
+ default:
+ case 0:
+ return 1;
+ case 0x4:
+ return (value & 0x2) == 0;
+ case 0x10:
+ return (value & 0x8) == 0;
+ case 0x14:
+ return value == 0x14;
+ }
+}
+
+/* The BO field in a B form instruction. Warn about attempts to set
+ the field to an illegal value. */
+
+static unsigned long
+insert_bo (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **) NULL
+ && ! valid_bo (value))
+ *errmsg = "invalid conditional option";
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_bo (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ int32_t value;
+
+ value = (insn >> 21) & 0x1f;
+ if (invalid != (int *) NULL
+ && ! valid_bo (value))
+ *invalid = 1;
+ return value;
+}
+
+/* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. When
+ extracting it, we force it to be even. */
+
+static unsigned long
+insert_boe (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **) NULL)
+ {
+ if (! valid_bo (value))
+ *errmsg = "invalid conditional option";
+ else if ((value & 1) != 0)
+ *errmsg = "attempt to set y bit when using + or - modifier";
+ }
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_boe (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ int32_t value;
+
+ value = (insn >> 21) & 0x1f;
+ if (invalid != (int *) NULL
+ && ! valid_bo (value))
+ *invalid = 1;
+ return value & 0x1e;
+}
+
+/* The DS field in a DS form instruction. This is like D, but the
+ lower two bits are forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_ds (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_ds (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The LI field in an I form instruction. The lower two bits are
+ forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_li (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (value & 0x3fffffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_li (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if ((insn & 0x2000000) != 0)
+ return (insn & 0x3fffffc) - 0x4000000;
+ else
+ return insn & 0x3fffffc;
+}
+
+/* The MB and ME fields in an M form instruction expressed as a single
+ operand which is itself a bitmask. The extraction function always
+ marks it as invalid, since we never want to recognize an
+ instruction which uses a field of this type. */
+
+static unsigned long
+insert_mbe (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ uint32_t uval;
+ int mb, me;
+
+ uval = value;
+
+ if (uval == 0)
+ {
+ if (errmsg != (const char **) NULL)
+ *errmsg = "illegal bitmask";
+ return insn;
+ }
+
+ me = 31;
+ while ((uval & 1) == 0)
+ {
+ uval >>= 1;
+ --me;
+ }
+
+ mb = me;
+ uval >>= 1;
+ while ((uval & 1) != 0)
+ {
+ uval >>= 1;
+ --mb;
+ }
+
+ if (uval != 0)
+ {
+ if (errmsg != (const char **) NULL)
+ *errmsg = "illegal bitmask";
+ }
+
+ return insn | (mb << 6) | (me << 1);
+}
+
+static long
+extract_mbe (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ long ret;
+ int mb, me;
+ int i;
+
+ if (invalid != (int *) NULL)
+ *invalid = 1;
+
+ ret = 0;
+ mb = (insn >> 6) & 0x1f;
+ me = (insn >> 1) & 0x1f;
+ for (i = mb; i < me; i++)
+ ret |= 1 << (31 - i);
+ return ret;
+}
+
+/* The MB or ME field in an MD or MDS form instruction. The high bit
+ is wrapped to the low end. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_mb6 (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 6) | (value & 0x20);
+}
+
+/*ARGSUSED*/
+static long
+extract_mb6 (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ return ((insn >> 6) & 0x1f) | (insn & 0x20);
+}
+
+/* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+
+static unsigned long
+insert_nb (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (value < 0 || value > 32)
+ *errmsg = "value out of range";
+ if (value == 32)
+ value = 0;
+ return insn | ((value & 0x1f) << 11);
+}
+
+/*ARGSUSED*/
+static long
+extract_nb (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ long ret;
+
+ ret = (insn >> 11) & 0x1f;
+ if (ret == 0)
+ ret = 32;
+ return ret;
+}
+
+/* The NSI field in a D form instruction. This is the same as the SI
+ field, only negated. The extraction function always marks it as
+ invalid, since we never want to recognize an instruction which uses
+ a field of this type. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_nsi (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | ((- value) & 0xffff);
+}
+
+static long
+extract_nsi (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL)
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return - ((insn & 0xffff) - 0x10000);
+ else
+ return - (insn & 0xffff);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+
+static unsigned long
+insert_ral (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (value == 0
+ || value == ((insn >> 21) & 0x1f))
+ *errmsg = "invalid register operand when updating";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in an lmw instruction, which has special value
+ restrictions. */
+
+static unsigned long
+insert_ram (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (value >= ((insn >> 21) & 0x1f))
+ *errmsg = "index register in load range";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+
+static unsigned long
+insert_ras (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (value == 0)
+ *errmsg = "invalid register operand when updating";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. This operand is marked FAKE. The insertion
+ function just copies the BT field into the BA field, and the
+ extraction function just checks that the fields are the same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_rbs (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 21) & 0x1f) << 11);
+}
+
+static long
+extract_rbs (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The SH field in an MD form instruction. This is split. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_sh6 (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
+}
+
+/*ARGSUSED*/
+static long
+extract_sh6 (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
+}
+
+/* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+
+static unsigned long
+insert_spr (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_spr (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+}
+
+/* The TBR field in an XFX instruction. This is just like SPR, but it
+ is optional. When TBR is omitted, it must be inserted as 268 (the
+ magic number of the TB register). These functions treat 0
+ (indicating an omitted optional operand) as 268. This means that
+ ``mftb 4,0'' is not handled correctly. This does not matter very
+ much, since the architecture manual does not define mftb as
+ accepting any values other than 268 or 269. */
+
+#define TB (268)
+
+static unsigned long
+insert_tbr (insn, value, errmsg)
+ uint32_t insn;
+ int32_t value;
+ const char **errmsg;
+{
+ if (value == 0)
+ value = TB;
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_tbr (insn, invalid)
+ uint32_t insn;
+ int *invalid;
+{
+ long ret;
+
+ ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+ if (ret == TB)
+ ret = 0;
+ return ret;
+}
+
+/* Macros used to form opcodes. */
+
+/* The main opcode. */
+#define OP(x) (((x) & 0x3f) << 26)
+#define OP_MASK OP (0x3f)
+
+/* The main opcode combined with a trap code in the TO field of a D
+ form instruction. Used for extended mnemonics for the trap
+ instructions. */
+#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21))
+#define OPTO_MASK (OP_MASK | TO_MASK)
+
+/* The main opcode combined with a comparison size bit in the L field
+ of a D form or X form instruction. Used for extended mnemonics for
+ the comparison instructions. */
+#define OPL(x,l) (OP (x) | (((l) & 1) << 21))
+#define OPL_MASK OPL (0x3f,1)
+
+/* An A form instruction. */
+#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1))
+#define A_MASK A (0x3f, 0x1f, 1)
+
+/* An A_MASK with the FRB field fixed. */
+#define AFRB_MASK (A_MASK | FRB_MASK)
+
+/* An A_MASK with the FRC field fixed. */
+#define AFRC_MASK (A_MASK | FRC_MASK)
+
+/* An A_MASK with the FRA and FRC fields fixed. */
+#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK)
+
+/* A B form instruction. */
+#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1))
+#define B_MASK B (0x3f, 1, 1)
+
+/* A B form instruction setting the BO field. */
+#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21))
+#define BBO_MASK BBO (0x3f, 0x1f, 1, 1)
+
+/* A BBO_MASK with the y bit of the BO field removed. This permits
+ matching a conditional branch regardless of the setting of the y
+ bit. */
+#define Y_MASK (1 << 21)
+#define BBOY_MASK (BBO_MASK &~ Y_MASK)
+
+/* A B form instruction setting the BO field and the condition bits of
+ the BI field. */
+#define BBOCB(op, bo, cb, aa, lk) \
+ (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16))
+#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1)
+
+/* A BBOCB_MASK with the y bit of the BO field removed. */
+#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK)
+
+/* A BBOYCB_MASK in which the BI field is fixed. */
+#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
+
+/* The main opcode mask with the RA field clear. */
+#define DRA_MASK (OP_MASK | RA_MASK)
+
+/* A DS form instruction. */
+#define DSO(op, xop) (OP (op) | ((xop) & 0x3))
+#define DS_MASK DSO (0x3f, 3)
+
+/* An M form instruction. */
+#define M(op, rc) (OP (op) | ((rc) & 1))
+#define M_MASK M (0x3f, 1)
+
+/* An M form instruction with the ME field specified. */
+#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1))
+
+/* An M_MASK with the MB and ME fields fixed. */
+#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK)
+
+/* An M_MASK with the SH and ME fields fixed. */
+#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK)
+
+/* An MD form instruction. */
+#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1))
+#define MD_MASK MD (0x3f, 0x7, 1)
+
+/* An MD_MASK with the MB field fixed. */
+#define MDMB_MASK (MD_MASK | MB6_MASK)
+
+/* An MD_MASK with the SH field fixed. */
+#define MDSH_MASK (MD_MASK | SH6_MASK)
+
+/* An MDS form instruction. */
+#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1))
+#define MDS_MASK MDS (0x3f, 0xf, 1)
+
+/* An MDS_MASK with the MB field fixed. */
+#define MDSMB_MASK (MDS_MASK | MB6_MASK)
+
+/* An SC form instruction. */
+#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1))
+#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1)
+
+/* An X form instruction. */
+#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An X form instruction with the RC bit specified. */
+#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1))
+
+/* The mask for an X form instruction. */
+#define X_MASK XRC (0x3f, 0x3ff, 1)
+
+/* An X_MASK with the RA field fixed. */
+#define XRA_MASK (X_MASK | RA_MASK)
+
+/* An X_MASK with the RB field fixed. */
+#define XRB_MASK (X_MASK | RB_MASK)
+
+/* An X_MASK with the RT field fixed. */
+#define XRT_MASK (X_MASK | RT_MASK)
+
+/* An X_MASK with the RA and RB fields fixed. */
+#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK)
+
+/* An X_MASK with the RT and RA fields fixed. */
+#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK)
+
+/* An X form comparison instruction. */
+#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21))
+
+/* The mask for an X form comparison instruction. */
+#define XCMP_MASK (X_MASK | (1 << 22))
+
+/* The mask for an X form comparison instruction with the L field
+ fixed. */
+#define XCMPL_MASK (XCMP_MASK | (1 << 21))
+
+/* An X form trap instruction with the TO field specified. */
+#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21))
+#define XTO_MASK (X_MASK | TO_MASK)
+
+/* An XFL form instruction. */
+#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1))
+#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16))
+
+/* An XL form instruction with the LK field set to 0. */
+#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An XL form instruction which uses the LK field. */
+#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1))
+
+/* The mask for an XL form instruction. */
+#define XL_MASK XLLK (0x3f, 0x3ff, 1)
+
+/* An XL form instruction which explicitly sets the BO field. */
+#define XLO(op, bo, xop, lk) \
+ (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21))
+#define XLO_MASK (XL_MASK | BO_MASK)
+
+/* An XL form instruction which explicitly sets the y bit of the BO
+ field. */
+#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21))
+#define XLYLK_MASK (XL_MASK | Y_MASK)
+
+/* An XL form instruction which sets the BO field and the condition
+ bits of the BI field. */
+#define XLOCB(op, bo, cb, xop, lk) \
+ (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16))
+#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1)
+
+/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */
+#define XLBB_MASK (XL_MASK | BB_MASK)
+#define XLYBB_MASK (XLYLK_MASK | BB_MASK)
+#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK)
+
+/* An XL_MASK with the BO and BB fields fixed. */
+#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK)
+
+/* An XL_MASK with the BO, BI and BB fields fixed. */
+#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK)
+
+/* An XO form instruction. */
+#define XO(op, xop, oe, rc) \
+ (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1))
+#define XO_MASK XO (0x3f, 0x1ff, 1, 1)
+
+/* An XO_MASK with the RB field fixed. */
+#define XORB_MASK (XO_MASK | RB_MASK)
+
+/* An XS form instruction. */
+#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1))
+#define XS_MASK XS (0x3f, 0x1ff, 1)
+
+/* A mask for the FXM version of an XFX form instruction. */
+#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11))
+
+/* An XFX form instruction with the FXM field filled in. */
+#define XFXM(op, xop, fxm) \
+ (X ((op), (xop)) | (((fxm) & 0xff) << 12))
+
+/* An XFX form instruction with the SPR field filled in. */
+#define XSPR(op, xop, spr) \
+ (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6))
+#define XSPR_MASK (X_MASK | SPR_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRBAT field. */
+#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRG field. */
+#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK)
+
+/* The BO encodings used in extended conditional branch mnemonics. */
+#define BODNZF (0x0)
+#define BODNZFP (0x1)
+#define BODZF (0x2)
+#define BODZFP (0x3)
+#define BOF (0x4)
+#define BOFP (0x5)
+#define BODNZT (0x8)
+#define BODNZTP (0x9)
+#define BODZT (0xa)
+#define BODZTP (0xb)
+#define BOT (0xc)
+#define BOTP (0xd)
+#define BODNZ (0x10)
+#define BODNZP (0x11)
+#define BODZ (0x12)
+#define BODZP (0x13)
+#define BOU (0x14)
+
+/* The BI condition bit encodings used in extended conditional branch
+ mnemonics. */
+#define CBLT (0)
+#define CBGT (1)
+#define CBEQ (2)
+#define CBSO (3)
+
+/* The TO encodings used in extended trap mnemonics. */
+#define TOLGT (0x1)
+#define TOLLT (0x2)
+#define TOEQ (0x4)
+#define TOLGE (0x5)
+#define TOLNL (0x5)
+#define TOLLE (0x6)
+#define TOLNG (0x6)
+#define TOGT (0x8)
+#define TOGE (0xc)
+#define TONL (0xc)
+#define TOLT (0x10)
+#define TOLE (0x14)
+#define TONG (0x14)
+#define TONE (0x18)
+#define TOU (0x1f)
+
+/* Smaller names for the flags so each entry in the opcodes table will
+ fit on a single line. */
+#undef PPC
+#define PPC PPC_OPCODE_PPC
+#define POWER PPC_OPCODE_POWER
+#define POWER2 PPC_OPCODE_POWER2
+#define B32 PPC_OPCODE_32
+#define B64 PPC_OPCODE_64
+#define M601 PPC_OPCODE_601
+
+/* The opcode table.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK FLAGS { OPERANDS }
+
+ NAME is the name of the instruction.
+ OPCODE is the instruction opcode.
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+ FLAGS are flags indicated what processors support the instruction.
+ OPERANDS is the list of operands.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches, so this table is sorted to put more
+ specific instructions before more general instructions. It is also
+ sorted by major opcode. */
+
+const struct powerpc_opcode powerpc_opcodes[] = {
+{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } },
+
+{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } },
+{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } },
+{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } },
+{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } },
+{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } },
+{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } },
+{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } },
+{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } },
+{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } },
+{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } },
+{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } },
+{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } },
+{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } },
+{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } },
+{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } },
+{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } },
+{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } },
+{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } },
+{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } },
+{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } },
+{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } },
+{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } },
+{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } },
+{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } },
+{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } },
+{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } },
+{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } },
+{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } },
+{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } },
+{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } },
+
+{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } },
+{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } },
+
+{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } },
+{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } },
+
+{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } },
+
+{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } },
+{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } },
+
+{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } },
+{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } },
+
+{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } },
+{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } },
+{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } },
+{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } },
+{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "li", OP(14), DRA_MASK, PPC, { RT, SI } },
+{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } },
+{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } },
+{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } },
+{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } },
+{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } },
+
+{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } },
+{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } },
+{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } },
+{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } },
+{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } },
+{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } },
+{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } },
+{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } },
+{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } },
+{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } },
+{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } },
+{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } },
+{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } },
+{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } },
+{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } },
+{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } },
+{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } },
+{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } },
+{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } },
+{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } },
+{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } },
+{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } },
+{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } },
+{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } },
+{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } },
+{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } },
+{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } },
+{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } },
+{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } },
+{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } },
+{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } },
+{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } },
+{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } },
+{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } },
+{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } },
+{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } },
+{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } },
+{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } },
+{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } },
+{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } },
+{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } },
+{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } },
+{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } },
+{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } },
+{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } },
+
+{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } },
+{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } },
+{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } },
+{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } },
+{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } },
+
+{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } },
+{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } },
+{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } },
+{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } },
+
+{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } },
+{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } },
+{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } },
+{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } },
+{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } },
+{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } },
+{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } },
+{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } },
+{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } },
+{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } },
+
+{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } },
+{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } },
+{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } },
+
+{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } },
+
+{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } },
+{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } },
+
+{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } },
+{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } },
+{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } },
+{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } },
+{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } },
+{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } },
+{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } },
+
+{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } },
+{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } },
+{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } },
+{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } },
+{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } },
+{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } },
+
+{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } },
+{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } },
+{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } },
+{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } },
+{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } },
+{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } },
+
+{ "nop", OP(24), 0xffffffff, PPC, { 0 } },
+{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } },
+{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } },
+{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } },
+{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } },
+{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } },
+{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } },
+{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } },
+{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } },
+{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } },
+{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } },
+
+{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } },
+{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } },
+{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } },
+{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } },
+
+{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } },
+{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } },
+
+{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } },
+{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } },
+{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } },
+
+{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } },
+{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } },
+{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } },
+{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } },
+{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } },
+{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } },
+{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } },
+{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } },
+{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } },
+{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } },
+{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } },
+{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } },
+{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } },
+{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } },
+{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } },
+{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } },
+{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } },
+{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } },
+{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } },
+{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } },
+{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } },
+{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } },
+{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } },
+{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } },
+{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } },
+{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } },
+{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } },
+{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } },
+{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } },
+{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } },
+{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } },
+
+{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } },
+
+{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } },
+
+{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } },
+{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } },
+
+{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } },
+{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } },
+{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } },
+{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } },
+{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } },
+
+{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } },
+{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } },
+{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } },
+
+{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } },
+
+{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } },
+
+{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } },
+{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } },
+
+{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } },
+{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } },
+
+{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } },
+
+{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } },
+
+{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } },
+
+{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } },
+
+{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } },
+
+{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }},
+{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } },
+
+{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } },
+
+{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } },
+
+{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } },
+
+{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } },
+{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } },
+
+{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } },
+
+{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } },
+{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } },
+
+{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } },
+
+{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } },
+
+{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } },
+{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } },
+
+{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } },
+
+{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } },
+
+{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } },
+{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } },
+
+{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } },
+
+{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } },
+{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } },
+
+{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } },
+
+{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } },
+
+{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } },
+{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } },
+{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } },
+{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } },
+{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } },
+{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } },
+{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } },
+{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } },
+{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } },
+{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } },
+{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } },
+
+{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } },
+
+{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } },
+
+{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } },
+
+{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } },
+{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } },
+
+{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } },
+
+{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } },
+
+{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } },
+
+{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } },
+
+{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } },
+
+{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } },
+
+{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } },
+{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } },
+{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } },
+{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } },
+{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } },
+{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } },
+{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } },
+{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } },
+{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } },
+{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } },
+
+{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } },
+
+{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } },
+
+{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } },
+
+{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } },
+
+{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } },
+
+{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } },
+
+{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } },
+{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } },
+
+{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } },
+{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } },
+
+{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } },
+
+{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } },
+{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } },
+
+{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } },
+
+{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } },
+
+{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } },
+{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } },
+
+{ "sync", X(31,598), 0xffffffff, PPC, { 0 } },
+{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } },
+
+{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } },
+
+{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } },
+
+{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } },
+
+{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } },
+
+{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } },
+
+{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } },
+{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } },
+
+{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } },
+{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } },
+
+{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } },
+
+{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } },
+
+{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } },
+{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } },
+
+{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } },
+
+{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } },
+
+{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } },
+{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } },
+
+{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } },
+{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } },
+{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } },
+{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } },
+
+{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } },
+
+{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } },
+{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } },
+{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } },
+{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } },
+
+{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} },
+{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} },
+
+{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } },
+
+{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } },
+
+{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } },
+
+{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } },
+{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } },
+
+{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+
+{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } },
+{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } },
+
+{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } },
+{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } },
+
+{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } },
+{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } },
+
+{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } },
+{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } },
+
+{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } },
+
+{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } },
+
+{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } },
+
+{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } },
+
+{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } },
+{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } },
+
+{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } },
+{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } },
+
+{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } },
+
+{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } },
+
+{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } },
+
+{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } },
+
+{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } },
+
+{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } },
+
+{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } },
+
+{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } },
+
+{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } },
+
+{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } },
+
+{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } },
+
+{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } },
+
+{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } },
+
+{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+
+{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } },
+
+{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } },
+
+{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } },
+
+{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } },
+{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } },
+
+{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } },
+{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } },
+
+{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } },
+
+{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } },
+{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } },
+
+{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } },
+{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } },
+
+{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+
+{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } },
+{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } },
+
+{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } },
+{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } },
+
+{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+};
+
+const int powerpc_num_opcodes =
+ sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
+
+/* The macro table. This is only used by the assembler. */
+
+const struct powerpc_macro powerpc_macros[] = {
+{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" },
+{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" },
+{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" },
+{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" },
+{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" },
+{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" },
+{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" },
+{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" },
+{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" },
+{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" },
+{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" },
+{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" },
+{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" },
+{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" },
+{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" },
+{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" },
+
+{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" },
+{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" },
+{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" },
+{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" },
+{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" },
+{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"},
+{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" },
+{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" },
+{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" },
+{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" },
+{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" },
+{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" },
+{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" },
+{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" },
+{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" },
+{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" },
+{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" },
+{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" },
+{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" },
+{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" },
+
+};
+
+const int powerpc_num_macros =
+ sizeof (powerpc_macros) / sizeof (powerpc_macros[0]);
+
+static int
+print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr,
+ int dialect);
+
+/* Print a big endian PowerPC instruction. For convenience, also
+ disassemble instructions supported by the Motorola PowerPC 601. */
+
+int print_insn_ppc (bfd_vma pc, disassemble_info *info)
+{
+ uint32_t opc;
+ bfd_byte buf[4];
+
+ (*info->read_memory_func)(pc, buf, 4, info);
+ if (info->endian == BFD_ENDIAN_BIG)
+ opc = bfd_getb32(buf);
+ else
+ opc = bfd_getl32(buf);
+ if (info->mach == bfd_mach_ppc64) {
+ return print_insn_powerpc (info, opc, pc,
+ PPC | B64);
+ } else {
+ return print_insn_powerpc (info, opc, pc,
+ PPC | B32 | M601);
+ }
+}
+
+/* Print a PowerPC or POWER instruction. */
+
+static int
+print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr,
+ int dialect)
+{
+ const struct powerpc_opcode *opcode;
+ const struct powerpc_opcode *opcode_end;
+ uint32_t op;
+
+ /* Get the major opcode of the instruction. */
+ op = PPC_OP (insn);
+
+ /* Find the first match in the opcode table. We could speed this up
+ a bit by doing a binary search on the major opcode. */
+ opcode_end = powerpc_opcodes + powerpc_num_opcodes;
+ for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+ {
+ uint32_t table_op;
+ const unsigned char *opindex;
+ const struct powerpc_operand *operand;
+ int invalid;
+ int need_comma;
+ int need_paren;
+
+ table_op = PPC_OP (opcode->opcode);
+ if (op < table_op)
+ break;
+ if (op > table_op)
+ continue;
+
+ if ((insn & opcode->mask) != opcode->opcode
+ || (opcode->flags & dialect) == 0)
+ continue;
+
+ /* Make two passes over the operands. First see if any of them
+ have extraction functions, and, if they do, make sure the
+ instruction is valid. */
+ invalid = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ operand = powerpc_operands + *opindex;
+ if (operand->extract)
+ (*operand->extract) (insn, &invalid);
+ }
+ if (invalid)
+ continue;
+
+ /* The instruction is valid. */
+ (*info->fprintf_func)(info->stream, "%s", opcode->name);
+ if (opcode->operands[0] != 0)
+ (*info->fprintf_func)(info->stream, "\t");
+
+ /* Now extract and print the operands. */
+ need_comma = 0;
+ need_paren = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ int32_t value;
+
+ operand = powerpc_operands + *opindex;
+
+ /* Operands that are marked FAKE are simply ignored. We
+ already made sure that the extract function considered
+ the instruction to be valid. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ continue;
+
+ /* Extract the value from the instruction. */
+ if (operand->extract)
+ value = (*operand->extract) (insn, (int *) 0);
+ else
+ {
+ value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0
+ && (value & (1 << (operand->bits - 1))) != 0)
+ value -= 1 << operand->bits;
+ }
+
+ /* If the operand is optional, and the value is zero, don't
+ print anything. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && (operand->flags & PPC_OPERAND_NEXT) == 0
+ && value == 0)
+ continue;
+
+ if (need_comma)
+ {
+ (*info->fprintf_func)(info->stream, ",");
+ need_comma = 0;
+ }
+
+ /* Print the operand as directed by the flags. */
+ if ((operand->flags & PPC_OPERAND_GPR) != 0)
+ (*info->fprintf_func)(info->stream, "r%d", value);
+ else if ((operand->flags & PPC_OPERAND_FPR) != 0)
+ (*info->fprintf_func)(info->stream, "f%d", value);
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
+ (*info->fprintf_func)(info->stream, "%08X", memaddr + value);
+ else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+ (*info->fprintf_func)(info->stream, "%08X", value & 0xffffffff);
+ else if ((operand->flags & PPC_OPERAND_CR) == 0
+ || (dialect & PPC_OPCODE_PPC) == 0)
+ (*info->fprintf_func)(info->stream, "%d", value);
+ else
+ {
+ if (operand->bits == 3)
+ (*info->fprintf_func)(info->stream, "cr%d", value);
+ else
+ {
+ static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
+ int cr;
+ int cc;
+
+ cr = value >> 2;
+ if (cr != 0)
+ (*info->fprintf_func)(info->stream, "4*cr%d", cr);
+ cc = value & 3;
+ if (cc != 0)
+ {
+ if (cr != 0)
+ (*info->fprintf_func)(info->stream, "+");
+ (*info->fprintf_func)(info->stream, "%s", cbnames[cc]);
+ }
+ }
+ }
+
+ if (need_paren)
+ {
+ (*info->fprintf_func)(info->stream, ")");
+ need_paren = 0;
+ }
+
+ if ((operand->flags & PPC_OPERAND_PARENS) == 0)
+ need_comma = 1;
+ else
+ {
+ (*info->fprintf_func)(info->stream, "(");
+ need_paren = 1;
+ }
+ }
+
+ /* We have found and printed an instruction; return. */
+ return 4;
+ }
+
+ /* We could not find a match. */
+ (*info->fprintf_func)(info->stream, ".long 0x%x", insn);
+
+ return 4;
+}
diff --git a/ppc.ld b/ppc.ld
new file mode 100644
index 0000000..d0a0dcb
--- /dev/null
+++ b/ppc.ld
@@ -0,0 +1,140 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
+OUTPUT_ARCH(powerpc)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/qemu-binfmt-conf.sh b/qemu-binfmt-conf.sh
new file mode 100644
index 0000000..bd278d9
--- /dev/null
+++ b/qemu-binfmt-conf.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# enable automatic i386/ARM/SPARC/PPC program execution by the kernel
+
+# load the binfmt_misc module
+/sbin/modprobe binfmt_misc
+
+# probe cpu type
+cpu=`uname -m`
+case "$cpu" in
+ i386|i486|i586|i686|i86pc|BePC)
+ cpu="i386"
+ ;;
+ "Power Macintosh"|ppc|ppc64)
+ cpu="ppc"
+ ;;
+ armv4l)
+ cpu="arm"
+ ;;
+esac
+
+# register the interpreter for each cpu except for the native one
+if [ $cpu != "i386" ] ; then
+ echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
+ echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "arm" ] ; then
+ echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register
+ echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "sparc" ] ; then
+ echo ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "ppc" ] ; then
+ echo ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "mips" ] ; then
+ echo ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register
+fi
diff --git a/qemu-doc.texi b/qemu-doc.texi
new file mode 100644
index 0000000..1e49ccf
--- /dev/null
+++ b/qemu-doc.texi
@@ -0,0 +1,1892 @@
+\input texinfo @c -*- texinfo -*-
+@c %**start of header
+@setfilename qemu-doc.info
+@settitle QEMU CPU Emulator User Documentation
+@exampleindent 0
+@paragraphindent 0
+@c %**end of header
+
+@iftex
+@titlepage
+@sp 7
+@center @titlefont{QEMU CPU Emulator}
+@sp 1
+@center @titlefont{User Documentation}
+@sp 3
+@end titlepage
+@end iftex
+
+@ifnottex
+@node Top
+@top
+
+@menu
+* Introduction::
+* Installation::
+* QEMU PC System emulator::
+* QEMU System emulator for non PC targets::
+* QEMU Linux User space emulator::
+* compilation:: Compilation from the sources
+* Index::
+@end menu
+@end ifnottex
+
+@contents
+
+@node Introduction
+@chapter Introduction
+
+@menu
+* intro_features:: Features
+@end menu
+
+@node intro_features
+@section Features
+
+QEMU is a FAST! processor emulator using dynamic translation to
+achieve good emulation speed.
+
+QEMU has two operating modes:
+
+@itemize @minus
+
+@item
+Full system emulation. In this mode, QEMU emulates a full system (for
+example a PC), including one or several processors and various
+peripherals. It can be used to launch different Operating Systems
+without rebooting the PC or to debug system code.
+
+@item
+User mode emulation (Linux host only). In this mode, QEMU can launch
+Linux processes compiled for one CPU on another CPU. It can be used to
+launch the Wine Windows API emulator (@url{http://www.winehq.org}) or
+to ease cross-compilation and cross-debugging.
+
+@end itemize
+
+QEMU can run without an host kernel driver and yet gives acceptable
+performance.
+
+For system emulation, the following hardware targets are supported:
+@itemize
+@item PC (x86 or x86_64 processor)
+@item ISA PC (old style PC without PCI bus)
+@item PREP (PowerPC processor)
+@item G3 BW PowerMac (PowerPC processor)
+@item Mac99 PowerMac (PowerPC processor, in progress)
+@item Sun4m (32-bit Sparc processor)
+@item Sun4u (64-bit Sparc processor, in progress)
+@item Malta board (32-bit MIPS processor)
+@item ARM Integrator/CP (ARM926E or 1026E processor)
+@item ARM Versatile baseboard (ARM926E)
+@end itemize
+
+For user emulation, x86, PowerPC, ARM, MIPS, and Sparc32/64 CPUs are supported.
+
+@node Installation
+@chapter Installation
+
+If you want to compile QEMU yourself, see @ref{compilation}.
+
+@menu
+* install_linux:: Linux
+* install_windows:: Windows
+* install_mac:: Macintosh
+@end menu
+
+@node install_linux
+@section Linux
+
+If a precompiled package is available for your distribution - you just
+have to install it. Otherwise, see @ref{compilation}.
+
+@node install_windows
+@section Windows
+
+Download the experimental binary installer at
+@url{http://www.free.oszoo.org/@/download.html}.
+
+@node install_mac
+@section Mac OS X
+
+Download the experimental binary installer at
+@url{http://www.free.oszoo.org/@/download.html}.
+
+@node QEMU PC System emulator
+@chapter QEMU PC System emulator
+
+@menu
+* pcsys_introduction:: Introduction
+* pcsys_quickstart:: Quick Start
+* sec_invocation:: Invocation
+* pcsys_keys:: Keys
+* pcsys_monitor:: QEMU Monitor
+* disk_images:: Disk Images
+* pcsys_network:: Network emulation
+* direct_linux_boot:: Direct Linux Boot
+* pcsys_usb:: USB emulation
+* gdb_usage:: GDB usage
+* pcsys_os_specific:: Target OS specific information
+@end menu
+
+@node pcsys_introduction
+@section Introduction
+
+@c man begin DESCRIPTION
+
+The QEMU PC System emulator simulates the
+following peripherals:
+
+@itemize @minus
+@item
+i440FX host PCI bridge and PIIX3 PCI to ISA bridge
+@item
+Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA
+extensions (hardware level, including all non standard modes).
+@item
+PS/2 mouse and keyboard
+@item
+2 PCI IDE interfaces with hard disk and CD-ROM support
+@item
+Floppy disk
+@item
+NE2000 PCI network adapters
+@item
+Serial ports
+@item
+Creative SoundBlaster 16 sound card
+@item
+ENSONIQ AudioPCI ES1370 sound card
+@item
+Adlib(OPL2) - Yamaha YM3812 compatible chip
+@item
+PCI UHCI USB controller and a virtual USB hub.
+@end itemize
+
+SMP is supported with up to 255 CPUs.
+
+Note that adlib is only available when QEMU was configured with
+-enable-adlib
+
+QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL
+VGA BIOS.
+
+QEMU uses YM3812 emulation by Tatsuyuki Satoh.
+
+@c man end
+
+@node pcsys_quickstart
+@section Quick Start
+
+Download and uncompress the linux image (@file{linux.img}) and type:
+
+@example
+qemu linux.img
+@end example
+
+Linux should boot and give you a prompt.
+
+@node sec_invocation
+@section Invocation
+
+@example
+@c man begin SYNOPSIS
+usage: qemu [options] [disk_image]
+@c man end
+@end example
+
+@c man begin OPTIONS
+@var{disk_image} is a raw hard disk image for IDE hard disk 0.
+
+General options:
+@table @option
+@item -M machine
+Select the emulated machine (@code{-M ?} for list)
+
+@item -fda file
+@item -fdb file
+Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can
+use the host floppy by using @file{/dev/fd0} as filename.
+
+@item -hda file
+@item -hdb file
+@item -hdc file
+@item -hdd file
+Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}).
+
+@item -cdrom file
+Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and and
+@option{-cdrom} at the same time). You can use the host CD-ROM by
+using @file{/dev/cdrom} as filename.
+
+@item -boot [a|c|d]
+Boot on floppy (a), hard disk (c) or CD-ROM (d). Hard disk boot is
+the default.
+
+@item -snapshot
+Write to temporary files instead of disk image files. In this case,
+the raw disk image you use is not written back. You can however force
+the write back by pressing @key{C-a s} (@pxref{disk_images}).
+
+@item -no-fd-bootchk
+Disable boot signature checking for floppy disks in Bochs BIOS. It may
+be needed to boot from old floppy disks.
+
+@item -m megs
+Set virtual RAM size to @var{megs} megabytes. Default is 128 MB.
+
+@item -smp n
+Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255
+CPUs are supported.
+
+@item -nographic
+
+Normally, QEMU uses SDL to display the VGA output. With this option,
+you can totally disable graphical output so that QEMU is a simple
+command line application. The emulated serial port is redirected on
+the console. Therefore, you can still use QEMU to debug a Linux kernel
+with a serial console.
+
+@item -vnc d
+
+Normally, QEMU uses SDL to display the VGA output. With this option,
+you can have QEMU listen on VNC display @var{d} and redirect the VGA
+display over the VNC session. It is very useful to enable the usb
+tablet device when using this option (option @option{-usbdevice
+tablet}). When using the VNC display, you must use the @option{-k}
+option to set the keyboard layout.
+
+@item -k language
+
+Use keyboard layout @var{language} (for example @code{fr} for
+French). This option is only needed where it is not easy to get raw PC
+keycodes (e.g. on Macs, with some X11 servers or with a VNC
+display). You don't normally need to use it on PC/Linux or PC/Windows
+hosts.
+
+The available layouts are:
+@example
+ar de-ch es fo fr-ca hu ja mk no pt-br sv
+da en-gb et fr fr-ch is lt nl pl ru th
+de en-us fi fr-be hr it lv nl-be pt sl tr
+@end example
+
+The default is @code{en-us}.
+
+@item -audio-help
+
+Will show the audio subsystem help: list of drivers, tunable
+parameters.
+
+@item -soundhw card1,card2,... or -soundhw all
+
+Enable audio and selected sound hardware. Use ? to print all
+available sound hardware.
+
+@example
+qemu -soundhw sb16,adlib hda
+qemu -soundhw es1370 hda
+qemu -soundhw all hda
+qemu -soundhw ?
+@end example
+
+@item -localtime
+Set the real time clock to local time (the default is to UTC
+time). This option is needed to have correct date in MS-DOS or
+Windows.
+
+@item -full-screen
+Start in full screen.
+
+@item -pidfile file
+Store the QEMU process PID in @var{file}. It is useful if you launch QEMU
+from a script.
+
+@item -win2k-hack
+Use it when installing Windows 2000 to avoid a disk full bug. After
+Windows 2000 is installed, you no longer need this option (this option
+slows down the IDE transfers).
+
+@end table
+
+USB options:
+@table @option
+
+@item -usb
+Enable the USB driver (will be the default soon)
+
+@item -usbdevice devname
+Add the USB device @var{devname}. @xref{usb_devices}.
+@end table
+
+Network options:
+
+@table @option
+
+@item -net nic[,vlan=n][,macaddr=addr][,model=type]
+Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n}
+= 0 is the default). The NIC is currently an NE2000 on the PC
+target. Optionally, the MAC address can be changed. If no
+@option{-net} option is specified, a single NIC is created.
+Qemu can emulate several different models of network card. Valid values for
+@var{type} are @code{ne2k_pci}, @code{ne2k_isa}, @code{rtl8139},
+@code{smc91c111} and @code{lance}. Not all devices are supported on all
+targets.
+
+@item -net user[,vlan=n][,hostname=name]
+Use the user mode network stack which requires no administrator
+priviledge to run. @option{hostname=name} can be used to specify the client
+hostname reported by the builtin DHCP server.
+
+@item -net tap[,vlan=n][,fd=h][,ifname=name][,script=file]
+Connect the host TAP network interface @var{name} to VLAN @var{n} and
+use the network script @var{file} to configure it. The default
+network script is @file{/etc/qemu-ifup}. If @var{name} is not
+provided, the OS automatically provides one. @option{fd=h} can be
+used to specify the handle of an already opened host TAP interface. Example:
+
+@example
+qemu linux.img -net nic -net tap
+@end example
+
+More complicated example (two NICs, each one connected to a TAP device)
+@example
+qemu linux.img -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
+ -net nic,vlan=1 -net tap,vlan=1,ifname=tap1
+@end example
+
+
+@item -net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]
+
+Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual
+machine using a TCP socket connection. If @option{listen} is
+specified, QEMU waits for incoming connections on @var{port}
+(@var{host} is optional). @option{connect} is used to connect to
+another QEMU instance using the @option{listen} option. @option{fd=h}
+specifies an already opened TCP socket.
+
+Example:
+@example
+# launch a first QEMU instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,listen=:1234
+# connect the VLAN 0 of this instance to the VLAN 0
+# of the first instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \
+ -net socket,connect=127.0.0.1:1234
+@end example
+
+@item -net socket[,vlan=n][,fd=h][,mcast=maddr:port]
+
+Create a VLAN @var{n} shared with another QEMU virtual
+machines using a UDP multicast socket, effectively making a bus for
+every QEMU with same multicast address @var{maddr} and @var{port}.
+NOTES:
+@enumerate
+@item
+Several QEMU can be running on different hosts and share same bus (assuming
+correct multicast setup for these hosts).
+@item
+mcast support is compatible with User Mode Linux (argument @option{eth@var{N}=mcast}), see
+@url{http://user-mode-linux.sf.net}.
+@item Use @option{fd=h} to specify an already opened UDP multicast socket.
+@end enumerate
+
+Example:
+@example
+# launch one QEMU instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,mcast=230.0.0.1:1234
+# launch another QEMU instance on same "bus"
+qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \
+ -net socket,mcast=230.0.0.1:1234
+# launch yet another QEMU instance on same "bus"
+qemu linux.img -net nic,macaddr=52:54:00:12:34:58 \
+ -net socket,mcast=230.0.0.1:1234
+@end example
+
+Example (User Mode Linux compat.):
+@example
+# launch QEMU instance (note mcast address selected
+# is UML's default)
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,mcast=239.192.168.1:1102
+# launch UML
+/path/to/linux ubd0=/path/to/root_fs eth0=mcast
+@end example
+
+@item -net none
+Indicate that no network devices should be configured. It is used to
+override the default configuration (@option{-net nic -net user}) which
+is activated if no @option{-net} options are provided.
+
+@item -tftp prefix
+When using the user mode network stack, activate a built-in TFTP
+server. All filenames beginning with @var{prefix} can be downloaded
+from the host to the guest using a TFTP client. The TFTP client on the
+guest must be configured in binary mode (use the command @code{bin} of
+the Unix TFTP client). The host IP address on the guest is as usual
+10.0.2.2.
+
+@item -smb dir
+When using the user mode network stack, activate a built-in SMB
+server so that Windows OSes can access to the host files in @file{dir}
+transparently.
+
+In the guest Windows OS, the line:
+@example
+10.0.2.4 smbserver
+@end example
+must be added in the file @file{C:\WINDOWS\LMHOSTS} (for windows 9x/Me)
+or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000).
+
+Then @file{dir} can be accessed in @file{\\smbserver\qemu}.
+
+Note that a SAMBA server must be installed on the host OS in
+@file{/usr/sbin/smbd}. QEMU was tested succesfully with smbd version
+2.2.7a from the Red Hat 9 and version 3.0.10-1.fc3 from Fedora Core 3.
+
+@item -redir [tcp|udp]:host-port:[guest-host]:guest-port
+
+When using the user mode network stack, redirect incoming TCP or UDP
+connections to the host port @var{host-port} to the guest
+@var{guest-host} on guest port @var{guest-port}. If @var{guest-host}
+is not specified, its value is 10.0.2.15 (default address given by the
+built-in DHCP server).
+
+For example, to redirect host X11 connection from screen 1 to guest
+screen 0, use the following:
+
+@example
+# on the host
+qemu -redir tcp:6001::6000 [...]
+# this host xterm should open in the guest X11 server
+xterm -display :1
+@end example
+
+To redirect telnet connections from host port 5555 to telnet port on
+the guest, use the following:
+
+@example
+# on the host
+qemu -redir tcp:5555::23 [...]
+telnet localhost 5555
+@end example
+
+Then when you use on the host @code{telnet localhost 5555}, you
+connect to the guest telnet server.
+
+@end table
+
+Linux boot specific: When using these options, you can use a given
+Linux kernel without installing it in the disk image. It can be useful
+for easier testing of various kernels.
+
+@table @option
+
+@item -kernel bzImage
+Use @var{bzImage} as kernel image.
+
+@item -append cmdline
+Use @var{cmdline} as kernel command line
+
+@item -initrd file
+Use @var{file} as initial ram disk.
+
+@end table
+
+Debug/Expert options:
+@table @option
+
+@item -serial dev
+Redirect the virtual serial port to host character device
+@var{dev}. The default device is @code{vc} in graphical mode and
+@code{stdio} in non graphical mode.
+
+This option can be used several times to simulate up to 4 serials
+ports.
+
+Available character devices are:
+@table @code
+@item vc
+Virtual console
+@item pty
+[Linux only] Pseudo TTY (a new PTY is automatically allocated)
+@item null
+void device
+@item /dev/XXX
+[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port
+parameters are set according to the emulated ones.
+@item /dev/parportN
+[Linux only, parallel port only] Use host parallel port
+@var{N}. Currently only SPP parallel port features can be used.
+@item file:filename
+Write output to filename. No character can be read.
+@item stdio
+[Unix only] standard input/output
+@item pipe:filename
+name pipe @var{filename}
+@item COMn
+[Windows only] Use host serial port @var{n}
+@item udp:[remote_host]:remote_port[@@[src_ip]:src_port]
+This implements UDP Net Console. When @var{remote_host} or @var{src_ip} are not specified they default to @code{0.0.0.0}. When not using a specifed @var{src_port} a random port is automatically chosen.
+
+If you just want a simple readonly console you can use @code{netcat} or
+@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as:
+@code{nc -u -l -p 4555}. Any time qemu writes something to that port it
+will appear in the netconsole session.
+
+If you plan to send characters back via netconsole or you want to stop
+and start qemu a lot of times, you should have qemu use the same
+source port each time by using something like @code{-serial
+udp::4555@@:4556} to qemu. Another approach is to use a patched
+version of netcat which can listen to a TCP port and send and receive
+characters via udp. If you have a patched version of netcat which
+activates telnet remote echo and single char transfer, then you can
+use the following options to step up a netcat redirector to allow
+telnet on port 5555 to access the qemu port.
+@table @code
+@item Qemu Options:
+-serial udp::4555@@:4556
+@item netcat options:
+-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T
+@item telnet options:
+localhost 5555
+@end table
+
+
+@item tcp:[host]:port[,server][,nowait]
+The TCP Net Console has two modes of operation. It can send the serial
+I/O to a location or wait for a connection from a location. By default
+the TCP Net Console is sent to @var{host} at the @var{port}. If you use
+the @var{,server} option QEMU will wait for a client socket application
+to connect to the port before continuing, unless the @code{,nowait}
+option was specified. If @var{host} is omitted, 0.0.0.0 is assumed. Only
+one TCP connection at a time is accepted. You can use @code{telnet} to
+connect to the corresponding character device.
+@table @code
+@item Example to send tcp console to 192.168.0.2 port 4444
+-serial tcp:192.168.0.2:4444
+@item Example to listen and wait on port 4444 for connection
+-serial tcp::4444,server
+@item Example to not wait and listen on ip 192.168.0.100 port 4444
+-serial tcp:192.168.0.100:4444,server,nowait
+@end table
+
+@item telnet:host:port[,server][,nowait]
+The telnet protocol is used instead of raw tcp sockets. The options
+work the same as if you had specified @code{-serial tcp}. The
+difference is that the port acts like a telnet server or client using
+telnet option negotiation. This will also allow you to send the
+MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
+sequence. Typically in unix telnet you do it with Control-] and then
+type "send break" followed by pressing the enter key.
+
+@end table
+
+@item -parallel dev
+Redirect the virtual parallel port to host device @var{dev} (same
+devices as the serial port). On Linux hosts, @file{/dev/parportN} can
+be used to use hardware devices connected on the corresponding host
+parallel port.
+
+This option can be used several times to simulate up to 3 parallel
+ports.
+
+@item -monitor dev
+Redirect the monitor to host device @var{dev} (same devices as the
+serial port).
+The default device is @code{vc} in graphical mode and @code{stdio} in
+non graphical mode.
+
+@item -s
+Wait gdb connection to port 1234 (@pxref{gdb_usage}).
+@item -p port
+Change gdb connection port.
+@item -S
+Do not start CPU at startup (you must type 'c' in the monitor).
+@item -d
+Output log in /tmp/qemu.log
+@item -hdachs c,h,s,[,t]
+Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
+@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
+translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
+all thoses parameters. This option is useful for old MS-DOS disk
+images.
+
+@item -std-vga
+Simulate a standard VGA card with Bochs VBE extensions (default is
+Cirrus Logic GD5446 PCI VGA). If your guest OS supports the VESA 2.0
+VBE extensions (e.g. Windows XP) and if you want to use high
+resolution modes (>= 1280x1024x16) then you should use this option.
+
+@item -no-acpi
+Disable ACPI (Advanced Configuration and Power Interface) support. Use
+it if your guest OS complains about ACPI problems (PC target machine
+only).
+
+@item -loadvm file
+Start right away with a saved state (@code{loadvm} in monitor)
+@end table
+
+@c man end
+
+@node pcsys_keys
+@section Keys
+
+@c man begin OPTIONS
+
+During the graphical emulation, you can use the following keys:
+@table @key
+@item Ctrl-Alt-f
+Toggle full screen
+
+@item Ctrl-Alt-n
+Switch to virtual console 'n'. Standard console mappings are:
+@table @emph
+@item 1
+Target system display
+@item 2
+Monitor
+@item 3
+Serial port
+@end table
+
+@item Ctrl-Alt
+Toggle mouse and keyboard grab.
+@end table
+
+In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down},
+@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log.
+
+During emulation, if you are using the @option{-nographic} option, use
+@key{Ctrl-a h} to get terminal commands:
+
+@table @key
+@item Ctrl-a h
+Print this help
+@item Ctrl-a x
+Exit emulatior
+@item Ctrl-a s
+Save disk data back to file (if -snapshot)
+@item Ctrl-a b
+Send break (magic sysrq in Linux)
+@item Ctrl-a c
+Switch between console and monitor
+@item Ctrl-a Ctrl-a
+Send Ctrl-a
+@end table
+@c man end
+
+@ignore
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more precise information and Linux
+user mode emulator invocation.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
+
+@node pcsys_monitor
+@section QEMU Monitor
+
+The QEMU monitor is used to give complex commands to the QEMU
+emulator. You can use it to:
+
+@itemize @minus
+
+@item
+Remove or insert removable medias images
+(such as CD-ROM or floppies)
+
+@item
+Freeze/unfreeze the Virtual Machine (VM) and save or restore its state
+from a disk file.
+
+@item Inspect the VM state without an external debugger.
+
+@end itemize
+
+@subsection Commands
+
+The following commands are available:
+
+@table @option
+
+@item help or ? [cmd]
+Show the help for all commands or just for command @var{cmd}.
+
+@item commit
+Commit changes to the disk images (if -snapshot is used)
+
+@item info subcommand
+show various information about the system state
+
+@table @option
+@item info network
+show the various VLANs and the associated devices
+@item info block
+show the block devices
+@item info registers
+show the cpu registers
+@item info history
+show the command line history
+@item info pci
+show emulated PCI device
+@item info usb
+show USB devices plugged on the virtual USB hub
+@item info usbhost
+show all USB host devices
+@item info capture
+show information about active capturing
+@end table
+
+@item q or quit
+Quit the emulator.
+
+@item eject [-f] device
+Eject a removable media (use -f to force it).
+
+@item change device filename
+Change a removable media.
+
+@item screendump filename
+Save screen into PPM image @var{filename}.
+
+@item wavcapture filename [frequency [bits [channels]]]
+Capture audio into @var{filename}. Using sample rate @var{frequency}
+bits per sample @var{bits} and number of channels @var{channels}.
+
+Defaults:
+@itemize @minus
+@item Sample rate = 44100 Hz - CD quality
+@item Bits = 16
+@item Number of channels = 2 - Stereo
+@end itemize
+
+@item stopcapture index
+Stop capture with a given @var{index}, index can be obtained with
+@example
+info capture
+@end example
+
+@item log item1[,...]
+Activate logging of the specified items to @file{/tmp/qemu.log}.
+
+@item savevm filename
+Save the whole virtual machine state to @var{filename}.
+
+@item loadvm filename
+Restore the whole virtual machine state from @var{filename}.
+
+@item stop
+Stop emulation.
+
+@item c or cont
+Resume emulation.
+
+@item gdbserver [port]
+Start gdbserver session (default port=1234)
+
+@item x/fmt addr
+Virtual memory dump starting at @var{addr}.
+
+@item xp /fmt addr
+Physical memory dump starting at @var{addr}.
+
+@var{fmt} is a format which tells the command how to format the
+data. Its syntax is: @option{/@{count@}@{format@}@{size@}}
+
+@table @var
+@item count
+is the number of items to be dumped.
+
+@item format
+can be x (hexa), d (signed decimal), u (unsigned decimal), o (octal),
+c (char) or i (asm instruction).
+
+@item size
+can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86,
+@code{h} or @code{w} can be specified with the @code{i} format to
+respectively select 16 or 32 bit code instruction size.
+
+@end table
+
+Examples:
+@itemize
+@item
+Dump 10 instructions at the current instruction pointer:
+@example
+(qemu) x/10i $eip
+0x90107063: ret
+0x90107064: sti
+0x90107065: lea 0x0(%esi,1),%esi
+0x90107069: lea 0x0(%edi,1),%edi
+0x90107070: ret
+0x90107071: jmp 0x90107080
+0x90107073: nop
+0x90107074: nop
+0x90107075: nop
+0x90107076: nop
+@end example
+
+@item
+Dump 80 16 bit values at the start of the video memory.
+@smallexample
+(qemu) xp/80hx 0xb8000
+0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42
+0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41
+0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72
+0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73
+0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20
+0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720
+0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+@end smallexample
+@end itemize
+
+@item p or print/fmt expr
+
+Print expression value. Only the @var{format} part of @var{fmt} is
+used.
+
+@item sendkey keys
+
+Send @var{keys} to the emulator. Use @code{-} to press several keys
+simultaneously. Example:
+@example
+sendkey ctrl-alt-f1
+@end example
+
+This command is useful to send keys that your graphical user interface
+intercepts at low level, such as @code{ctrl-alt-f1} in X Window.
+
+@item system_reset
+
+Reset the system.
+
+@item usb_add devname
+
+Add the USB device @var{devname}. For details of available devices see
+@ref{usb_devices}
+
+@item usb_del devname
+
+Remove the USB device @var{devname} from the QEMU virtual USB
+hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
+command @code{info usb} to see the devices you can remove.
+
+@end table
+
+@subsection Integer expressions
+
+The monitor understands integers expressions for every integer
+argument. You can use register names to get the value of specifics
+CPU registers by prefixing them with @emph{$}.
+
+@node disk_images
+@section Disk Images
+
+Since version 0.6.1, QEMU supports many disk image formats, including
+growable disk images (their size increase as non empty sectors are
+written), compressed and encrypted disk images.
+
+@menu
+* disk_images_quickstart:: Quick start for disk image creation
+* disk_images_snapshot_mode:: Snapshot mode
+* qemu_img_invocation:: qemu-img Invocation
+* disk_images_fat_images:: Virtual FAT disk images
+@end menu
+
+@node disk_images_quickstart
+@subsection Quick start for disk image creation
+
+You can create a disk image with the command:
+@example
+qemu-img create myimage.img mysize
+@end example
+where @var{myimage.img} is the disk image filename and @var{mysize} is its
+size in kilobytes. You can add an @code{M} suffix to give the size in
+megabytes and a @code{G} suffix for gigabytes.
+
+See @ref{qemu_img_invocation} for more information.
+
+@node disk_images_snapshot_mode
+@subsection Snapshot mode
+
+If you use the option @option{-snapshot}, all disk images are
+considered as read only. When sectors in written, they are written in
+a temporary file created in @file{/tmp}. You can however force the
+write back to the raw disk images by using the @code{commit} monitor
+command (or @key{C-a s} in the serial console).
+
+@node qemu_img_invocation
+@subsection @code{qemu-img} Invocation
+
+@include qemu-img.texi
+
+@node disk_images_fat_images
+@subsection Virtual FAT disk images
+
+QEMU can automatically create a virtual FAT disk image from a
+directory tree. In order to use it, just type:
+
+@example
+qemu linux.img -hdb fat:/my_directory
+@end example
+
+Then you access access to all the files in the @file{/my_directory}
+directory without having to copy them in a disk image or to export
+them via SAMBA or NFS. The default access is @emph{read-only}.
+
+Floppies can be emulated with the @code{:floppy:} option:
+
+@example
+qemu linux.img -fda fat:floppy:/my_directory
+@end example
+
+A read/write support is available for testing (beta stage) with the
+@code{:rw:} option:
+
+@example
+qemu linux.img -fda fat:floppy:rw:/my_directory
+@end example
+
+What you should @emph{never} do:
+@itemize
+@item use non-ASCII filenames ;
+@item use "-snapshot" together with ":rw:" ;
+@item expect it to work when loadvm'ing ;
+@item write to the FAT directory on the host system while accessing it with the guest system.
+@end itemize
+
+@node pcsys_network
+@section Network emulation
+
+QEMU can simulate several networks cards (NE2000 boards on the PC
+target) and can connect them to an arbitrary number of Virtual Local
+Area Networks (VLANs). Host TAP devices can be connected to any QEMU
+VLAN. VLAN can be connected between separate instances of QEMU to
+simulate large networks. For simpler usage, a non priviledged user mode
+network stack can replace the TAP device to have a basic network
+connection.
+
+@subsection VLANs
+
+QEMU simulates several VLANs. A VLAN can be symbolised as a virtual
+connection between several network devices. These devices can be for
+example QEMU virtual Ethernet cards or virtual Host ethernet devices
+(TAP devices).
+
+@subsection Using TAP network interfaces
+
+This is the standard way to connect QEMU to a real network. QEMU adds
+a virtual network device on your host (called @code{tapN}), and you
+can then configure it as if it was a real ethernet card.
+
+As an example, you can download the @file{linux-test-xxx.tar.gz}
+archive and copy the script @file{qemu-ifup} in @file{/etc} and
+configure properly @code{sudo} so that the command @code{ifconfig}
+contained in @file{qemu-ifup} can be executed as root. You must verify
+that your host kernel supports the TAP network interfaces: the
+device @file{/dev/net/tun} must be present.
+
+See @ref{direct_linux_boot} to have an example of network use with a
+Linux distribution and @ref{sec_invocation} to have examples of
+command lines using the TAP network interfaces.
+
+@subsection Using the user mode network stack
+
+By using the option @option{-net user} (default configuration if no
+@option{-net} option is specified), QEMU uses a completely user mode
+network stack (you don't need root priviledge to use the virtual
+network). The virtual network configuration is the following:
+
+@example
+
+ QEMU VLAN <------> Firewall/DHCP server <-----> Internet
+ | (10.0.2.2)
+ |
+ ----> DNS server (10.0.2.3)
+ |
+ ----> SMB server (10.0.2.4)
+@end example
+
+The QEMU VM behaves as if it was behind a firewall which blocks all
+incoming connections. You can use a DHCP client to automatically
+configure the network in the QEMU VM. The DHCP server assign addresses
+to the hosts starting from 10.0.2.15.
+
+In order to check that the user mode network is working, you can ping
+the address 10.0.2.2 and verify that you got an address in the range
+10.0.2.x from the QEMU virtual DHCP server.
+
+Note that @code{ping} is not supported reliably to the internet as it
+would require root priviledges. It means you can only ping the local
+router (10.0.2.2).
+
+When using the built-in TFTP server, the router is also the TFTP
+server.
+
+When using the @option{-redir} option, TCP or UDP connections can be
+redirected from the host to the guest. It allows for example to
+redirect X11, telnet or SSH connections.
+
+@subsection Connecting VLANs between QEMU instances
+
+Using the @option{-net socket} option, it is possible to make VLANs
+that span several QEMU instances. See @ref{sec_invocation} to have a
+basic example.
+
+@node direct_linux_boot
+@section Direct Linux Boot
+
+This section explains how to launch a Linux kernel inside QEMU without
+having to make a full bootable image. It is very useful for fast Linux
+kernel testing. The QEMU network configuration is also explained.
+
+@enumerate
+@item
+Download the archive @file{linux-test-xxx.tar.gz} containing a Linux
+kernel and a disk image.
+
+@item Optional: If you want network support (for example to launch X11 examples), you
+must copy the script @file{qemu-ifup} in @file{/etc} and configure
+properly @code{sudo} so that the command @code{ifconfig} contained in
+@file{qemu-ifup} can be executed as root. You must verify that your host
+kernel supports the TUN/TAP network interfaces: the device
+@file{/dev/net/tun} must be present.
+
+When network is enabled, there is a virtual network connection between
+the host kernel and the emulated kernel. The emulated kernel is seen
+from the host kernel at IP address 172.20.0.2 and the host kernel is
+seen from the emulated kernel at IP address 172.20.0.1.
+
+@item Launch @code{qemu.sh}. You should have the following output:
+
+@smallexample
+> ./qemu.sh
+Connected to host network interface: tun0
+Linux version 2.4.21 (bellard@@voyager.localdomain) (gcc version 3.2.2 20030222 @/(Red Hat @/Linux 3.2.2-5)) #5 Tue Nov 11 18:18:53 CET 2003
+BIOS-provided physical RAM map:
+ BIOS-e801: 0000000000000000 - 000000000009f000 (usable)
+ BIOS-e801: 0000000000100000 - 0000000002000000 (usable)
+32MB LOWMEM available.
+On node 0 totalpages: 8192
+zone(0): 4096 pages.
+zone(1): 4096 pages.
+zone(2): 0 pages.
+Kernel command line: root=/dev/hda sb=0x220,5,1,5 ide2=noprobe ide3=noprobe ide4=noprobe @/ide5=noprobe console=ttyS0
+ide_setup: ide2=noprobe
+ide_setup: ide3=noprobe
+ide_setup: ide4=noprobe
+ide_setup: ide5=noprobe
+Initializing CPU#0
+Detected 2399.621 MHz processor.
+Console: colour EGA 80x25
+Calibrating delay loop... 4744.80 BogoMIPS
+Memory: 28872k/32768k available (1210k kernel code, 3508k reserved, 266k data, 64k init, @/0k highmem)
+Dentry cache hash table entries: 4096 (order: 3, 32768 bytes)
+Inode cache hash table entries: 2048 (order: 2, 16384 bytes)
+Mount cache hash table entries: 512 (order: 0, 4096 bytes)
+Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
+Page-cache hash table entries: 8192 (order: 3, 32768 bytes)
+CPU: Intel Pentium Pro stepping 03
+Checking 'hlt' instruction... OK.
+POSIX conformance testing by UNIFIX
+Linux NET4.0 for Linux 2.4
+Based upon Swansea University Computer Society NET3.039
+Initializing RT netlink socket
+apm: BIOS not found.
+Starting kswapd
+Journalled Block Device driver loaded
+Detected PS/2 Mouse Port.
+pty: 256 Unix98 ptys configured
+Serial driver version 5.05c (2001-07-08) with no serial options enabled
+ttyS00 at 0x03f8 (irq = 4) is a 16450
+ne.c:v1.10 9/23/94 Donald Becker (becker@@scyld.com)
+Last modified Nov 1, 2000 by Paul Gortmaker
+NE*000 ethercard probe at 0x300: 52 54 00 12 34 56
+eth0: NE2000 found at 0x300, using IRQ 9.
+RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
+Uniform Multi-Platform E-IDE driver Revision: 7.00beta4-2.4
+ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx
+hda: QEMU HARDDISK, ATA DISK drive
+ide0 at 0x1f0-0x1f7,0x3f6 on irq 14
+hda: attached ide-disk driver.
+hda: 20480 sectors (10 MB) w/256KiB Cache, CHS=20/16/63
+Partition check:
+ hda:
+Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996
+NET4: Linux TCP/IP 1.0 for NET4.0
+IP Protocols: ICMP, UDP, TCP, IGMP
+IP: routing cache hash table of 512 buckets, 4Kbytes
+TCP: Hash tables configured (established 2048 bind 4096)
+NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
+EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended
+VFS: Mounted root (ext2 filesystem).
+Freeing unused kernel memory: 64k freed
+
+Linux version 2.4.21 (bellard@@voyager.localdomain) (gcc version 3.2.2 20030222 @/(Red Hat @/Linux 3.2.2-5)) #5 Tue Nov 11 18:18:53 CET 2003
+
+QEMU Linux test distribution (based on Redhat 9)
+
+Type 'exit' to halt the system
+
+sh-2.05b#
+@end smallexample
+
+@item
+Then you can play with the kernel inside the virtual serial console. You
+can launch @code{ls} for example. Type @key{Ctrl-a h} to have an help
+about the keys you can type inside the virtual serial console. In
+particular, use @key{Ctrl-a x} to exit QEMU and use @key{Ctrl-a b} as
+the Magic SysRq key.
+
+@item
+If the network is enabled, launch the script @file{/etc/linuxrc} in the
+emulator (don't forget the leading dot):
+@example
+. /etc/linuxrc
+@end example
+
+Then enable X11 connections on your PC from the emulated Linux:
+@example
+xhost +172.20.0.2
+@end example
+
+You can now launch @file{xterm} or @file{xlogo} and verify that you have
+a real Virtual Linux system !
+
+@end enumerate
+
+NOTES:
+@enumerate
+@item
+A 2.5.74 kernel is also included in the archive. Just
+replace the bzImage in qemu.sh to try it.
+
+@item
+In order to exit cleanly from qemu, you can do a @emph{shutdown} inside
+qemu. qemu will automatically exit when the Linux shutdown is done.
+
+@item
+You can boot slightly faster by disabling the probe of non present IDE
+interfaces. To do so, add the following options on the kernel command
+line:
+@example
+ide1=noprobe ide2=noprobe ide3=noprobe ide4=noprobe ide5=noprobe
+@end example
+
+@item
+The example disk image is a modified version of the one made by Kevin
+Lawton for the plex86 Project (@url{www.plex86.org}).
+
+@end enumerate
+
+@node pcsys_usb
+@section USB emulation
+
+QEMU emulates a PCI UHCI USB controller. You can virtually plug
+virtual USB devices or real host USB devices (experimental, works only
+on Linux hosts). Qemu will automatically create and connect virtual USB hubs
+as neccessary to connect multiple USB devices.
+
+@menu
+* usb_devices::
+* host_usb_devices::
+@end menu
+@node usb_devices
+@subsection Connecting USB devices
+
+USB devices can be connected with the @option{-usbdevice} commandline option
+or the @code{usb_add} monitor command. Available devices are:
+
+@table @var
+@item @code{mouse}
+Virtual Mouse. This will override the PS/2 mouse emulation when activated.
+@item @code{tablet}
+Pointer device that uses abolsute coordinates (like a touchscreen).
+This means qemu is able to report the mouse position without having
+to grab the mouse. Also overrides the PS/2 mouse emulation when activated.
+@item @code{disk:file}
+Mass storage device based on @var{file} (@pxref{disk_images})
+@item @code{host:bus.addr}
+Pass through the host device identified by @var{bus.addr}
+(Linux only)
+@item @code{host:vendor_id:product_id}
+Pass through the host device identified by @var{vendor_id:product_id}
+(Linux only)
+@end table
+
+@node host_usb_devices
+@subsection Using host USB devices on a Linux host
+
+WARNING: this is an experimental feature. QEMU will slow down when
+using it. USB devices requiring real time streaming (i.e. USB Video
+Cameras) are not supported yet.
+
+@enumerate
+@item If you use an early Linux 2.4 kernel, verify that no Linux driver
+is actually using the USB device. A simple way to do that is simply to
+disable the corresponding kernel module by renaming it from @file{mydriver.o}
+to @file{mydriver.o.disabled}.
+
+@item Verify that @file{/proc/bus/usb} is working (most Linux distributions should enable it by default). You should see something like that:
+@example
+ls /proc/bus/usb
+001 devices drivers
+@end example
+
+@item Since only root can access to the USB devices directly, you can either launch QEMU as root or change the permissions of the USB devices you want to use. For testing, the following suffices:
+@example
+chown -R myuid /proc/bus/usb
+@end example
+
+@item Launch QEMU and do in the monitor:
+@example
+info usbhost
+ Device 1.2, speed 480 Mb/s
+ Class 00: USB device 1234:5678, USB DISK
+@end example
+You should see the list of the devices you can use (Never try to use
+hubs, it won't work).
+
+@item Add the device in QEMU by using:
+@example
+usb_add host:1234:5678
+@end example
+
+Normally the guest OS should report that a new USB device is
+plugged. You can use the option @option{-usbdevice} to do the same.
+
+@item Now you can try to use the host USB device in QEMU.
+
+@end enumerate
+
+When relaunching QEMU, you may have to unplug and plug again the USB
+device to make it work again (this is a bug).
+
+@node gdb_usage
+@section GDB usage
+
+QEMU has a primitive support to work with gdb, so that you can do
+'Ctrl-C' while the virtual machine is running and inspect its state.
+
+In order to use gdb, launch qemu with the '-s' option. It will wait for a
+gdb connection:
+@example
+> qemu -s -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \
+ -append "root=/dev/hda"
+Connected to host network interface: tun0
+Waiting gdb connection on port 1234
+@end example
+
+Then launch gdb on the 'vmlinux' executable:
+@example
+> gdb vmlinux
+@end example
+
+In gdb, connect to QEMU:
+@example
+(gdb) target remote localhost:1234
+@end example
+
+Then you can use gdb normally. For example, type 'c' to launch the kernel:
+@example
+(gdb) c
+@end example
+
+Here are some useful tips in order to use gdb on system code:
+
+@enumerate
+@item
+Use @code{info reg} to display all the CPU registers.
+@item
+Use @code{x/10i $eip} to display the code at the PC position.
+@item
+Use @code{set architecture i8086} to dump 16 bit code. Then use
+@code{x/10i $cs*16+$eip} to dump the code at the PC position.
+@end enumerate
+
+@node pcsys_os_specific
+@section Target OS specific information
+
+@subsection Linux
+
+To have access to SVGA graphic modes under X11, use the @code{vesa} or
+the @code{cirrus} X11 driver. For optimal performances, use 16 bit
+color depth in the guest and the host OS.
+
+When using a 2.6 guest Linux kernel, you should add the option
+@code{clock=pit} on the kernel command line because the 2.6 Linux
+kernels make very strict real time clock checks by default that QEMU
+cannot simulate exactly.
+
+When using a 2.6 guest Linux kernel, verify that the 4G/4G patch is
+not activated because QEMU is slower with this patch. The QEMU
+Accelerator Module is also much slower in this case. Earlier Fedora
+Core 3 Linux kernel (< 2.6.9-1.724_FC3) were known to incorporte this
+patch by default. Newer kernels don't have it.
+
+@subsection Windows
+
+If you have a slow host, using Windows 95 is better as it gives the
+best speed. Windows 2000 is also a good choice.
+
+@subsubsection SVGA graphic modes support
+
+QEMU emulates a Cirrus Logic GD5446 Video
+card. All Windows versions starting from Windows 95 should recognize
+and use this graphic card. For optimal performances, use 16 bit color
+depth in the guest and the host OS.
+
+If you are using Windows XP as guest OS and if you want to use high
+resolution modes which the Cirrus Logic BIOS does not support (i.e. >=
+1280x1024x16), then you should use the VESA VBE virtual graphic card
+(option @option{-std-vga}).
+
+@subsubsection CPU usage reduction
+
+Windows 9x does not correctly use the CPU HLT
+instruction. The result is that it takes host CPU cycles even when
+idle. You can install the utility from
+@url{http://www.user.cityline.ru/~maxamn/amnhltm.zip} to solve this
+problem. Note that no such tool is needed for NT, 2000 or XP.
+
+@subsubsection Windows 2000 disk full problem
+
+Windows 2000 has a bug which gives a disk full problem during its
+installation. When installing it, use the @option{-win2k-hack} QEMU
+option to enable a specific workaround. After Windows 2000 is
+installed, you no longer need this option (this option slows down the
+IDE transfers).
+
+@subsubsection Windows 2000 shutdown
+
+Windows 2000 cannot automatically shutdown in QEMU although Windows 98
+can. It comes from the fact that Windows 2000 does not automatically
+use the APM driver provided by the BIOS.
+
+In order to correct that, do the following (thanks to Struan
+Bartlett): go to the Control Panel => Add/Remove Hardware & Next =>
+Add/Troubleshoot a device => Add a new device & Next => No, select the
+hardware from a list & Next => NT Apm/Legacy Support & Next => Next
+(again) a few times. Now the driver is installed and Windows 2000 now
+correctly instructs QEMU to shutdown at the appropriate moment.
+
+@subsubsection Share a directory between Unix and Windows
+
+See @ref{sec_invocation} about the help of the option @option{-smb}.
+
+@subsubsection Windows XP security problems
+
+Some releases of Windows XP install correctly but give a security
+error when booting:
+@example
+A problem is preventing Windows from accurately checking the
+license for this computer. Error code: 0x800703e6.
+@end example
+The only known workaround is to boot in Safe mode
+without networking support.
+
+Future QEMU releases are likely to correct this bug.
+
+@subsection MS-DOS and FreeDOS
+
+@subsubsection CPU usage reduction
+
+DOS does not correctly use the CPU HLT instruction. The result is that
+it takes host CPU cycles even when idle. You can install the utility
+from @url{http://www.vmware.com/software/dosidle210.zip} to solve this
+problem.
+
+@node QEMU System emulator for non PC targets
+@chapter QEMU System emulator for non PC targets
+
+QEMU is a generic emulator and it emulates many non PC
+machines. Most of the options are similar to the PC emulator. The
+differences are mentionned in the following sections.
+
+@menu
+* QEMU PowerPC System emulator::
+* Sparc32 System emulator invocation::
+* Sparc64 System emulator invocation::
+* MIPS System emulator invocation::
+* ARM System emulator invocation::
+@end menu
+
+@node QEMU PowerPC System emulator
+@section QEMU PowerPC System emulator
+
+Use the executable @file{qemu-system-ppc} to simulate a complete PREP
+or PowerMac PowerPC system.
+
+QEMU emulates the following PowerMac peripherals:
+
+@itemize @minus
+@item
+UniNorth PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+2 PMAC IDE interfaces with hard disk and CD-ROM support
+@item
+NE2000 PCI adapters
+@item
+Non Volatile RAM
+@item
+VIA-CUDA with ADB keyboard and mouse.
+@end itemize
+
+QEMU emulates the following PREP peripherals:
+
+@itemize @minus
+@item
+PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+2 IDE interfaces with hard disk and CD-ROM support
+@item
+Floppy disk
+@item
+NE2000 network adapters
+@item
+Serial port
+@item
+PREP Non Volatile RAM
+@item
+PC compatible keyboard and mouse.
+@end itemize
+
+QEMU uses the Open Hack'Ware Open Firmware Compatible BIOS available at
+@url{http://perso.magic.fr/l_indien/OpenHackWare/index.htm}.
+
+@c man begin OPTIONS
+
+The following options are specific to the PowerPC emulation:
+
+@table @option
+
+@item -g WxH[xDEPTH]
+
+Set the initial VGA graphic mode. The default is 800x600x15.
+
+@end table
+
+@c man end
+
+
+More information is available at
+@url{http://perso.magic.fr/l_indien/qemu-ppc/}.
+
+@node Sparc32 System emulator invocation
+@section Sparc32 System emulator invocation
+
+Use the executable @file{qemu-system-sparc} to simulate a SparcStation 5
+(sun4m architecture). The emulation is somewhat complete.
+
+QEMU emulates the following sun4m peripherals:
+
+@itemize @minus
+@item
+IOMMU
+@item
+TCX Frame buffer
+@item
+Lance (Am7990) Ethernet
+@item
+Non Volatile RAM M48T08
+@item
+Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard
+and power/reset logic
+@item
+ESP SCSI controller with hard disk and CD-ROM support
+@item
+Floppy drive
+@end itemize
+
+The number of peripherals is fixed in the architecture.
+
+Since version 0.8.2, QEMU uses OpenBIOS
+@url{http://www.openbios.org/}. OpenBIOS is a free (GPL v2) portable
+firmware implementation. The goal is to implement a 100% IEEE
+1275-1994 (referred to as Open Firmware) compliant firmware.
+
+A sample Linux 2.6 series kernel and ram disk image are available on
+the QEMU web site. Please note that currently NetBSD, OpenBSD or
+Solaris kernels don't work.
+
+@c man begin OPTIONS
+
+The following options are specific to the Sparc emulation:
+
+@table @option
+
+@item -g WxH
+
+Set the initial TCX graphic mode. The default is 1024x768.
+
+@end table
+
+@c man end
+
+@node Sparc64 System emulator invocation
+@section Sparc64 System emulator invocation
+
+Use the executable @file{qemu-system-sparc64} to simulate a Sun4u machine.
+The emulator is not usable for anything yet.
+
+QEMU emulates the following sun4u peripherals:
+
+@itemize @minus
+@item
+UltraSparc IIi APB PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+Non Volatile RAM M48T59
+@item
+PC-compatible serial ports
+@end itemize
+
+@node MIPS System emulator invocation
+@section MIPS System emulator invocation
+
+Use the executable @file{qemu-system-mips} to simulate a MIPS machine.
+The emulator is able to boot a Linux kernel and to run a Linux Debian
+installation from NFS. The following devices are emulated:
+
+@itemize @minus
+@item
+MIPS R4K CPU
+@item
+PC style serial port
+@item
+NE2000 network card
+@end itemize
+
+More information is available in the QEMU mailing-list archive.
+
+@node ARM System emulator invocation
+@section ARM System emulator invocation
+
+Use the executable @file{qemu-system-arm} to simulate a ARM
+machine. The ARM Integrator/CP board is emulated with the following
+devices:
+
+@itemize @minus
+@item
+ARM926E or ARM1026E CPU
+@item
+Two PL011 UARTs
+@item
+SMC 91c111 Ethernet adapter
+@item
+PL110 LCD controller
+@item
+PL050 KMI with PS/2 keyboard and mouse.
+@end itemize
+
+The ARM Versatile baseboard is emulated with the following devices:
+
+@itemize @minus
+@item
+ARM926E CPU
+@item
+PL190 Vectored Interrupt Controller
+@item
+Four PL011 UARTs
+@item
+SMC 91c111 Ethernet adapter
+@item
+PL110 LCD controller
+@item
+PL050 KMI with PS/2 keyboard and mouse.
+@item
+PCI host bridge. Note the emulated PCI bridge only provides access to
+PCI memory space. It does not provide access to PCI IO space.
+This means some devices (eg. ne2k_pci NIC) are not useable, and others
+(eg. rtl8139 NIC) are only useable when the guest drivers use the memory
+mapped control registers.
+@item
+PCI OHCI USB controller.
+@item
+LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM devices.
+@end itemize
+
+A Linux 2.6 test image is available on the QEMU web site. More
+information is available in the QEMU mailing-list archive.
+
+@node QEMU Linux User space emulator
+@chapter QEMU Linux User space emulator
+
+@menu
+* Quick Start::
+* Wine launch::
+* Command line options::
+* Other binaries::
+@end menu
+
+@node Quick Start
+@section Quick Start
+
+In order to launch a Linux process, QEMU needs the process executable
+itself and all the target (x86) dynamic libraries used by it.
+
+@itemize
+
+@item On x86, you can just try to launch any process by using the native
+libraries:
+
+@example
+qemu-i386 -L / /bin/ls
+@end example
+
+@code{-L /} tells that the x86 dynamic linker must be searched with a
+@file{/} prefix.
+
+@item Since QEMU is also a linux process, you can launch qemu with qemu (NOTE: you can only do that if you compiled QEMU from the sources):
+
+@example
+qemu-i386 -L / qemu-i386 -L / /bin/ls
+@end example
+
+@item On non x86 CPUs, you need first to download at least an x86 glibc
+(@file{qemu-runtime-i386-XXX-.tar.gz} on the QEMU web page). Ensure that
+@code{LD_LIBRARY_PATH} is not set:
+
+@example
+unset LD_LIBRARY_PATH
+@end example
+
+Then you can launch the precompiled @file{ls} x86 executable:
+
+@example
+qemu-i386 tests/i386/ls
+@end example
+You can look at @file{qemu-binfmt-conf.sh} so that
+QEMU is automatically launched by the Linux kernel when you try to
+launch x86 executables. It requires the @code{binfmt_misc} module in the
+Linux kernel.
+
+@item The x86 version of QEMU is also included. You can try weird things such as:
+@example
+qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 \
+ /usr/local/qemu-i386/bin/ls-i386
+@end example
+
+@end itemize
+
+@node Wine launch
+@section Wine launch
+
+@itemize
+
+@item Ensure that you have a working QEMU with the x86 glibc
+distribution (see previous section). In order to verify it, you must be
+able to do:
+
+@example
+qemu-i386 /usr/local/qemu-i386/bin/ls-i386
+@end example
+
+@item Download the binary x86 Wine install
+(@file{qemu-XXX-i386-wine.tar.gz} on the QEMU web page).
+
+@item Configure Wine on your account. Look at the provided script
+@file{/usr/local/qemu-i386/@/bin/wine-conf.sh}. Your previous
+@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}.
+
+@item Then you can try the example @file{putty.exe}:
+
+@example
+qemu-i386 /usr/local/qemu-i386/wine/bin/wine \
+ /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe
+@end example
+
+@end itemize
+
+@node Command line options
+@section Command line options
+
+@example
+usage: qemu-i386 [-h] [-d] [-L path] [-s size] program [arguments...]
+@end example
+
+@table @option
+@item -h
+Print the help
+@item -L path
+Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386)
+@item -s size
+Set the x86 stack size in bytes (default=524288)
+@end table
+
+Debug options:
+
+@table @option
+@item -d
+Activate log (logfile=/tmp/qemu.log)
+@item -p pagesize
+Act as if the host page size was 'pagesize' bytes
+@end table
+
+@node Other binaries
+@section Other binaries
+
+@command{qemu-arm} is also capable of running ARM "Angel" semihosted ELF
+binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB
+configurations), and arm-uclinux bFLT format binaries.
+
+The binary format is detected automatically.
+
+@node compilation
+@chapter Compilation from the sources
+
+@menu
+* Linux/Unix::
+* Windows::
+* Cross compilation for Windows with Linux::
+* Mac OS X::
+@end menu
+
+@node Linux/Unix
+@section Linux/Unix
+
+@subsection Compilation
+
+First you must decompress the sources:
+@example
+cd /tmp
+tar zxvf qemu-x.y.z.tar.gz
+cd qemu-x.y.z
+@end example
+
+Then you configure QEMU and build it (usually no options are needed):
+@example
+./configure
+make
+@end example
+
+Then type as root user:
+@example
+make install
+@end example
+to install QEMU in @file{/usr/local}.
+
+@subsection Tested tool versions
+
+In order to compile QEMU succesfully, it is very important that you
+have the right tools. The most important one is gcc. I cannot guaranty
+that QEMU works if you do not use a tested gcc version. Look at
+'configure' and 'Makefile' if you want to make a different gcc
+version work.
+
+@example
+host gcc binutils glibc linux distribution
+----------------------------------------------------------------------
+x86 3.2 2.13.2 2.1.3 2.4.18
+ 2.96 2.11.93.0.2 2.2.5 2.4.18 Red Hat 7.3
+ 3.2.2 2.13.90.0.18 2.3.2 2.4.20 Red Hat 9
+
+PowerPC 3.3 [4] 2.13.90.0.18 2.3.1 2.4.20briq
+ 3.2
+
+Alpha 3.3 [1] 2.14.90.0.4 2.2.5 2.2.20 [2] Debian 3.0
+
+Sparc32 2.95.4 2.12.90.0.1 2.2.5 2.4.18 Debian 3.0
+
+ARM 2.95.4 2.12.90.0.1 2.2.5 2.4.9 [3] Debian 3.0
+
+[1] On Alpha, QEMU needs the gcc 'visibility' attribute only available
+ for gcc version >= 3.3.
+[2] Linux >= 2.4.20 is necessary for precise exception support
+ (untested).
+[3] 2.4.9-ac10-rmk2-np1-cerf2
+
+[4] gcc 2.95.x generates invalid code when using too many register
+variables. You must use gcc 3.x on PowerPC.
+@end example
+
+@node Windows
+@section Windows
+
+@itemize
+@item Install the current versions of MSYS and MinGW from
+@url{http://www.mingw.org/}. You can find detailed installation
+instructions in the download section and the FAQ.
+
+@item Download
+the MinGW development library of SDL 1.2.x
+(@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from
+@url{http://www.libsdl.org}. Unpack it in a temporary place, and
+unpack the archive @file{i386-mingw32msvc.tar.gz} in the MinGW tool
+directory. Edit the @file{sdl-config} script so that it gives the
+correct SDL directory when invoked.
+
+@item Extract the current version of QEMU.
+
+@item Start the MSYS shell (file @file{msys.bat}).
+
+@item Change to the QEMU directory. Launch @file{./configure} and
+@file{make}. If you have problems using SDL, verify that
+@file{sdl-config} can be launched from the MSYS command line.
+
+@item You can install QEMU in @file{Program Files/Qemu} by typing
+@file{make install}. Don't forget to copy @file{SDL.dll} in
+@file{Program Files/Qemu}.
+
+@end itemize
+
+@node Cross compilation for Windows with Linux
+@section Cross compilation for Windows with Linux
+
+@itemize
+@item
+Install the MinGW cross compilation tools available at
+@url{http://www.mingw.org/}.
+
+@item
+Install the Win32 version of SDL (@url{http://www.libsdl.org}) by
+unpacking @file{i386-mingw32msvc.tar.gz}. Set up the PATH environment
+variable so that @file{i386-mingw32msvc-sdl-config} can be launched by
+the QEMU configuration script.
+
+@item
+Configure QEMU for Windows cross compilation:
+@example
+./configure --enable-mingw32
+@end example
+If necessary, you can change the cross-prefix according to the prefix
+choosen for the MinGW tools with --cross-prefix. You can also use
+--prefix to set the Win32 install path.
+
+@item You can install QEMU in the installation directory by typing
+@file{make install}. Don't forget to copy @file{SDL.dll} in the
+installation directory.
+
+@end itemize
+
+Note: Currently, Wine does not seem able to launch
+QEMU for Win32.
+
+@node Mac OS X
+@section Mac OS X
+
+The Mac OS X patches are not fully merged in QEMU, so you should look
+at the QEMU mailing list archive to have all the necessary
+information.
+
+@node Index
+@chapter Index
+@printindex cp
+
+@bye
diff --git a/qemu-img.c b/qemu-img.c
new file mode 100644
index 0000000..c5a8e1a
--- /dev/null
+++ b/qemu-img.c
@@ -0,0 +1,716 @@
+/*
+ * create a COW disk image
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+void *get_mmap_addr(unsigned long size)
+{
+ return NULL;
+}
+
+void qemu_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *qemu_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ ptr = qemu_malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+char *qemu_strdup(const char *str)
+{
+ char *ptr;
+ ptr = qemu_malloc(strlen(str) + 1);
+ if (!ptr)
+ return NULL;
+ strcpy(ptr, str);
+ return ptr;
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+void term_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void __attribute__((noreturn)) error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu-img: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ exit(1);
+ va_end(ap);
+}
+
+static void format_print(void *opaque, const char *name)
+{
+ printf(" %s", name);
+}
+
+void help(void)
+{
+ printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2005 Fabrice Bellard\n"
+ "usage: qemu-img command [command options]\n"
+ "QEMU disk image utility\n"
+ "\n"
+ "Command syntax:\n"
+ " create [-e] [-b base_image] [-f fmt] filename [size]\n"
+ " commit [-f fmt] filename\n"
+ " convert [-c] [-e] [-f fmt] filename [-O output_fmt] output_filename\n"
+ " info [-f fmt] filename\n"
+ "\n"
+ "Command parameters:\n"
+ " 'filename' is a disk image filename\n"
+ " 'base_image' is the read-only disk image which is used as base for a copy on\n"
+ " write image; the copy on write image only stores the modified data\n"
+ " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
+ " 'size' is the disk image size in kilobytes. Optional suffixes 'M' (megabyte)\n"
+ " and 'G' (gigabyte) are supported\n"
+ " 'output_filename' is the destination disk image filename\n"
+ " 'output_fmt' is the destination format\n"
+ " '-c' indicates that target image must be compressed (qcow format only)\n"
+ " '-e' indicates that the target image must be encrypted (qcow format only)\n"
+ );
+ printf("\nSupported format:");
+ bdrv_iterate_format(format_print, NULL);
+ printf("\n");
+ exit(1);
+}
+
+
+#define NB_SUFFIXES 4
+
+static void get_human_readable_size(char *buf, int buf_size, int64_t size)
+{
+ static const char suffixes[NB_SUFFIXES] = "KMGT";
+ int64_t base;
+ int i;
+
+ if (size <= 999) {
+ snprintf(buf, buf_size, "%" PRId64, size);
+ } else {
+ base = 1024;
+ for(i = 0; i < NB_SUFFIXES; i++) {
+ if (size < (10 * base)) {
+ snprintf(buf, buf_size, "%0.1f%c",
+ (double)size / base,
+ suffixes[i]);
+ break;
+ } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
+ snprintf(buf, buf_size, "%" PRId64 "%c",
+ ((size + (base >> 1)) / base),
+ suffixes[i]);
+ break;
+ }
+ base = base * 1024;
+ }
+ }
+}
+
+#if defined(WIN32)
+/* XXX: put correct support for win32 */
+static int read_password(char *buf, int buf_size)
+{
+ int c, i;
+ printf("Password: ");
+ fflush(stdout);
+ i = 0;
+ for(;;) {
+ c = getchar();
+ if (c == '\n')
+ break;
+ if (i < (buf_size - 1))
+ buf[i++] = c;
+ }
+ buf[i] = '\0';
+ return 0;
+}
+
+#else
+
+#include <termios.h>
+
+static struct termios oldtty;
+
+static void term_exit(void)
+{
+ tcsetattr (0, TCSANOW, &oldtty);
+}
+
+static void term_init(void)
+{
+ struct termios tty;
+
+ tcgetattr (0, &tty);
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+
+ tcsetattr (0, TCSANOW, &tty);
+
+ atexit(term_exit);
+}
+
+int read_password(char *buf, int buf_size)
+{
+ uint8_t ch;
+ int i, ret;
+
+ printf("password: ");
+ fflush(stdout);
+ term_init();
+ i = 0;
+ for(;;) {
+ ret = read(0, &ch, 1);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ } else {
+ ret = -1;
+ break;
+ }
+ } else if (ret == 0) {
+ ret = -1;
+ break;
+ } else {
+ if (ch == '\r') {
+ ret = 0;
+ break;
+ }
+ if (i < (buf_size - 1))
+ buf[i++] = ch;
+ }
+ }
+ term_exit();
+ buf[i] = '\0';
+ printf("\n");
+ return ret;
+}
+#endif
+
+static BlockDriverState *bdrv_new_open(const char *filename,
+ const char *fmt)
+{
+ BlockDriverState *bs;
+ BlockDriver *drv;
+ char password[256];
+
+ bs = bdrv_new("");
+ if (!bs)
+ error("Not enough memory");
+ if (fmt) {
+ drv = bdrv_find_format(fmt);
+ if (!drv)
+ error("Unknown file format '%s'", fmt);
+ } else {
+ drv = NULL;
+ }
+ if (bdrv_open2(bs, filename, 0, drv) < 0) {
+ error("Could not open '%s'", filename);
+ }
+ if (bdrv_is_encrypted(bs)) {
+ printf("Disk image '%s' is encrypted.\n", filename);
+ if (read_password(password, sizeof(password)) < 0)
+ error("No password given");
+ if (bdrv_set_key(bs, password) < 0)
+ error("invalid password");
+ }
+ return bs;
+}
+
+static int img_create(int argc, char **argv)
+{
+ int c, ret, encrypted;
+ const char *fmt = "raw";
+ const char *filename;
+ const char *base_filename = NULL;
+ int64_t size;
+ const char *p;
+ BlockDriver *drv;
+
+ encrypted = 0;
+ for(;;) {
+ c = getopt(argc, argv, "b:f:he");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ break;
+ case 'b':
+ base_filename = optarg;
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'e':
+ encrypted = 1;
+ break;
+ }
+ }
+ if (optind >= argc)
+ help();
+ filename = argv[optind++];
+ size = 0;
+ if (base_filename) {
+ BlockDriverState *bs;
+ bs = bdrv_new_open(base_filename, NULL);
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
+ bdrv_delete(bs);
+ } else {
+ if (optind >= argc)
+ help();
+ p = argv[optind];
+ size = strtoul(p, (char **)&p, 0);
+ if (*p == 'M') {
+ size *= 1024 * 1024;
+ } else if (*p == 'G') {
+ size *= 1024 * 1024 * 1024;
+ } else if (*p == 'k' || *p == 'K' || *p == '\0') {
+ size *= 1024;
+ } else {
+ help();
+ }
+ }
+ drv = bdrv_find_format(fmt);
+ if (!drv)
+ error("Unknown file format '%s'", fmt);
+ printf("Formating '%s', fmt=%s",
+ filename, fmt);
+ if (encrypted)
+ printf(", encrypted");
+ if (base_filename) {
+ printf(", backing_file=%s",
+ base_filename);
+ }
+ printf(", size=%" PRId64 " kB\n", (int64_t) (size / 1024));
+ ret = bdrv_create(drv, filename, size / 512, base_filename, encrypted);
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error("Formatting or formatting option not supported for file format '%s'", fmt);
+ } else {
+ error("Error while formatting");
+ }
+ }
+ return 0;
+}
+
+static int img_commit(int argc, char **argv)
+{
+ int c, ret;
+ const char *filename, *fmt;
+ BlockDriver *drv;
+ BlockDriverState *bs;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind >= argc)
+ help();
+ filename = argv[optind++];
+
+ bs = bdrv_new("");
+ if (!bs)
+ error("Not enough memory");
+ if (fmt) {
+ drv = bdrv_find_format(fmt);
+ if (!drv)
+ error("Unknown file format '%s'", fmt);
+ } else {
+ drv = NULL;
+ }
+ if (bdrv_open2(bs, filename, 0, drv) < 0) {
+ error("Could not open '%s'", filename);
+ }
+ ret = bdrv_commit(bs);
+ switch(ret) {
+ case 0:
+ printf("Image committed.\n");
+ break;
+ case -ENOENT:
+ error("No disk inserted");
+ break;
+ case -EACCES:
+ error("Image is read-only");
+ break;
+ case -ENOTSUP:
+ error("Image is already committed");
+ break;
+ default:
+ error("Error while committing image");
+ break;
+ }
+
+ bdrv_delete(bs);
+ return 0;
+}
+
+static int is_not_zero(const uint8_t *sector, int len)
+{
+ int i;
+ len >>= 2;
+ for(i = 0;i < len; i++) {
+ if (((uint32_t *)sector)[i] != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
+{
+ int v, i;
+
+ if (n <= 0) {
+ *pnum = 0;
+ return 0;
+ }
+ v = is_not_zero(buf, 512);
+ for(i = 1; i < n; i++) {
+ buf += 512;
+ if (v != is_not_zero(buf, 512))
+ break;
+ }
+ *pnum = i;
+ return v;
+}
+
+#define IO_BUF_SIZE 65536
+
+static int img_convert(int argc, char **argv)
+{
+ int c, ret, n, n1, compress, cluster_size, cluster_sectors, encrypt;
+ const char *filename, *fmt, *out_fmt, *out_filename;
+ BlockDriver *drv;
+ BlockDriverState *bs, *out_bs;
+ int64_t total_sectors, nb_sectors, sector_num;
+ uint8_t buf[IO_BUF_SIZE];
+ const uint8_t *buf1;
+
+ fmt = NULL;
+ out_fmt = "raw";
+ compress = 0;
+ encrypt = 0;
+ for(;;) {
+ c = getopt(argc, argv, "f:O:hce");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'O':
+ out_fmt = optarg;
+ break;
+ case 'c':
+ compress = 1;
+ break;
+ case 'e':
+ encrypt = 1;
+ break;
+ }
+ }
+ if (optind >= argc)
+ help();
+ filename = argv[optind++];
+ if (optind >= argc)
+ help();
+ out_filename = argv[optind++];
+
+ bs = bdrv_new_open(filename, fmt);
+
+ drv = bdrv_find_format(out_fmt);
+ if (!drv)
+ error("Unknown file format '%s'", fmt);
+ if (compress && drv != &bdrv_qcow)
+ error("Compression not supported for this file format");
+ if (encrypt && drv != &bdrv_qcow)
+ error("Encryption not supported for this file format");
+ if (compress && encrypt)
+ error("Compression and encryption not supported at the same time");
+ bdrv_get_geometry(bs, &total_sectors);
+ ret = bdrv_create(drv, out_filename, total_sectors, NULL, encrypt);
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error("Formatting not supported for file format '%s'", fmt);
+ } else {
+ error("Error while formatting '%s'", out_filename);
+ }
+ }
+
+ out_bs = bdrv_new_open(out_filename, out_fmt);
+
+ if (compress) {
+ cluster_size = qcow_get_cluster_size(out_bs);
+ if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE)
+ error("invalid cluster size");
+ cluster_sectors = cluster_size >> 9;
+ sector_num = 0;
+ for(;;) {
+ nb_sectors = total_sectors - sector_num;
+ if (nb_sectors <= 0)
+ break;
+ if (nb_sectors >= cluster_sectors)
+ n = cluster_sectors;
+ else
+ n = nb_sectors;
+ if (bdrv_read(bs, sector_num, buf, n) < 0)
+ error("error while reading");
+ if (n < cluster_sectors)
+ memset(buf + n * 512, 0, cluster_size - n * 512);
+ if (is_not_zero(buf, cluster_size)) {
+ if (qcow_compress_cluster(out_bs, sector_num, buf) != 0)
+ error("error while compressing sector %" PRId64,
+ sector_num);
+ }
+ sector_num += n;
+ }
+ } else {
+ sector_num = 0;
+ for(;;) {
+ nb_sectors = total_sectors - sector_num;
+ if (nb_sectors <= 0)
+ break;
+ if (nb_sectors >= (IO_BUF_SIZE / 512))
+ n = (IO_BUF_SIZE / 512);
+ else
+ n = nb_sectors;
+ if (bdrv_read(bs, sector_num, buf, n) < 0)
+ error("error while reading");
+ /* NOTE: at the same time we convert, we do not write zero
+ sectors to have a chance to compress the image. Ideally, we
+ should add a specific call to have the info to go faster */
+ buf1 = buf;
+ while (n > 0) {
+ if (is_allocated_sectors(buf1, n, &n1)) {
+ if (bdrv_write(out_bs, sector_num, buf1, n1) < 0)
+ error("error while writing");
+ }
+ sector_num += n1;
+ n -= n1;
+ buf1 += n1 * 512;
+ }
+ }
+ }
+ bdrv_delete(out_bs);
+ bdrv_delete(bs);
+ return 0;
+}
+
+#ifdef _WIN32
+static int64_t get_allocated_file_size(const char *filename)
+{
+ typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
+ get_compressed_t get_compressed;
+ struct _stati64 st;
+
+ /* WinNT support GetCompressedFileSize to determine allocate size */
+ get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
+ if (get_compressed) {
+ DWORD high, low;
+ low = get_compressed(filename, &high);
+ if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
+ return (((int64_t) high) << 32) + low;
+ }
+
+ if (_stati64(filename, &st) < 0)
+ return -1;
+ return st.st_size;
+}
+#else
+static int64_t get_allocated_file_size(const char *filename)
+{
+ struct stat st;
+ if (stat(filename, &st) < 0)
+ return -1;
+ return (int64_t)st.st_blocks * 512;
+}
+#endif
+
+static int img_info(int argc, char **argv)
+{
+ int c;
+ const char *filename, *fmt;
+ BlockDriver *drv;
+ BlockDriverState *bs;
+ char fmt_name[128], size_buf[128], dsize_buf[128];
+ int64_t total_sectors, allocated_size;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind >= argc)
+ help();
+ filename = argv[optind++];
+
+ bs = bdrv_new("");
+ if (!bs)
+ error("Not enough memory");
+ if (fmt) {
+ drv = bdrv_find_format(fmt);
+ if (!drv)
+ error("Unknown file format '%s'", fmt);
+ } else {
+ drv = NULL;
+ }
+ if (bdrv_open2(bs, filename, 0, drv) < 0) {
+ error("Could not open '%s'", filename);
+ }
+ bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
+ bdrv_get_geometry(bs, &total_sectors);
+ get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
+ allocated_size = get_allocated_file_size(filename);
+ if (allocated_size < 0)
+ sprintf(dsize_buf, "unavailable");
+ else
+ get_human_readable_size(dsize_buf, sizeof(dsize_buf),
+ allocated_size);
+ printf("image: %s\n"
+ "file format: %s\n"
+ "virtual size: %s (%" PRId64 " bytes)\n"
+ "disk size: %s\n",
+ filename, fmt_name, size_buf,
+ (total_sectors * 512),
+ dsize_buf);
+ if (bdrv_is_encrypted(bs))
+ printf("encrypted: yes\n");
+ bdrv_delete(bs);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *cmd;
+
+ bdrv_init();
+ if (argc < 2)
+ help();
+ cmd = argv[1];
+ optind++;
+ if (!strcmp(cmd, "create")) {
+ img_create(argc, argv);
+ } else if (!strcmp(cmd, "commit")) {
+ img_commit(argc, argv);
+ } else if (!strcmp(cmd, "convert")) {
+ img_convert(argc, argv);
+ } else if (!strcmp(cmd, "info")) {
+ img_info(argc, argv);
+ } else {
+ help();
+ }
+ return 0;
+}
diff --git a/qemu-img.texi b/qemu-img.texi
new file mode 100644
index 0000000..ac7923f
--- /dev/null
+++ b/qemu-img.texi
@@ -0,0 +1,126 @@
+@example
+@c man begin SYNOPSIS
+usage: qemu-img command [command options]
+@c man end
+@end example
+
+@c man begin OPTIONS
+
+The following commands are supported:
+@table @option
+@item create [-e] [-b @var{base_image}] [-f @var{fmt}] @var{filename} [@var{size}]
+@item commit [-f @var{fmt}] @var{filename}
+@item convert [-c] [-e] [-f @var{fmt}] @var{filename} [-O @var{output_fmt}] @var{output_filename}
+@item info [-f @var{fmt}] @var{filename}
+@end table
+
+Command parameters:
+@table @var
+@item filename
+ is a disk image filename
+@item base_image
+is the read-only disk image which is used as base for a copy on
+ write image; the copy on write image only stores the modified data
+
+@item fmt
+is the disk image format. It is guessed automatically in most cases. The following formats are supported:
+
+@table @code
+@item raw
+
+Raw disk image format (default). This format has the advantage of
+being simple and easily exportable to all other emulators. If your file
+system supports @emph{holes} (for example in ext2 or ext3 on Linux),
+then only the written sectors will reserve space. Use @code{qemu-img
+info} to know the real size used by the image or @code{ls -ls} on
+Unix/Linux.
+
+@item qcow
+QEMU image format, the most versatile format. Use it to have smaller
+images (useful if your filesystem does not supports holes, for example
+on Windows), optional AES encryption and zlib based compression.
+@item cow
+User Mode Linux Copy On Write image format. Used to be the only growable
+image format in QEMU. It is supported only for compatibility with
+previous versions. It does not work on win32.
+@item vmdk
+VMware 3 and 4 compatible image format.
+@item cloop
+Linux Compressed Loop image, useful only to reuse directly compressed
+CD-ROM images present for example in the Knoppix CD-ROMs.
+@end table
+
+@item size
+is the disk image size in kilobytes. Optional suffixes @code{M}
+(megabyte) and @code{G} (gigabyte) are supported
+
+@item output_filename
+is the destination disk image filename
+
+@item output_fmt
+ is the destination format
+
+@item -c
+indicates that target image must be compressed (qcow format only)
+@item -e
+indicates that the target image must be encrypted (qcow format only)
+@end table
+
+Command description:
+
+@table @option
+@item create [-e] [-b @var{base_image}] [-f @var{fmt}] @var{filename} [@var{size}]
+
+Create the new disk image @var{filename} of size @var{size} and format
+@var{fmt}.
+
+If @var{base_image} is specified, then the image will record only the
+differences from @var{base_image}. No size needs to be specified in
+this case. @var{base_image} will never be modified unless you use the
+@code{commit} monitor command.
+
+@item commit [-f @var{fmt}] @var{filename}
+
+Commit the changes recorded in @var{filename} in its base image.
+
+@item convert [-c] [-e] [-f @var{fmt}] @var{filename} [-O @var{output_fmt}] @var{output_filename}
+
+Convert the disk image @var{filename} to disk image @var{output_filename}
+using format @var{output_fmt}. It can be optionnaly encrypted
+(@code{-e} option) or compressed (@code{-c} option).
+
+Only the format @code{qcow} supports encryption or compression. The
+compression is read-only. It means that if a compressed sector is
+rewritten, then it is rewritten as uncompressed data.
+
+Encryption uses the AES format which is very secure (128 bit keys). Use
+a long password (16 characters) to get maximum protection.
+
+Image conversion is also useful to get smaller image when using a
+growable format such as @code{qcow} or @code{cow}: the empty sectors
+are detected and suppressed from the destination image.
+
+@item info [-f @var{fmt}] @var{filename}
+
+Give information about the disk image @var{filename}. Use it in
+particular to know the size reserved on disk which can be different
+from the displayed size.
+@end table
+
+@c man end
+
+@ignore
+
+@setfilename qemu-img
+@settitle QEMU disk image utility
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more precise information and Linux
+user mode emulator invocation.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
diff --git a/qemu-tech.texi b/qemu-tech.texi
new file mode 100644
index 0000000..77bda86
--- /dev/null
+++ b/qemu-tech.texi
@@ -0,0 +1,595 @@
+\input texinfo @c -*- texinfo -*-
+@c %**start of header
+@setfilename qemu-tech.info
+@settitle QEMU Internals
+@exampleindent 0
+@paragraphindent 0
+@c %**end of header
+
+@iftex
+@titlepage
+@sp 7
+@center @titlefont{QEMU Internals}
+@sp 3
+@end titlepage
+@end iftex
+
+@ifnottex
+@node Top
+@top
+
+@menu
+* Introduction::
+* QEMU Internals::
+* Regression Tests::
+* Index::
+@end menu
+@end ifnottex
+
+@contents
+
+@node Introduction
+@chapter Introduction
+
+@menu
+* intro_features:: Features
+* intro_x86_emulation:: x86 emulation
+* intro_arm_emulation:: ARM emulation
+* intro_ppc_emulation:: PowerPC emulation
+* intro_sparc_emulation:: SPARC emulation
+@end menu
+
+@node intro_features
+@section Features
+
+QEMU is a FAST! processor emulator using a portable dynamic
+translator.
+
+QEMU has two operating modes:
+
+@itemize @minus
+
+@item
+Full system emulation. In this mode, QEMU emulates a full system
+(usually a PC), including a processor and various peripherals. It can
+be used to launch an different Operating System without rebooting the
+PC or to debug system code.
+
+@item
+User mode emulation (Linux host only). In this mode, QEMU can launch
+Linux processes compiled for one CPU on another CPU. It can be used to
+launch the Wine Windows API emulator (@url{http://www.winehq.org}) or
+to ease cross-compilation and cross-debugging.
+
+@end itemize
+
+As QEMU requires no host kernel driver to run, it is very safe and
+easy to use.
+
+QEMU generic features:
+
+@itemize
+
+@item User space only or full system emulation.
+
+@item Using dynamic translation to native code for reasonable speed.
+
+@item Working on x86 and PowerPC hosts. Being tested on ARM, Sparc32, Alpha and S390.
+
+@item Self-modifying code support.
+
+@item Precise exceptions support.
+
+@item The virtual CPU is a library (@code{libqemu}) which can be used
+in other projects (look at @file{qemu/tests/qruncom.c} to have an
+example of user mode @code{libqemu} usage).
+
+@end itemize
+
+QEMU user mode emulation features:
+@itemize
+@item Generic Linux system call converter, including most ioctls.
+
+@item clone() emulation using native CPU clone() to use Linux scheduler for threads.
+
+@item Accurate signal handling by remapping host signals to target signals.
+@end itemize
+
+QEMU full system emulation features:
+@itemize
+@item QEMU can either use a full software MMU for maximum portability or use the host system call mmap() to simulate the target MMU.
+@end itemize
+
+@node intro_x86_emulation
+@section x86 emulation
+
+QEMU x86 target features:
+
+@itemize
+
+@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation.
+LDT/GDT and IDT are emulated. VM86 mode is also supported to run DOSEMU.
+
+@item Support of host page sizes bigger than 4KB in user mode emulation.
+
+@item QEMU can emulate itself on x86.
+
+@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}.
+It can be used to test other x86 virtual CPUs.
+
+@end itemize
+
+Current QEMU limitations:
+
+@itemize
+
+@item No SSE/MMX support (yet).
+
+@item No x86-64 support.
+
+@item IPC syscalls are missing.
+
+@item The x86 segment limits and access rights are not tested at every
+memory access (yet). Hopefully, very few OSes seem to rely on that for
+normal use.
+
+@item On non x86 host CPUs, @code{double}s are used instead of the non standard
+10 byte @code{long double}s of x86 for floating point emulation to get
+maximum performances.
+
+@end itemize
+
+@node intro_arm_emulation
+@section ARM emulation
+
+@itemize
+
+@item Full ARM 7 user emulation.
+
+@item NWFPE FPU support included in user Linux emulation.
+
+@item Can run most ARM Linux binaries.
+
+@end itemize
+
+@node intro_ppc_emulation
+@section PowerPC emulation
+
+@itemize
+
+@item Full PowerPC 32 bit emulation, including privileged instructions,
+FPU and MMU.
+
+@item Can run most PowerPC Linux binaries.
+
+@end itemize
+
+@node intro_sparc_emulation
+@section SPARC emulation
+
+@itemize
+
+@item Somewhat complete SPARC V8 emulation, including privileged
+instructions, FPU and MMU. SPARC V9 emulation includes most privileged
+instructions, FPU and I/D MMU, but misses VIS instructions.
+
+@item Can run some 32-bit SPARC Linux binaries.
+
+@end itemize
+
+Current QEMU limitations:
+
+@itemize
+
+@item Tagged add/subtract instructions are not supported, but they are
+probably not used.
+
+@item IPC syscalls are missing.
+
+@item 128-bit floating point operations are not supported, though none of the
+real CPUs implement them either. FCMPE[SD] are not correctly
+implemented. Floating point exception support is untested.
+
+@item Alignment is not enforced at all.
+
+@item Atomic instructions are not correctly implemented.
+
+@item Sparc64 emulators are not usable for anything yet.
+
+@end itemize
+
+@node QEMU Internals
+@chapter QEMU Internals
+
+@menu
+* QEMU compared to other emulators::
+* Portable dynamic translation::
+* Register allocation::
+* Condition code optimisations::
+* CPU state optimisations::
+* Translation cache::
+* Direct block chaining::
+* Self-modifying code and translated code invalidation::
+* Exception support::
+* MMU emulation::
+* Hardware interrupts::
+* User emulation specific details::
+* Bibliography::
+@end menu
+
+@node QEMU compared to other emulators
+@section QEMU compared to other emulators
+
+Like bochs [3], QEMU emulates an x86 CPU. But QEMU is much faster than
+bochs as it uses dynamic compilation. Bochs is closely tied to x86 PC
+emulation while QEMU can emulate several processors.
+
+Like Valgrind [2], QEMU does user space emulation and dynamic
+translation. Valgrind is mainly a memory debugger while QEMU has no
+support for it (QEMU could be used to detect out of bound memory
+accesses as Valgrind, but it has no support to track uninitialised data
+as Valgrind does). The Valgrind dynamic translator generates better code
+than QEMU (in particular it does register allocation) but it is closely
+tied to an x86 host and target and has no support for precise exceptions
+and system emulation.
+
+EM86 [4] is the closest project to user space QEMU (and QEMU still uses
+some of its code, in particular the ELF file loader). EM86 was limited
+to an alpha host and used a proprietary and slow interpreter (the
+interpreter part of the FX!32 Digital Win32 code translator [5]).
+
+TWIN [6] is a Windows API emulator like Wine. It is less accurate than
+Wine but includes a protected mode x86 interpreter to launch x86 Windows
+executables. Such an approach has greater potential because most of the
+Windows API is executed natively but it is far more difficult to develop
+because all the data structures and function parameters exchanged
+between the API and the x86 code must be converted.
+
+User mode Linux [7] was the only solution before QEMU to launch a
+Linux kernel as a process while not needing any host kernel
+patches. However, user mode Linux requires heavy kernel patches while
+QEMU accepts unpatched Linux kernels. The price to pay is that QEMU is
+slower.
+
+The new Plex86 [8] PC virtualizer is done in the same spirit as the
+qemu-fast system emulator. It requires a patched Linux kernel to work
+(you cannot launch the same kernel on your PC), but the patches are
+really small. As it is a PC virtualizer (no emulation is done except
+for some priveledged instructions), it has the potential of being
+faster than QEMU. The downside is that a complicated (and potentially
+unsafe) host kernel patch is needed.
+
+The commercial PC Virtualizers (VMWare [9], VirtualPC [10], TwoOStwo
+[11]) are faster than QEMU, but they all need specific, proprietary
+and potentially unsafe host drivers. Moreover, they are unable to
+provide cycle exact simulation as an emulator can.
+
+@node Portable dynamic translation
+@section Portable dynamic translation
+
+QEMU is a dynamic translator. When it first encounters a piece of code,
+it converts it to the host instruction set. Usually dynamic translators
+are very complicated and highly CPU dependent. QEMU uses some tricks
+which make it relatively easily portable and simple while achieving good
+performances.
+
+The basic idea is to split every x86 instruction into fewer simpler
+instructions. Each simple instruction is implemented by a piece of C
+code (see @file{target-i386/op.c}). Then a compile time tool
+(@file{dyngen}) takes the corresponding object file (@file{op.o})
+to generate a dynamic code generator which concatenates the simple
+instructions to build a function (see @file{op.h:dyngen_code()}).
+
+In essence, the process is similar to [1], but more work is done at
+compile time.
+
+A key idea to get optimal performances is that constant parameters can
+be passed to the simple operations. For that purpose, dummy ELF
+relocations are generated with gcc for each constant parameter. Then,
+the tool (@file{dyngen}) can locate the relocations and generate the
+appriopriate C code to resolve them when building the dynamic code.
+
+That way, QEMU is no more difficult to port than a dynamic linker.
+
+To go even faster, GCC static register variables are used to keep the
+state of the virtual CPU.
+
+@node Register allocation
+@section Register allocation
+
+Since QEMU uses fixed simple instructions, no efficient register
+allocation can be done. However, because RISC CPUs have a lot of
+register, most of the virtual CPU state can be put in registers without
+doing complicated register allocation.
+
+@node Condition code optimisations
+@section Condition code optimisations
+
+Good CPU condition codes emulation (@code{EFLAGS} register on x86) is a
+critical point to get good performances. QEMU uses lazy condition code
+evaluation: instead of computing the condition codes after each x86
+instruction, it just stores one operand (called @code{CC_SRC}), the
+result (called @code{CC_DST}) and the type of operation (called
+@code{CC_OP}).
+
+@code{CC_OP} is almost never explicitely set in the generated code
+because it is known at translation time.
+
+In order to increase performances, a backward pass is performed on the
+generated simple instructions (see
+@code{target-i386/translate.c:optimize_flags()}). When it can be proved that
+the condition codes are not needed by the next instructions, no
+condition codes are computed at all.
+
+@node CPU state optimisations
+@section CPU state optimisations
+
+The x86 CPU has many internal states which change the way it evaluates
+instructions. In order to achieve a good speed, the translation phase
+considers that some state information of the virtual x86 CPU cannot
+change in it. For example, if the SS, DS and ES segments have a zero
+base, then the translator does not even generate an addition for the
+segment base.
+
+[The FPU stack pointer register is not handled that way yet].
+
+@node Translation cache
+@section Translation cache
+
+A 16 MByte cache holds the most recently used translations. For
+simplicity, it is completely flushed when it is full. A translation unit
+contains just a single basic block (a block of x86 instructions
+terminated by a jump or by a virtual CPU state change which the
+translator cannot deduce statically).
+
+@node Direct block chaining
+@section Direct block chaining
+
+After each translated basic block is executed, QEMU uses the simulated
+Program Counter (PC) and other cpu state informations (such as the CS
+segment base value) to find the next basic block.
+
+In order to accelerate the most common cases where the new simulated PC
+is known, QEMU can patch a basic block so that it jumps directly to the
+next one.
+
+The most portable code uses an indirect jump. An indirect jump makes
+it easier to make the jump target modification atomic. On some host
+architectures (such as x86 or PowerPC), the @code{JUMP} opcode is
+directly patched so that the block chaining has no overhead.
+
+@node Self-modifying code and translated code invalidation
+@section Self-modifying code and translated code invalidation
+
+Self-modifying code is a special challenge in x86 emulation because no
+instruction cache invalidation is signaled by the application when code
+is modified.
+
+When translated code is generated for a basic block, the corresponding
+host page is write protected if it is not already read-only (with the
+system call @code{mprotect()}). Then, if a write access is done to the
+page, Linux raises a SEGV signal. QEMU then invalidates all the
+translated code in the page and enables write accesses to the page.
+
+Correct translated code invalidation is done efficiently by maintaining
+a linked list of every translated block contained in a given page. Other
+linked lists are also maintained to undo direct block chaining.
+
+Although the overhead of doing @code{mprotect()} calls is important,
+most MSDOS programs can be emulated at reasonnable speed with QEMU and
+DOSEMU.
+
+Note that QEMU also invalidates pages of translated code when it detects
+that memory mappings are modified with @code{mmap()} or @code{munmap()}.
+
+When using a software MMU, the code invalidation is more efficient: if
+a given code page is invalidated too often because of write accesses,
+then a bitmap representing all the code inside the page is
+built. Every store into that page checks the bitmap to see if the code
+really needs to be invalidated. It avoids invalidating the code when
+only data is modified in the page.
+
+@node Exception support
+@section Exception support
+
+longjmp() is used when an exception such as division by zero is
+encountered.
+
+The host SIGSEGV and SIGBUS signal handlers are used to get invalid
+memory accesses. The exact CPU state can be retrieved because all the
+x86 registers are stored in fixed host registers. The simulated program
+counter is found by retranslating the corresponding basic block and by
+looking where the host program counter was at the exception point.
+
+The virtual CPU cannot retrieve the exact @code{EFLAGS} register because
+in some cases it is not computed because of condition code
+optimisations. It is not a big concern because the emulated code can
+still be restarted in any cases.
+
+@node MMU emulation
+@section MMU emulation
+
+For system emulation, QEMU uses the mmap() system call to emulate the
+target CPU MMU. It works as long the emulated OS does not use an area
+reserved by the host OS (such as the area above 0xc0000000 on x86
+Linux).
+
+In order to be able to launch any OS, QEMU also supports a soft
+MMU. In that mode, the MMU virtual to physical address translation is
+done at every memory access. QEMU uses an address translation cache to
+speed up the translation.
+
+In order to avoid flushing the translated code each time the MMU
+mappings change, QEMU uses a physically indexed translation cache. It
+means that each basic block is indexed with its physical address.
+
+When MMU mappings change, only the chaining of the basic blocks is
+reset (i.e. a basic block can no longer jump directly to another one).
+
+@node Hardware interrupts
+@section Hardware interrupts
+
+In order to be faster, QEMU does not check at every basic block if an
+hardware interrupt is pending. Instead, the user must asynchrously
+call a specific function to tell that an interrupt is pending. This
+function resets the chaining of the currently executing basic
+block. It ensures that the execution will return soon in the main loop
+of the CPU emulator. Then the main loop can test if the interrupt is
+pending and handle it.
+
+@node User emulation specific details
+@section User emulation specific details
+
+@subsection Linux system call translation
+
+QEMU includes a generic system call translator for Linux. It means that
+the parameters of the system calls can be converted to fix the
+endianness and 32/64 bit issues. The IOCTLs are converted with a generic
+type description system (see @file{ioctls.h} and @file{thunk.c}).
+
+QEMU supports host CPUs which have pages bigger than 4KB. It records all
+the mappings the process does and try to emulated the @code{mmap()}
+system calls in cases where the host @code{mmap()} call would fail
+because of bad page alignment.
+
+@subsection Linux signals
+
+Normal and real-time signals are queued along with their information
+(@code{siginfo_t}) as it is done in the Linux kernel. Then an interrupt
+request is done to the virtual CPU. When it is interrupted, one queued
+signal is handled by generating a stack frame in the virtual CPU as the
+Linux kernel does. The @code{sigreturn()} system call is emulated to return
+from the virtual signal handler.
+
+Some signals (such as SIGALRM) directly come from the host. Other
+signals are synthetized from the virtual CPU exceptions such as SIGFPE
+when a division by zero is done (see @code{main.c:cpu_loop()}).
+
+The blocked signal mask is still handled by the host Linux kernel so
+that most signal system calls can be redirected directly to the host
+Linux kernel. Only the @code{sigaction()} and @code{sigreturn()} system
+calls need to be fully emulated (see @file{signal.c}).
+
+@subsection clone() system call and threads
+
+The Linux clone() system call is usually used to create a thread. QEMU
+uses the host clone() system call so that real host threads are created
+for each emulated thread. One virtual CPU instance is created for each
+thread.
+
+The virtual x86 CPU atomic operations are emulated with a global lock so
+that their semantic is preserved.
+
+Note that currently there are still some locking issues in QEMU. In
+particular, the translated cache flush is not protected yet against
+reentrancy.
+
+@subsection Self-virtualization
+
+QEMU was conceived so that ultimately it can emulate itself. Although
+it is not very useful, it is an important test to show the power of the
+emulator.
+
+Achieving self-virtualization is not easy because there may be address
+space conflicts. QEMU solves this problem by being an executable ELF
+shared object as the ld-linux.so ELF interpreter. That way, it can be
+relocated at load time.
+
+@node Bibliography
+@section Bibliography
+
+@table @asis
+
+@item [1]
+@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing
+direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio
+Riccardi.
+
+@item [2]
+@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source
+memory debugger for x86-GNU/Linux, by Julian Seward.
+
+@item [3]
+@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project,
+by Kevin Lawton et al.
+
+@item [4]
+@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86
+x86 emulator on Alpha-Linux.
+
+@item [5]
+@url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/@/full_papers/chernoff/chernoff.pdf},
+DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton
+Chernoff and Ray Hookway.
+
+@item [6]
+@url{http://www.willows.com/}, Windows API library emulation from
+Willows Software.
+
+@item [7]
+@url{http://user-mode-linux.sourceforge.net/},
+The User-mode Linux Kernel.
+
+@item [8]
+@url{http://www.plex86.org/},
+The new Plex86 project.
+
+@item [9]
+@url{http://www.vmware.com/},
+The VMWare PC virtualizer.
+
+@item [10]
+@url{http://www.microsoft.com/windowsxp/virtualpc/},
+The VirtualPC PC virtualizer.
+
+@item [11]
+@url{http://www.twoostwo.org/},
+The TwoOStwo PC virtualizer.
+
+@end table
+
+@node Regression Tests
+@chapter Regression Tests
+
+In the directory @file{tests/}, various interesting testing programs
+are available. There are used for regression testing.
+
+@menu
+* test-i386::
+* linux-test::
+* qruncom.c::
+@end menu
+
+@node test-i386
+@section @file{test-i386}
+
+This program executes most of the 16 bit and 32 bit x86 instructions and
+generates a text output. It can be compared with the output obtained with
+a real CPU or another emulator. The target @code{make test} runs this
+program and a @code{diff} on the generated output.
+
+The Linux system call @code{modify_ldt()} is used to create x86 selectors
+to test some 16 bit addressing and 32 bit with segmentation cases.
+
+The Linux system call @code{vm86()} is used to test vm86 emulation.
+
+Various exceptions are raised to test most of the x86 user space
+exception reporting.
+
+@node linux-test
+@section @file{linux-test}
+
+This program tests various Linux system calls. It is used to verify
+that the system call parameters are correctly converted between target
+and host CPUs.
+
+@node qruncom.c
+@section @file{qruncom.c}
+
+Example of usage of @code{libqemu} to emulate a user mode i386 CPU.
+
+@node Index
+@chapter Index
+@printindex cp
+
+@bye
diff --git a/qemu_socket.h b/qemu_socket.h
new file mode 100644
index 0000000..64b7d4e
--- /dev/null
+++ b/qemu_socket.h
@@ -0,0 +1,30 @@
+/* headers to use the BSD sockets */
+#ifndef QEMU_SOCKET_H
+#define QEMU_SOCKET_H
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#define socket_error() WSAGetLastError()
+#undef EINTR
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINTR WSAEINTR
+#define EINPROGRESS WSAEINPROGRESS
+
+#else
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#define socket_error() errno
+#define closesocket(s) close(s)
+
+#endif /* !_WIN32 */
+
+void socket_set_nonblock(int fd);
+
+#endif /* QEMU_SOCKET_H */
diff --git a/readline.c b/readline.c
new file mode 100644
index 0000000..cbe33db
--- /dev/null
+++ b/readline.c
@@ -0,0 +1,425 @@
+/*
+ * QEMU readline utility
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#define TERM_CMD_BUF_SIZE 4095
+#define TERM_MAX_CMDS 64
+#define NB_COMPLETIONS_MAX 256
+
+#define IS_NORM 0
+#define IS_ESC 1
+#define IS_CSI 2
+
+#define printf do_not_use_printf
+
+static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
+static int term_cmd_buf_index;
+static int term_cmd_buf_size;
+
+static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
+static int term_last_cmd_buf_index;
+static int term_last_cmd_buf_size;
+
+static int term_esc_state;
+static int term_esc_param;
+
+static char *term_history[TERM_MAX_CMDS];
+static int term_hist_entry = -1;
+
+static int nb_completions;
+int completion_index;
+static char *completions[NB_COMPLETIONS_MAX];
+
+static ReadLineFunc *term_readline_func;
+static int term_is_password;
+static char term_prompt[256];
+static void *term_readline_opaque;
+
+static void term_show_prompt2(void)
+{
+ term_printf("%s", term_prompt);
+ term_flush();
+ term_last_cmd_buf_index = 0;
+ term_last_cmd_buf_size = 0;
+ term_esc_state = IS_NORM;
+}
+
+static void term_show_prompt(void)
+{
+ term_show_prompt2();
+ term_cmd_buf_index = 0;
+ term_cmd_buf_size = 0;
+}
+
+/* update the displayed command line */
+static void term_update(void)
+{
+ int i, delta, len;
+
+ if (term_cmd_buf_size != term_last_cmd_buf_size ||
+ memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
+ for(i = 0; i < term_last_cmd_buf_index; i++) {
+ term_printf("\033[D");
+ }
+ term_cmd_buf[term_cmd_buf_size] = '\0';
+ if (term_is_password) {
+ len = strlen(term_cmd_buf);
+ for(i = 0; i < len; i++)
+ term_printf("*");
+ } else {
+ term_printf("%s", term_cmd_buf);
+ }
+ term_printf("\033[K");
+ memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
+ term_last_cmd_buf_size = term_cmd_buf_size;
+ term_last_cmd_buf_index = term_cmd_buf_size;
+ }
+ if (term_cmd_buf_index != term_last_cmd_buf_index) {
+ delta = term_cmd_buf_index - term_last_cmd_buf_index;
+ if (delta > 0) {
+ for(i = 0;i < delta; i++) {
+ term_printf("\033[C");
+ }
+ } else {
+ delta = -delta;
+ for(i = 0;i < delta; i++) {
+ term_printf("\033[D");
+ }
+ }
+ term_last_cmd_buf_index = term_cmd_buf_index;
+ }
+ term_flush();
+}
+
+static void term_insert_char(int ch)
+{
+ if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
+ memmove(term_cmd_buf + term_cmd_buf_index + 1,
+ term_cmd_buf + term_cmd_buf_index,
+ term_cmd_buf_size - term_cmd_buf_index);
+ term_cmd_buf[term_cmd_buf_index] = ch;
+ term_cmd_buf_size++;
+ term_cmd_buf_index++;
+ }
+}
+
+static void term_backward_char(void)
+{
+ if (term_cmd_buf_index > 0) {
+ term_cmd_buf_index--;
+ }
+}
+
+static void term_forward_char(void)
+{
+ if (term_cmd_buf_index < term_cmd_buf_size) {
+ term_cmd_buf_index++;
+ }
+}
+
+static void term_delete_char(void)
+{
+ if (term_cmd_buf_index < term_cmd_buf_size) {
+ memmove(term_cmd_buf + term_cmd_buf_index,
+ term_cmd_buf + term_cmd_buf_index + 1,
+ term_cmd_buf_size - term_cmd_buf_index - 1);
+ term_cmd_buf_size--;
+ }
+}
+
+static void term_backspace(void)
+{
+ if (term_cmd_buf_index > 0) {
+ term_backward_char();
+ term_delete_char();
+ }
+}
+
+static void term_bol(void)
+{
+ term_cmd_buf_index = 0;
+}
+
+static void term_eol(void)
+{
+ term_cmd_buf_index = term_cmd_buf_size;
+}
+
+static void term_up_char(void)
+{
+ int idx;
+
+ if (term_hist_entry == 0)
+ return;
+ if (term_hist_entry == -1) {
+ /* Find latest entry */
+ for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
+ if (term_history[idx] == NULL)
+ break;
+ }
+ term_hist_entry = idx;
+ }
+ term_hist_entry--;
+ if (term_hist_entry >= 0) {
+ pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
+ term_history[term_hist_entry]);
+ term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
+ }
+}
+
+static void term_down_char(void)
+{
+ if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
+ return;
+ if (term_history[++term_hist_entry] != NULL) {
+ pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
+ term_history[term_hist_entry]);
+ } else {
+ term_hist_entry = -1;
+ }
+ term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
+}
+
+static void term_hist_add(const char *cmdline)
+{
+ char *hist_entry, *new_entry;
+ int idx;
+
+ if (cmdline[0] == '\0')
+ return;
+ new_entry = NULL;
+ if (term_hist_entry != -1) {
+ /* We were editing an existing history entry: replace it */
+ hist_entry = term_history[term_hist_entry];
+ idx = term_hist_entry;
+ if (strcmp(hist_entry, cmdline) == 0) {
+ goto same_entry;
+ }
+ }
+ /* Search cmdline in history buffers */
+ for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
+ hist_entry = term_history[idx];
+ if (hist_entry == NULL)
+ break;
+ if (strcmp(hist_entry, cmdline) == 0) {
+ same_entry:
+ new_entry = hist_entry;
+ /* Put this entry at the end of history */
+ memmove(&term_history[idx], &term_history[idx + 1],
+ &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]);
+ term_history[TERM_MAX_CMDS - 1] = NULL;
+ for (; idx < TERM_MAX_CMDS; idx++) {
+ if (term_history[idx] == NULL)
+ break;
+ }
+ break;
+ }
+ }
+ if (idx == TERM_MAX_CMDS) {
+ /* Need to get one free slot */
+ free(term_history[0]);
+ memcpy(term_history, &term_history[1],
+ &term_history[TERM_MAX_CMDS] - &term_history[1]);
+ term_history[TERM_MAX_CMDS - 1] = NULL;
+ idx = TERM_MAX_CMDS - 1;
+ }
+ if (new_entry == NULL)
+ new_entry = strdup(cmdline);
+ term_history[idx] = new_entry;
+ term_hist_entry = -1;
+}
+
+/* completion support */
+
+void add_completion(const char *str)
+{
+ if (nb_completions < NB_COMPLETIONS_MAX) {
+ completions[nb_completions++] = qemu_strdup(str);
+ }
+}
+
+static void term_completion(void)
+{
+ int len, i, j, max_width, nb_cols;
+ char *cmdline;
+
+ nb_completions = 0;
+
+ cmdline = qemu_malloc(term_cmd_buf_index + 1);
+ if (!cmdline)
+ return;
+ memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
+ cmdline[term_cmd_buf_index] = '\0';
+ readline_find_completion(cmdline);
+ qemu_free(cmdline);
+
+ /* no completion found */
+ if (nb_completions <= 0)
+ return;
+ if (nb_completions == 1) {
+ len = strlen(completions[0]);
+ for(i = completion_index; i < len; i++) {
+ term_insert_char(completions[0][i]);
+ }
+ /* extra space for next argument. XXX: make it more generic */
+ if (len > 0 && completions[0][len - 1] != '/')
+ term_insert_char(' ');
+ } else {
+ term_printf("\n");
+ max_width = 0;
+ for(i = 0; i < nb_completions; i++) {
+ len = strlen(completions[i]);
+ if (len > max_width)
+ max_width = len;
+ }
+ max_width += 2;
+ if (max_width < 10)
+ max_width = 10;
+ else if (max_width > 80)
+ max_width = 80;
+ nb_cols = 80 / max_width;
+ j = 0;
+ for(i = 0; i < nb_completions; i++) {
+ term_printf("%-*s", max_width, completions[i]);
+ if (++j == nb_cols || i == (nb_completions - 1)) {
+ term_printf("\n");
+ j = 0;
+ }
+ }
+ term_show_prompt2();
+ }
+}
+
+/* return true if command handled */
+void readline_handle_byte(int ch)
+{
+ switch(term_esc_state) {
+ case IS_NORM:
+ switch(ch) {
+ case 1:
+ term_bol();
+ break;
+ case 4:
+ term_delete_char();
+ break;
+ case 5:
+ term_eol();
+ break;
+ case 9:
+ term_completion();
+ break;
+ case 10:
+ case 13:
+ term_cmd_buf[term_cmd_buf_size] = '\0';
+ if (!term_is_password)
+ term_hist_add(term_cmd_buf);
+ term_printf("\n");
+ /* NOTE: readline_start can be called here */
+ term_readline_func(term_readline_opaque, term_cmd_buf);
+ break;
+ case 27:
+ term_esc_state = IS_ESC;
+ break;
+ case 127:
+ case 8:
+ term_backspace();
+ break;
+ case 155:
+ term_esc_state = IS_CSI;
+ break;
+ default:
+ if (ch >= 32) {
+ term_insert_char(ch);
+ }
+ break;
+ }
+ break;
+ case IS_ESC:
+ if (ch == '[') {
+ term_esc_state = IS_CSI;
+ term_esc_param = 0;
+ } else {
+ term_esc_state = IS_NORM;
+ }
+ break;
+ case IS_CSI:
+ switch(ch) {
+ case 'A':
+ case 'F':
+ term_up_char();
+ break;
+ case 'B':
+ case 'E':
+ term_down_char();
+ break;
+ case 'D':
+ term_backward_char();
+ break;
+ case 'C':
+ term_forward_char();
+ break;
+ case '0' ... '9':
+ term_esc_param = term_esc_param * 10 + (ch - '0');
+ goto the_end;
+ case '~':
+ switch(term_esc_param) {
+ case 1:
+ term_bol();
+ break;
+ case 3:
+ term_delete_char();
+ break;
+ case 4:
+ term_eol();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ term_esc_state = IS_NORM;
+ the_end:
+ break;
+ }
+ term_update();
+}
+
+void readline_start(const char *prompt, int is_password,
+ ReadLineFunc *readline_func, void *opaque)
+{
+ pstrcpy(term_prompt, sizeof(term_prompt), prompt);
+ term_readline_func = readline_func;
+ term_readline_opaque = opaque;
+ term_is_password = is_password;
+ term_show_prompt();
+}
+
+const char *readline_get_history(unsigned int index)
+{
+ if (index >= TERM_MAX_CMDS)
+ return NULL;
+ return term_history[index];
+}
+
+
diff --git a/s390.ld b/s390.ld
new file mode 100644
index 0000000..7f14ea9
--- /dev/null
+++ b/s390.ld
@@ -0,0 +1,204 @@
+OUTPUT_FORMAT("elf32-s390", "elf32-s390",
+ "elf32-s390")
+OUTPUT_ARCH(s390:31-bit)
+ENTRY(_start)
+SEARCH_DIR("/usr/s390-redhat-linux/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib");
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+ *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+ *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+ *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x07070707
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x07070707
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x07070707
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x1000) + (. & (0x1000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}
+
diff --git a/sdl.c b/sdl.c
new file mode 100644
index 0000000..38d7553
--- /dev/null
+++ b/sdl.c
@@ -0,0 +1,569 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#include <SDL.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#endif
+
+static SDL_Surface *screen;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+static int last_vm_running;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_fullscreen_initial_grab;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[256];
+static int width, height;
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled = 0;
+
+static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
+ SDL_UpdateRect(screen, x, y, w, h);
+}
+
+static void sdl_resize(DisplayState *ds, int w, int h)
+{
+ int flags;
+
+ // printf("resizing to %d %d\n", w, h);
+
+ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
+ if (gui_fullscreen)
+ flags |= SDL_FULLSCREEN;
+
+ width = w;
+ height = h;
+
+ again:
+ screen = SDL_SetVideoMode(w, h, 0, flags);
+ if (!screen) {
+ fprintf(stderr, "Could not open SDL display\n");
+ exit(1);
+ }
+ if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
+ flags &= ~SDL_HWSURFACE;
+ goto again;
+ }
+
+ if (!screen->pixels) {
+ fprintf(stderr, "Could not open SDL display\n");
+ exit(1);
+ }
+ ds->data = screen->pixels;
+ ds->linesize = screen->pitch;
+ ds->depth = screen->format->BitsPerPixel;
+ if (ds->depth == 32 && screen->format->Rshift == 0) {
+ ds->bgr = 1;
+ } else {
+ ds->bgr = 0;
+ }
+ ds->width = w;
+ ds->height = h;
+}
+
+/* generic keyboard conversion */
+
+#include "sdl_keysym.h"
+#include "keymaps.c"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
+{
+ int keysym;
+ /* workaround for X11+SDL bug with AltGR */
+ keysym = ev->keysym.sym;
+ if (keysym == 0 && ev->keysym.scancode == 113)
+ keysym = SDLK_MODE;
+ return keysym2scancode(kbd_layout, keysym);
+}
+
+/* specific keyboard conversions from scan codes */
+
+#if defined(_WIN32)
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ return ev->keysym.scancode;
+}
+
+#else
+
+static const uint8_t x_keycode_to_pc_keycode[61] = {
+ 0xc7, /* 97 Home */
+ 0xc8, /* 98 Up */
+ 0xc9, /* 99 PgUp */
+ 0xcb, /* 100 Left */
+ 0x4c, /* 101 KP-5 */
+ 0xcd, /* 102 Right */
+ 0xcf, /* 103 End */
+ 0xd0, /* 104 Down */
+ 0xd1, /* 105 PgDn */
+ 0xd2, /* 106 Ins */
+ 0xd3, /* 107 Del */
+ 0x9c, /* 108 Enter */
+ 0x9d, /* 109 Ctrl-R */
+ 0x0, /* 110 Pause */
+ 0xb7, /* 111 Print */
+ 0xb5, /* 112 Divide */
+ 0xb8, /* 113 Alt-R */
+ 0xc6, /* 114 Break */
+ 0x0, /* 115 */
+ 0x0, /* 116 */
+ 0x0, /* 117 */
+ 0x0, /* 118 */
+ 0x0, /* 119 */
+ 0x70, /* 120 Hiragana_Katakana */
+ 0x0, /* 121 */
+ 0x0, /* 122 */
+ 0x73, /* 123 backslash */
+ 0x0, /* 124 */
+ 0x0, /* 125 */
+ 0x0, /* 126 */
+ 0x0, /* 127 */
+ 0x0, /* 128 */
+ 0x79, /* 129 Henkan */
+ 0x0, /* 130 */
+ 0x7b, /* 131 Muhenkan */
+ 0x0, /* 132 */
+ 0x7d, /* 133 Yen */
+ 0x0, /* 134 */
+ 0x0, /* 135 */
+ 0x47, /* 136 KP_7 */
+ 0x48, /* 137 KP_8 */
+ 0x49, /* 138 KP_9 */
+ 0x4b, /* 139 KP_4 */
+ 0x4c, /* 140 KP_5 */
+ 0x4d, /* 141 KP_6 */
+ 0x4f, /* 142 KP_1 */
+ 0x50, /* 143 KP_2 */
+ 0x51, /* 144 KP_3 */
+ 0x52, /* 145 KP_0 */
+ 0x53, /* 146 KP_. */
+ 0x47, /* 147 KP_HOME */
+ 0x48, /* 148 KP_UP */
+ 0x49, /* 149 KP_PgUp */
+ 0x4b, /* 150 KP_Left */
+ 0x4c, /* 151 KP_ */
+ 0x4d, /* 152 KP_Right */
+ 0x4f, /* 153 KP_End */
+ 0x50, /* 154 KP_Down */
+ 0x51, /* 155 KP_PgDn */
+ 0x52, /* 156 KP_Ins */
+ 0x53, /* 157 KP_Del */
+};
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+
+ keycode = ev->keysym.scancode;
+
+ if (keycode < 9) {
+ keycode = 0;
+ } else if (keycode < 97) {
+ keycode -= 8; /* just an offset */
+ } else if (keycode < 158) {
+ /* use conversion table */
+ keycode = x_keycode_to_pc_keycode[keycode - 97];
+ } else {
+ keycode = 0;
+ }
+ return keycode;
+}
+
+#endif
+
+static void reset_keys(void)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (modifiers_state[i]) {
+ if (i & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(i | 0x80);
+ modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+ int keycode, v;
+
+ if (ev->keysym.sym == SDLK_PAUSE) {
+ /* specific case */
+ v = 0;
+ if (ev->type == SDL_KEYUP)
+ v |= 0x80;
+ kbd_put_keycode(0xe1);
+ kbd_put_keycode(0x1d | v);
+ kbd_put_keycode(0x45 | v);
+ return;
+ }
+
+ if (kbd_layout) {
+ keycode = sdl_keyevent_to_keycode_generic(ev);
+ } else {
+ keycode = sdl_keyevent_to_keycode(ev);
+ }
+
+ switch(keycode) {
+ case 0x00:
+ /* sent when leaving window: reset the modifiers state */
+ reset_keys();
+ return;
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (ev->type == SDL_KEYUP)
+ modifiers_state[keycode] = 0;
+ else
+ modifiers_state[keycode] = 1;
+ break;
+ case 0x45: /* num lock */
+ case 0x3a: /* caps lock */
+ /* SDL does not send the key up event, so we generate it */
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | 0x80);
+ return;
+ }
+
+ /* now send the key code */
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (ev->type == SDL_KEYUP)
+ kbd_put_keycode(keycode | 0x80);
+ else
+ kbd_put_keycode(keycode & 0x7f);
+}
+
+static void sdl_update_caption(void)
+{
+ char buf[1024];
+ strcpy(buf, "QEMU");
+ if (!vm_running) {
+ strcat(buf, " [Stopped]");
+ }
+ if (gui_grab) {
+ strcat(buf, " - Press Ctrl-Alt to exit grab");
+ }
+ SDL_WM_SetCaption(buf, "QEMU");
+}
+
+static void sdl_hide_cursor(void)
+{
+ if (kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+ } else {
+ SDL_ShowCursor(0);
+ }
+}
+
+static void sdl_show_cursor(void)
+{
+ if (!kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ }
+}
+
+static void sdl_grab_start(void)
+{
+ sdl_hide_cursor();
+ SDL_WM_GrabInput(SDL_GRAB_ON);
+ /* dummy read to avoid moving the mouse */
+ SDL_GetRelativeMouseState(NULL, NULL);
+ gui_grab = 1;
+ sdl_update_caption();
+}
+
+static void sdl_grab_end(void)
+{
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ sdl_show_cursor();
+ gui_grab = 0;
+ sdl_update_caption();
+}
+
+static void sdl_send_mouse_event(int dz)
+{
+ int dx, dy, state, buttons;
+ state = SDL_GetRelativeMouseState(&dx, &dy);
+ buttons = 0;
+ if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
+ buttons |= MOUSE_EVENT_MBUTTON;
+
+ if (kbd_mouse_is_absolute()) {
+ if (!absolute_enabled) {
+ sdl_hide_cursor();
+ if (gui_grab) {
+ sdl_grab_end();
+ }
+ absolute_enabled = 1;
+ }
+
+ SDL_GetMouseState(&dx, &dy);
+ dx = dx * 0x7FFF / width;
+ dy = dy * 0x7FFF / height;
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+}
+
+static void toggle_full_screen(DisplayState *ds)
+{
+ gui_fullscreen = !gui_fullscreen;
+ sdl_resize(ds, screen->w, screen->h);
+ if (gui_fullscreen) {
+ gui_saved_grab = gui_grab;
+ sdl_grab_start();
+ } else {
+ if (!gui_saved_grab)
+ sdl_grab_end();
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void sdl_refresh(DisplayState *ds)
+{
+ SDL_Event ev1, *ev = &ev1;
+ int mod_state;
+
+ if (last_vm_running != vm_running) {
+ last_vm_running = vm_running;
+ sdl_update_caption();
+ }
+
+ vga_hw_update();
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_VIDEOEXPOSE:
+ sdl_update(ds, 0, 0, screen->w, screen->h);
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ if (ev->type == SDL_KEYDOWN) {
+ mod_state = (SDL_GetModState() & gui_grab_code) ==
+ gui_grab_code;
+ gui_key_modifier_pressed = mod_state;
+ if (gui_key_modifier_pressed) {
+ int keycode;
+ keycode = sdl_keyevent_to_keycode(&ev->key);
+ switch(keycode) {
+ case 0x21: /* 'f' key on US keyboard */
+ toggle_full_screen(ds);
+ gui_keysym = 1;
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ console_select(keycode - 0x02);
+ if (!is_graphic_console()) {
+ /* display grab if going to a text console */
+ if (gui_grab)
+ sdl_grab_end();
+ }
+ gui_keysym = 1;
+ break;
+ default:
+ break;
+ }
+ } else if (!is_graphic_console()) {
+ int keysym;
+ keysym = 0;
+ if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
+ default: break;
+ }
+ } else {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
+ case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break; case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
+ default: break;
+ }
+ }
+ if (keysym) {
+ kbd_put_keysym(keysym);
+ } else if (ev->key.keysym.unicode != 0) {
+ kbd_put_keysym(ev->key.keysym.unicode);
+ }
+ }
+ } else if (ev->type == SDL_KEYUP) {
+ mod_state = (ev->key.keysym.mod & gui_grab_code);
+ if (!mod_state) {
+ if (gui_key_modifier_pressed) {
+ gui_key_modifier_pressed = 0;
+ if (gui_keysym == 0) {
+ /* exit/enter grab if pressing Ctrl-Alt */
+ if (!gui_grab) {
+ /* if the application is not active,
+ do not try to enter grab state. It
+ prevents
+ 'SDL_WM_GrabInput(SDL_GRAB_ON)'
+ from blocking all the application
+ (SDL bug). */
+ if (SDL_GetAppState() & SDL_APPACTIVE)
+ sdl_grab_start();
+ } else {
+ sdl_grab_end();
+ }
+ /* SDL does not send back all the
+ modifiers key, so we must correct it */
+ reset_keys();
+ break;
+ }
+ gui_keysym = 0;
+ }
+ }
+ }
+ if (is_graphic_console())
+ sdl_process_key(&ev->key);
+ break;
+ case SDL_QUIT:
+ qemu_system_shutdown_request();
+ break;
+ case SDL_MOUSEMOTION:
+ if (gui_grab || kbd_mouse_is_absolute()) {
+ sdl_send_mouse_event(0);
+ }
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ {
+ SDL_MouseButtonEvent *bev = &ev->button;
+ if (!gui_grab && !kbd_mouse_is_absolute()) {
+ if (ev->type == SDL_MOUSEBUTTONDOWN &&
+ (bev->state & SDL_BUTTON_LMASK)) {
+ /* start grabbing all events */
+ sdl_grab_start();
+ }
+ } else {
+ int dz;
+ dz = 0;
+#ifdef SDL_BUTTON_WHEELUP
+ if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = -1;
+ } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = 1;
+ }
+#endif
+ sdl_send_mouse_event(dz);
+ }
+ }
+ break;
+ case SDL_ACTIVEEVENT:
+ if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
+ !ev->active.gain && !gui_fullscreen_initial_grab) {
+ sdl_grab_end();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void sdl_cleanup(void)
+{
+ SDL_Quit();
+}
+
+void sdl_display_init(DisplayState *ds, int full_screen)
+{
+ int flags;
+ uint8_t data = 0;
+
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+
+ flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+ if (SDL_Init (flags)) {
+ fprintf(stderr, "Could not initialize SDL - exiting\n");
+ exit(1);
+ }
+#ifndef _WIN32
+ /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+#endif
+
+ ds->dpy_update = sdl_update;
+ ds->dpy_resize = sdl_resize;
+ ds->dpy_refresh = sdl_refresh;
+
+ sdl_resize(ds, 640, 400);
+ sdl_update_caption();
+ SDL_EnableKeyRepeat(250, 50);
+ SDL_EnableUNICODE(1);
+ gui_grab = 0;
+
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ sdl_cursor_normal = SDL_GetCursor();
+
+ atexit(sdl_cleanup);
+ if (full_screen) {
+ gui_fullscreen = 1;
+ gui_fullscreen_initial_grab = 1;
+ sdl_grab_start();
+ }
+}
diff --git a/sdl_keysym.h b/sdl_keysym.h
new file mode 100644
index 0000000..9a74142
--- /dev/null
+++ b/sdl_keysym.h
@@ -0,0 +1,278 @@
+typedef struct {
+ const char* name;
+ int keysym;
+} name2keysym_t;
+static name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", SDLK_EURO},
+
+ /* modifiers */
+{"Control_L", SDLK_LCTRL},
+{"Control_R", SDLK_RCTRL},
+{"Alt_L", SDLK_LALT},
+{"Alt_R", SDLK_RALT},
+{"Caps_Lock", SDLK_CAPSLOCK},
+{"Meta_L", SDLK_LMETA},
+{"Meta_R", SDLK_RMETA},
+{"Shift_L", SDLK_LSHIFT},
+{"Shift_R", SDLK_RSHIFT},
+{"Super_L", SDLK_LSUPER},
+{"Super_R", SDLK_RSUPER},
+
+ /* special keys */
+{"BackSpace", SDLK_BACKSPACE},
+{"Tab", SDLK_TAB},
+{"Return", SDLK_RETURN},
+{"Right", SDLK_RIGHT},
+{"Left", SDLK_LEFT},
+{"Up", SDLK_UP},
+{"Down", SDLK_DOWN},
+{"Page_Down", SDLK_PAGEDOWN},
+{"Page_Up", SDLK_PAGEUP},
+{"Insert", SDLK_INSERT},
+{"Delete", SDLK_DELETE},
+{"Home", SDLK_HOME},
+{"End", SDLK_END},
+{"Scroll_Lock", SDLK_SCROLLOCK},
+{"F1", SDLK_F1},
+{"F2", SDLK_F2},
+{"F3", SDLK_F3},
+{"F4", SDLK_F4},
+{"F5", SDLK_F5},
+{"F6", SDLK_F6},
+{"F7", SDLK_F7},
+{"F8", SDLK_F8},
+{"F9", SDLK_F9},
+{"F10", SDLK_F10},
+{"F11", SDLK_F11},
+{"F12", SDLK_F12},
+{"F13", SDLK_F13},
+{"F14", SDLK_F14},
+{"F15", SDLK_F15},
+{"Sys_Req", SDLK_SYSREQ},
+{"KP_0", SDLK_KP0},
+{"KP_1", SDLK_KP1},
+{"KP_2", SDLK_KP2},
+{"KP_3", SDLK_KP3},
+{"KP_4", SDLK_KP4},
+{"KP_5", SDLK_KP5},
+{"KP_6", SDLK_KP6},
+{"KP_7", SDLK_KP7},
+{"KP_8", SDLK_KP8},
+{"KP_9", SDLK_KP9},
+{"KP_Add", SDLK_KP_PLUS},
+{"KP_Decimal", SDLK_KP_PERIOD},
+{"KP_Divide", SDLK_KP_DIVIDE},
+{"KP_Enter", SDLK_KP_ENTER},
+{"KP_Equal", SDLK_KP_EQUALS},
+{"KP_Multiply", SDLK_KP_MULTIPLY},
+{"KP_Subtract", SDLK_KP_MINUS},
+{"help", SDLK_HELP},
+{"Menu", SDLK_MENU},
+{"Power", SDLK_POWER},
+{"Print", SDLK_PRINT},
+{"Mode_switch", SDLK_MODE},
+{"Multi_Key", SDLK_COMPOSE},
+{"Num_Lock", SDLK_NUMLOCK},
+{"Pause", SDLK_PAUSE},
+{"Escape", SDLK_ESCAPE},
+
+{0,0},
+};
diff --git a/sh4-dis.c b/sh4-dis.c
new file mode 100644
index 0000000..5f45e5e
--- /dev/null
+++ b/sh4-dis.c
@@ -0,0 +1,2096 @@
+/* Disassemble SH instructions.
+ Copyright 1993, 1994, 1995, 1997, 1998, 2000, 2001, 2002, 2003, 2004
+ Free Software Foundation, Inc.
+
+ 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. */
+
+#include <stdio.h>
+#include "dis-asm.h"
+
+#define DEFINE_TABLE
+
+typedef enum
+ {
+ HEX_0,
+ HEX_1,
+ HEX_2,
+ HEX_3,
+ HEX_4,
+ HEX_5,
+ HEX_6,
+ HEX_7,
+ HEX_8,
+ HEX_9,
+ HEX_A,
+ HEX_B,
+ HEX_C,
+ HEX_D,
+ HEX_E,
+ HEX_F,
+ HEX_XX00,
+ HEX_00YY,
+ REG_N,
+ REG_N_D, /* nnn0 */
+ REG_N_B01, /* nn01 */
+ REG_M,
+ SDT_REG_N,
+ REG_NM,
+ REG_B,
+ BRANCH_12,
+ BRANCH_8,
+ IMM0_4,
+ IMM0_4BY2,
+ IMM0_4BY4,
+ IMM1_4,
+ IMM1_4BY2,
+ IMM1_4BY4,
+ PCRELIMM_8BY2,
+ PCRELIMM_8BY4,
+ IMM0_8,
+ IMM0_8BY2,
+ IMM0_8BY4,
+ IMM1_8,
+ IMM1_8BY2,
+ IMM1_8BY4,
+ PPI,
+ NOPX,
+ NOPY,
+ MOVX,
+ MOVY,
+ MOVX_NOPY,
+ MOVY_NOPX,
+ PSH,
+ PMUL,
+ PPI3,
+ PPI3NC,
+ PDC,
+ PPIC,
+ REPEAT,
+ IMM0_3c, /* xxxx 0iii */
+ IMM0_3s, /* xxxx 1iii */
+ IMM0_3Uc, /* 0iii xxxx */
+ IMM0_3Us, /* 1iii xxxx */
+ IMM0_20_4,
+ IMM0_20, /* follows IMM0_20_4 */
+ IMM0_20BY8, /* follows IMM0_20_4 */
+ DISP0_12,
+ DISP0_12BY2,
+ DISP0_12BY4,
+ DISP0_12BY8,
+ DISP1_12,
+ DISP1_12BY2,
+ DISP1_12BY4,
+ DISP1_12BY8
+ }
+sh_nibble_type;
+
+typedef enum
+ {
+ A_END,
+ A_BDISP12,
+ A_BDISP8,
+ A_DEC_M,
+ A_DEC_N,
+ A_DISP_GBR,
+ A_PC,
+ A_DISP_PC,
+ A_DISP_PC_ABS,
+ A_DISP_REG_M,
+ A_DISP_REG_N,
+ A_GBR,
+ A_IMM,
+ A_INC_M,
+ A_INC_N,
+ A_IND_M,
+ A_IND_N,
+ A_IND_R0_REG_M,
+ A_IND_R0_REG_N,
+ A_MACH,
+ A_MACL,
+ A_PR,
+ A_R0,
+ A_R0_GBR,
+ A_REG_M,
+ A_REG_N,
+ A_REG_B,
+ A_SR,
+ A_VBR,
+ A_TBR,
+ A_DISP_TBR,
+ A_DISP2_TBR,
+ A_DEC_R15,
+ A_INC_R15,
+ A_MOD,
+ A_RE,
+ A_RS,
+ A_DSR,
+ DSP_REG_M,
+ DSP_REG_N,
+ DSP_REG_X,
+ DSP_REG_Y,
+ DSP_REG_E,
+ DSP_REG_F,
+ DSP_REG_G,
+ DSP_REG_A_M,
+ DSP_REG_AX,
+ DSP_REG_XY,
+ DSP_REG_AY,
+ DSP_REG_YX,
+ AX_INC_N,
+ AY_INC_N,
+ AXY_INC_N,
+ AYX_INC_N,
+ AX_IND_N,
+ AY_IND_N,
+ AXY_IND_N,
+ AYX_IND_N,
+ AX_PMOD_N,
+ AXY_PMOD_N,
+ AY_PMOD_N,
+ AYX_PMOD_N,
+ AS_DEC_N,
+ AS_INC_N,
+ AS_IND_N,
+ AS_PMOD_N,
+ A_A0,
+ A_X0,
+ A_X1,
+ A_Y0,
+ A_Y1,
+ A_SSR,
+ A_SPC,
+ A_SGR,
+ A_DBR,
+ F_REG_N,
+ F_REG_M,
+ D_REG_N,
+ D_REG_M,
+ X_REG_N, /* Only used for argument parsing. */
+ X_REG_M, /* Only used for argument parsing. */
+ DX_REG_N,
+ DX_REG_M,
+ V_REG_N,
+ V_REG_M,
+ XMTRX_M4,
+ F_FR0,
+ FPUL_N,
+ FPUL_M,
+ FPSCR_N,
+ FPSCR_M
+ }
+sh_arg_type;
+
+typedef enum
+ {
+ A_A1_NUM = 5,
+ A_A0_NUM = 7,
+ A_X0_NUM, A_X1_NUM, A_Y0_NUM, A_Y1_NUM,
+ A_M0_NUM, A_A1G_NUM, A_M1_NUM, A_A0G_NUM
+ }
+sh_dsp_reg_nums;
+
+#define arch_sh1_base 0x0001
+#define arch_sh2_base 0x0002
+#define arch_sh3_base 0x0004
+#define arch_sh4_base 0x0008
+#define arch_sh4a_base 0x0010
+#define arch_sh2a_base 0x0020
+
+/* This is an annotation on instruction types, but we abuse the arch
+ field in instructions to denote it. */
+#define arch_op32 0x00100000 /* This is a 32-bit opcode. */
+
+#define arch_sh_no_mmu 0x04000000
+#define arch_sh_has_mmu 0x08000000
+#define arch_sh_no_co 0x10000000 /* neither FPU nor DSP co-processor */
+#define arch_sh_sp_fpu 0x20000000 /* single precision FPU */
+#define arch_sh_dp_fpu 0x40000000 /* double precision FPU */
+#define arch_sh_has_dsp 0x80000000
+
+
+#define arch_sh_base_mask 0x0000003f
+#define arch_opann_mask 0x00100000
+#define arch_sh_mmu_mask 0x0c000000
+#define arch_sh_co_mask 0xf0000000
+
+
+#define arch_sh1 (arch_sh1_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2 (arch_sh2_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2a (arch_sh2a_base|arch_sh_no_mmu|arch_sh_dp_fpu)
+#define arch_sh2a_nofpu (arch_sh2a_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2e (arch_sh2_base|arch_sh2a_base|arch_sh_no_mmu|arch_sh_sp_fpu)
+#define arch_sh_dsp (arch_sh2_base|arch_sh_no_mmu|arch_sh_has_dsp)
+#define arch_sh3_nommu (arch_sh3_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh3 (arch_sh3_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh3e (arch_sh3_base|arch_sh_has_mmu|arch_sh_sp_fpu)
+#define arch_sh3_dsp (arch_sh3_base|arch_sh_has_mmu|arch_sh_has_dsp)
+#define arch_sh4 (arch_sh4_base|arch_sh_has_mmu|arch_sh_dp_fpu)
+#define arch_sh4a (arch_sh4a_base|arch_sh_has_mmu|arch_sh_dp_fpu)
+#define arch_sh4al_dsp (arch_sh4a_base|arch_sh_has_mmu|arch_sh_has_dsp)
+#define arch_sh4_nofpu (arch_sh4_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh4a_nofpu (arch_sh4a_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh4_nommu_nofpu (arch_sh4_base|arch_sh_no_mmu|arch_sh_no_co)
+
+#define SH_MERGE_ARCH_SET(SET1, SET2) ((SET1) & (SET2))
+#define SH_VALID_BASE_ARCH_SET(SET) (((SET) & arch_sh_base_mask) != 0)
+#define SH_VALID_MMU_ARCH_SET(SET) (((SET) & arch_sh_mmu_mask) != 0)
+#define SH_VALID_CO_ARCH_SET(SET) (((SET) & arch_sh_co_mask) != 0)
+#define SH_VALID_ARCH_SET(SET) \
+ (SH_VALID_BASE_ARCH_SET (SET) \
+ && SH_VALID_MMU_ARCH_SET (SET) \
+ && SH_VALID_CO_ARCH_SET (SET))
+#define SH_MERGE_ARCH_SET_VALID(SET1, SET2) \
+ SH_VALID_ARCH_SET (SH_MERGE_ARCH_SET (SET1, SET2))
+
+#define SH_ARCH_SET_HAS_FPU(SET) \
+ (((SET) & (arch_sh_sp_fpu | arch_sh_dp_fpu)) != 0)
+#define SH_ARCH_SET_HAS_DSP(SET) \
+ (((SET) & arch_sh_has_dsp) != 0)
+
+/* This is returned from the functions below when an error occurs
+ (in addition to a call to BFD_FAIL). The value should allow
+ the tools to continue to function in most cases - there may
+ be some confusion between DSP and FPU etc. */
+#define SH_ARCH_UNKNOWN_ARCH 0xffffffff
+
+/* These are defined in bfd/cpu-sh.c . */
+unsigned int sh_get_arch_from_bfd_mach (unsigned long mach);
+unsigned int sh_get_arch_up_from_bfd_mach (unsigned long mach);
+unsigned long sh_get_bfd_mach_from_arch_set (unsigned int arch_set);
+/* bfd_boolean sh_merge_bfd_arch (bfd *ibfd, bfd *obfd); */
+
+/* Below are the 'architecture sets'.
+ They describe the following inheritance graph:
+
+ SH1
+ |
+ SH2
+ .------------'|`--------------------.
+ / | \
+SH-DSP SH3-nommu SH2E
+ | |`--------. |
+ | | \ |
+ | SH3 SH4-nommu-nofpu |
+ | | | |
+ | .------------'|`----------+---------. |
+ |/ / \|
+ | | .-------' |
+ | |/ |
+SH3-dsp SH4-nofpu SH3E
+ | |`--------------------. |
+ | | \|
+ | SH4A-nofpu SH4
+ | .------------' `--------------------. |
+ |/ \|
+SH4AL-dsp SH4A
+
+*/
+
+/* Central branches */
+#define arch_sh1_up (arch_sh1 | arch_sh2_up)
+#define arch_sh2_up (arch_sh2 | arch_sh2e_up | arch_sh2a_nofpu_up | arch_sh3_nommu_up | arch_sh_dsp_up)
+#define arch_sh3_nommu_up (arch_sh3_nommu | arch_sh3_up | arch_sh4_nommu_nofpu_up)
+#define arch_sh3_up (arch_sh3 | arch_sh3e_up | arch_sh3_dsp_up | arch_sh4_nofp_up)
+#define arch_sh4_nommu_nofpu_up (arch_sh4_nommu_nofpu | arch_sh4_nofp_up)
+#define arch_sh4_nofp_up (arch_sh4_nofpu | arch_sh4_up | arch_sh4a_nofp_up)
+#define arch_sh4a_nofp_up (arch_sh4a_nofpu | arch_sh4a_up | arch_sh4al_dsp_up)
+
+/* Right branch */
+#define arch_sh2e_up (arch_sh2e | arch_sh2a_up | arch_sh3e_up)
+#define arch_sh3e_up (arch_sh3e | arch_sh4_up)
+#define arch_sh4_up (arch_sh4 | arch_sh4a_up)
+#define arch_sh4a_up (arch_sh4a)
+
+/* Left branch */
+#define arch_sh_dsp_up (arch_sh_dsp | arch_sh3_dsp_up)
+#define arch_sh3_dsp_up (arch_sh3_dsp | arch_sh4al_dsp_up)
+#define arch_sh4al_dsp_up (arch_sh4al_dsp)
+
+/* SH 2a branched off SH2e, adding a lot but not all of SH4 and SH4a. */
+#define arch_sh2a_up (arch_sh2a)
+#define arch_sh2a_nofpu_up (arch_sh2a_nofpu | arch_sh2a_up)
+
+
+typedef struct
+{
+ char *name;
+ sh_arg_type arg[4];
+ sh_nibble_type nibbles[9];
+ unsigned int arch;
+} sh_opcode_info;
+
+#ifdef DEFINE_TABLE
+
+const sh_opcode_info sh_table[] =
+ {
+/* 0111nnnni8*1.... add #<imm>,<REG_N> */{"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM0_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm1100 add <REG_M>,<REG_N> */{"add",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0011nnnnmmmm1110 addc <REG_M>,<REG_N>*/{"addc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0011nnnnmmmm1111 addv <REG_M>,<REG_N>*/{"addv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 11001001i8*1.... and #<imm>,R0 */{"and",{A_IMM,A_R0},{HEX_C,HEX_9,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1001 and <REG_M>,<REG_N> */{"and",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_9}, arch_sh1_up},
+
+/* 11001101i8*1.... and.b #<imm>,@(R0,GBR)*/{"and.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_D,IMM0_8}, arch_sh1_up},
+
+/* 1010i12......... bra <bdisp12> */{"bra",{A_BDISP12},{HEX_A,BRANCH_12}, arch_sh1_up},
+
+/* 1011i12......... bsr <bdisp12> */{"bsr",{A_BDISP12},{HEX_B,BRANCH_12}, arch_sh1_up},
+
+/* 10001001i8p1.... bt <bdisp8> */{"bt",{A_BDISP8},{HEX_8,HEX_9,BRANCH_8}, arch_sh1_up},
+
+/* 10001011i8p1.... bf <bdisp8> */{"bf",{A_BDISP8},{HEX_8,HEX_B,BRANCH_8}, arch_sh1_up},
+
+/* 10001101i8p1.... bt.s <bdisp8> */{"bt.s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up},
+
+/* 10001101i8p1.... bt/s <bdisp8> */{"bt/s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up},
+
+/* 10001111i8p1.... bf.s <bdisp8> */{"bf.s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up},
+
+/* 10001111i8p1.... bf/s <bdisp8> */{"bf/s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up},
+
+/* 0000000010001000 clrdmxy */{"clrdmxy",{0},{HEX_0,HEX_0,HEX_8,HEX_8}, arch_sh4al_dsp_up},
+
+/* 0000000000101000 clrmac */{"clrmac",{0},{HEX_0,HEX_0,HEX_2,HEX_8}, arch_sh1_up},
+
+/* 0000000001001000 clrs */{"clrs",{0},{HEX_0,HEX_0,HEX_4,HEX_8}, arch_sh1_up},
+
+/* 0000000000001000 clrt */{"clrt",{0},{HEX_0,HEX_0,HEX_0,HEX_8}, arch_sh1_up},
+
+/* 10001000i8*1.... cmp/eq #<imm>,R0 */{"cmp/eq",{A_IMM,A_R0},{HEX_8,HEX_8,IMM0_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm0000 cmp/eq <REG_M>,<REG_N>*/{"cmp/eq",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 0011nnnnmmmm0011 cmp/ge <REG_M>,<REG_N>*/{"cmp/ge",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_3}, arch_sh1_up},
+
+/* 0011nnnnmmmm0111 cmp/gt <REG_M>,<REG_N>*/{"cmp/gt",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0011nnnnmmmm0110 cmp/hi <REG_M>,<REG_N>*/{"cmp/hi",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0011nnnnmmmm0010 cmp/hs <REG_M>,<REG_N>*/{"cmp/hs",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00010101 cmp/pl <REG_N> */{"cmp/pl",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_5}, arch_sh1_up},
+
+/* 0100nnnn00010001 cmp/pz <REG_N> */{"cmp/pz",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_1}, arch_sh1_up},
+
+/* 0010nnnnmmmm1100 cmp/str <REG_M>,<REG_N>*/{"cmp/str",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0010nnnnmmmm0111 div0s <REG_M>,<REG_N>*/{"div0s",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0000000000011001 div0u */{"div0u",{0},{HEX_0,HEX_0,HEX_1,HEX_9}, arch_sh1_up},
+
+/* 0011nnnnmmmm0100 div1 <REG_M>,<REG_N>*/{"div1",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0110nnnnmmmm1110 exts.b <REG_M>,<REG_N>*/{"exts.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm1111 exts.w <REG_M>,<REG_N>*/{"exts.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 0110nnnnmmmm1100 extu.b <REG_M>,<REG_N>*/{"extu.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0110nnnnmmmm1101 extu.w <REG_M>,<REG_N>*/{"extu.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0000nnnn11100011 icbi @<REG_N> */{"icbi",{A_IND_N},{HEX_0,REG_N,HEX_E,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00101011 jmp @<REG_N> */{"jmp",{A_IND_N},{HEX_4,REG_N,HEX_2,HEX_B}, arch_sh1_up},
+
+/* 0100nnnn00001011 jsr @<REG_N> */{"jsr",{A_IND_N},{HEX_4,REG_N,HEX_0,HEX_B}, arch_sh1_up},
+
+/* 0100nnnn00001110 ldc <REG_N>,SR */{"ldc",{A_REG_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn00011110 ldc <REG_N>,GBR */{"ldc",{A_REG_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn00111010 ldc <REG_N>,SGR */{"ldc",{A_REG_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0100mmmm01001010 ldc <REG_M>,TBR */{"ldc",{A_REG_M,A_TBR},{HEX_4,REG_M,HEX_4,HEX_A}, arch_sh2a_nofpu_up},
+
+/* 0100nnnn00101110 ldc <REG_N>,VBR */{"ldc",{A_REG_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn01011110 ldc <REG_N>,MOD */{"ldc",{A_REG_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn01111110 ldc <REG_N>,RE */{"ldc",{A_REG_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn01101110 ldc <REG_N>,RS */{"ldc",{A_REG_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn00111110 ldc <REG_N>,SSR */{"ldc",{A_REG_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn01001110 ldc <REG_N>,SPC */{"ldc",{A_REG_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn11111010 ldc <REG_N>,DBR */{"ldc",{A_REG_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx1110 ldc <REG_N>,Rn_BANK */{"ldc",{A_REG_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn00000111 ldc.l @<REG_N>+,SR */{"ldc.l",{A_INC_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00010111 ldc.l @<REG_N>+,GBR */{"ldc.l",{A_INC_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00100111 ldc.l @<REG_N>+,VBR */{"ldc.l",{A_INC_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00110110 ldc.l @<REG_N>+,SGR */{"ldc.l",{A_INC_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_6}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn01010111 ldc.l @<REG_N>+,MOD */{"ldc.l",{A_INC_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn01110111 ldc.l @<REG_N>+,RE */{"ldc.l",{A_INC_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn01100111 ldc.l @<REG_N>+,RS */{"ldc.l",{A_INC_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn00110111 ldc.l @<REG_N>+,SSR */{"ldc.l",{A_INC_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100nnnn01000111 ldc.l @<REG_N>+,SPC */{"ldc.l",{A_INC_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100nnnn11110110 ldc.l @<REG_N>+,DBR */{"ldc.l",{A_INC_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_6}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx0111 ldc.l <REG_N>,Rn_BANK */{"ldc.l",{A_INC_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100mmmm00110100 ldrc <REG_M> */{"ldrc",{A_REG_M},{HEX_4,REG_M,HEX_3,HEX_4}, arch_sh4al_dsp_up},
+/* 10001010i8*1.... ldrc #<imm> */{"ldrc",{A_IMM},{HEX_8,HEX_A,IMM0_8}, arch_sh4al_dsp_up},
+
+/* 10001110i8p2.... ldre @(<disp>,PC) */{"ldre",{A_DISP_PC},{HEX_8,HEX_E,PCRELIMM_8BY2}, arch_sh_dsp_up},
+
+/* 10001100i8p2.... ldrs @(<disp>,PC) */{"ldrs",{A_DISP_PC},{HEX_8,HEX_C,PCRELIMM_8BY2}, arch_sh_dsp_up},
+
+/* 0100nnnn00001010 lds <REG_N>,MACH */{"lds",{A_REG_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn00011010 lds <REG_N>,MACL */{"lds",{A_REG_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn00101010 lds <REG_N>,PR */{"lds",{A_REG_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn01101010 lds <REG_N>,DSR */{"lds",{A_REG_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn01111010 lds <REG_N>,A0 */{"lds",{A_REG_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10001010 lds <REG_N>,X0 */{"lds",{A_REG_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10011010 lds <REG_N>,X1 */{"lds",{A_REG_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10101010 lds <REG_N>,Y0 */{"lds",{A_REG_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10111010 lds <REG_N>,Y1 */{"lds",{A_REG_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn01011010 lds <REG_N>,FPUL */{"lds",{A_REG_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn01101010 lds <REG_M>,FPSCR */{"lds",{A_REG_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn00000110 lds.l @<REG_N>+,MACH*/{"lds.l",{A_INC_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn00010110 lds.l @<REG_N>+,MACL*/{"lds.l",{A_INC_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn00100110 lds.l @<REG_N>+,PR */{"lds.l",{A_INC_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn01100110 lds.l @<REG_N>+,DSR */{"lds.l",{A_INC_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn01110110 lds.l @<REG_N>+,A0 */{"lds.l",{A_INC_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10000110 lds.l @<REG_N>+,X0 */{"lds.l",{A_INC_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10010110 lds.l @<REG_N>+,X1 */{"lds.l",{A_INC_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10100110 lds.l @<REG_N>+,Y0 */{"lds.l",{A_INC_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10110110 lds.l @<REG_N>+,Y1 */{"lds.l",{A_INC_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn01010110 lds.l @<REG_M>+,FPUL*/{"lds.l",{A_INC_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_6}, arch_sh2e_up},
+
+/* 0100nnnn01100110 lds.l @<REG_M>+,FPSCR*/{"lds.l",{A_INC_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_6}, arch_sh2e_up},
+
+/* 0000000000111000 ldtlb */{"ldtlb",{0},{HEX_0,HEX_0,HEX_3,HEX_8}, arch_sh3_up},
+
+/* 0100nnnnmmmm1111 mac.w @<REG_M>+,@<REG_N>+*/{"mac.w",{A_INC_M,A_INC_N},{HEX_4,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 1110nnnni8*1.... mov #<imm>,<REG_N> */{"mov",{A_IMM,A_REG_N},{HEX_E,REG_N,IMM0_8}, arch_sh1_up},
+
+/* 0110nnnnmmmm0011 mov <REG_M>,<REG_N> */{"mov",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_3}, arch_sh1_up},
+
+/* 0000nnnnmmmm0100 mov.b <REG_M>,@(R0,<REG_N>)*/{"mov.b",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0010nnnnmmmm0100 mov.b <REG_M>,@-<REG_N>*/{"mov.b",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0010nnnnmmmm0000 mov.b <REG_M>,@<REG_N>*/{"mov.b",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 10000100mmmmi4*1 mov.b @(<disp>,<REG_M>),R0*/{"mov.b",{A_DISP_REG_M,A_R0},{HEX_8,HEX_4,REG_M,IMM0_4}, arch_sh1_up},
+
+/* 11000100i8*1.... mov.b @(<disp>,GBR),R0*/{"mov.b",{A_DISP_GBR,A_R0},{HEX_C,HEX_4,IMM0_8}, arch_sh1_up},
+
+/* 0000nnnnmmmm1100 mov.b @(R0,<REG_M>),<REG_N>*/{"mov.b",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0110nnnnmmmm0100 mov.b @<REG_M>+,<REG_N>*/{"mov.b",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0110nnnnmmmm0000 mov.b @<REG_M>,<REG_N>*/{"mov.b",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 10000000mmmmi4*1 mov.b R0,@(<disp>,<REG_M>)*/{"mov.b",{A_R0,A_DISP_REG_M},{HEX_8,HEX_0,REG_M,IMM1_4}, arch_sh1_up},
+
+/* 11000000i8*1.... mov.b R0,@(<disp>,GBR)*/{"mov.b",{A_R0,A_DISP_GBR},{HEX_C,HEX_0,IMM1_8}, arch_sh1_up},
+
+/* 0100nnnn10001011 mov.b R0,@<REG_N>+ */{"mov.b",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_8,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11001011 mov.b @-<REG_M>,R0 */{"mov.b",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_C,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0000dddddddddddd mov.b <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.b",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0100dddddddddddd mov.b @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_4,DISP0_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0001nnnnmmmmi4*4 mov.l <REG_M>,@(<disp>,<REG_N>)*/{"mov.l",{ A_REG_M,A_DISP_REG_N},{HEX_1,REG_N,REG_M,IMM1_4BY4}, arch_sh1_up},
+
+/* 0000nnnnmmmm0110 mov.l <REG_M>,@(R0,<REG_N>)*/{"mov.l",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0010nnnnmmmm0110 mov.l <REG_M>,@-<REG_N>*/{"mov.l",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0010nnnnmmmm0010 mov.l <REG_M>,@<REG_N>*/{"mov.l",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 0101nnnnmmmmi4*4 mov.l @(<disp>,<REG_M>),<REG_N>*/{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_5,REG_N,REG_M,IMM0_4BY4}, arch_sh1_up},
+
+/* 11000110i8*4.... mov.l @(<disp>,GBR),R0*/{"mov.l",{A_DISP_GBR,A_R0},{HEX_C,HEX_6,IMM0_8BY4}, arch_sh1_up},
+
+/* 1101nnnni8p4.... mov.l @(<disp>,PC),<REG_N>*/{"mov.l",{A_DISP_PC,A_REG_N},{HEX_D,REG_N,PCRELIMM_8BY4}, arch_sh1_up},
+
+/* 0000nnnnmmmm1110 mov.l @(R0,<REG_M>),<REG_N>*/{"mov.l",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm0110 mov.l @<REG_M>+,<REG_N>*/{"mov.l",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0110nnnnmmmm0010 mov.l @<REG_M>,<REG_N>*/{"mov.l",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 11000010i8*4.... mov.l R0,@(<disp>,GBR)*/{"mov.l",{A_R0,A_DISP_GBR},{HEX_C,HEX_2,IMM1_8BY4}, arch_sh1_up},
+
+/* 0100nnnn10101011 mov.l R0,@<REG_N>+ */{"mov.l",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_A,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11001011 mov.l @-<REG_M>,R0 */{"mov.l",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0010dddddddddddd mov.l <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.l",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_2,DISP1_12BY4}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0110dddddddddddd mov.l @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_6,DISP0_12BY4}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnnmmmm0101 mov.w <REG_M>,@(R0,<REG_N>)*/{"mov.w",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0010nnnnmmmm0101 mov.w <REG_M>,@-<REG_N>*/{"mov.w",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0010nnnnmmmm0001 mov.w <REG_M>,@<REG_N>*/{"mov.w",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_1}, arch_sh1_up},
+
+/* 10000101mmmmi4*2 mov.w @(<disp>,<REG_M>),R0*/{"mov.w",{A_DISP_REG_M,A_R0},{HEX_8,HEX_5,REG_M,IMM0_4BY2}, arch_sh1_up},
+
+/* 11000101i8*2.... mov.w @(<disp>,GBR),R0*/{"mov.w",{A_DISP_GBR,A_R0},{HEX_C,HEX_5,IMM0_8BY2}, arch_sh1_up},
+
+/* 1001nnnni8p2.... mov.w @(<disp>,PC),<REG_N>*/{"mov.w",{A_DISP_PC,A_REG_N},{HEX_9,REG_N,PCRELIMM_8BY2}, arch_sh1_up},
+
+/* 0000nnnnmmmm1101 mov.w @(R0,<REG_M>),<REG_N>*/{"mov.w",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0110nnnnmmmm0101 mov.w @<REG_M>+,<REG_N>*/{"mov.w",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0110nnnnmmmm0001 mov.w @<REG_M>,<REG_N>*/{"mov.w",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_1}, arch_sh1_up},
+
+/* 10000001mmmmi4*2 mov.w R0,@(<disp>,<REG_M>)*/{"mov.w",{A_R0,A_DISP_REG_M},{HEX_8,HEX_1,REG_M,IMM1_4BY2}, arch_sh1_up},
+
+/* 11000001i8*2.... mov.w R0,@(<disp>,GBR)*/{"mov.w",{A_R0,A_DISP_GBR},{HEX_C,HEX_1,IMM1_8BY2}, arch_sh1_up},
+
+/* 0100nnnn10011011 mov.w R0,@<REG_N>+ */{"mov.w",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_9,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11011011 mov.w @-<REG_M>,R0 */{"mov.w",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_D,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0001dddddddddddd mov.w <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.w",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_1,DISP1_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0101dddddddddddd mov.w @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_5,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+/* 11000111i8p4.... mova @(<disp>,PC),R0*/{"mova",{A_DISP_PC,A_R0},{HEX_C,HEX_7,PCRELIMM_8BY4}, arch_sh1_up},
+/* 0000nnnn11000011 movca.l R0,@<REG_N> */{"movca.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_C,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn01110011 movco.l r0,@<REG_N> */{"movco.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_7,HEX_3}, arch_sh4a_nofp_up},
+/* 0000mmmm01100011 movli.l @<REG_M>,r0 */{"movli.l",{A_IND_M,A_R0},{HEX_0,REG_M,HEX_6,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0000nnnn00101001 movt <REG_N> */{"movt",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_9}, arch_sh1_up},
+
+/* 0100mmmm10101001 movua.l @<REG_M>,r0 */{"movua.l",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_A,HEX_9}, arch_sh4a_nofp_up},
+/* 0100mmmm11101001 movua.l @<REG_M>+,r0 */{"movua.l",{A_INC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_9}, arch_sh4a_nofp_up},
+
+/* 0010nnnnmmmm1111 muls.w <REG_M>,<REG_N>*/{"muls.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up},
+/* 0010nnnnmmmm1111 muls <REG_M>,<REG_N>*/{"muls",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 0000nnnnmmmm0111 mul.l <REG_M>,<REG_N>*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh2_up},
+
+/* 0010nnnnmmmm1110 mulu.w <REG_M>,<REG_N>*/{"mulu.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up},
+/* 0010nnnnmmmm1110 mulu <REG_M>,<REG_N>*/{"mulu",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm1011 neg <REG_M>,<REG_N> */{"neg",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 0110nnnnmmmm1010 negc <REG_M>,<REG_N>*/{"negc",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 0000000000001001 nop */{"nop",{0},{HEX_0,HEX_0,HEX_0,HEX_9}, arch_sh1_up},
+
+/* 0110nnnnmmmm0111 not <REG_M>,<REG_N> */{"not",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_7}, arch_sh1_up},
+/* 0000nnnn10010011 ocbi @<REG_N> */{"ocbi",{A_IND_N},{HEX_0,REG_N,HEX_9,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn10100011 ocbp @<REG_N> */{"ocbp",{A_IND_N},{HEX_0,REG_N,HEX_A,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn10110011 ocbwb @<REG_N> */{"ocbwb",{A_IND_N},{HEX_0,REG_N,HEX_B,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+
+/* 11001011i8*1.... or #<imm>,R0 */{"or",{A_IMM,A_R0},{HEX_C,HEX_B,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1011 or <REG_M>,<REG_N> */{"or",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 11001111i8*1.... or.b #<imm>,@(R0,GBR)*/{"or.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_F,IMM0_8}, arch_sh1_up},
+
+/* 0000nnnn10000011 pref @<REG_N> */{"pref",{A_IND_N},{HEX_0,REG_N,HEX_8,HEX_3}, arch_sh4_nommu_nofpu_up | arch_sh2a_nofpu_up},
+
+/* 0000nnnn11010011 prefi @<REG_N> */{"prefi",{A_IND_N},{HEX_0,REG_N,HEX_D,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00100100 rotcl <REG_N> */{"rotcl",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_4}, arch_sh1_up},
+
+/* 0100nnnn00100101 rotcr <REG_N> */{"rotcr",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_5}, arch_sh1_up},
+
+/* 0100nnnn00000100 rotl <REG_N> */{"rotl",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_4}, arch_sh1_up},
+
+/* 0100nnnn00000101 rotr <REG_N> */{"rotr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_5}, arch_sh1_up},
+
+/* 0000000000101011 rte */{"rte",{0},{HEX_0,HEX_0,HEX_2,HEX_B}, arch_sh1_up},
+
+/* 0000000000001011 rts */{"rts",{0},{HEX_0,HEX_0,HEX_0,HEX_B}, arch_sh1_up},
+
+/* 0000000010011000 setdmx */{"setdmx",{0},{HEX_0,HEX_0,HEX_9,HEX_8}, arch_sh4al_dsp_up},
+/* 0000000011001000 setdmy */{"setdmy",{0},{HEX_0,HEX_0,HEX_C,HEX_8}, arch_sh4al_dsp_up},
+
+/* 0000000001011000 sets */{"sets",{0},{HEX_0,HEX_0,HEX_5,HEX_8}, arch_sh1_up},
+/* 0000000000011000 sett */{"sett",{0},{HEX_0,HEX_0,HEX_1,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00010100 setrc <REG_N> */{"setrc",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up},
+
+/* 10000010i8*1.... setrc #<imm> */{"setrc",{A_IMM},{HEX_8,HEX_2,IMM0_8}, arch_sh_dsp_up},
+
+/* repeat start end <REG_N> */{"repeat",{A_DISP_PC,A_DISP_PC,A_REG_N},{REPEAT,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up},
+
+/* repeat start end #<imm> */{"repeat",{A_DISP_PC,A_DISP_PC,A_IMM},{REPEAT,HEX_2,IMM0_8,HEX_8}, arch_sh_dsp_up},
+
+/* 0100nnnnmmmm1100 shad <REG_M>,<REG_N>*/{"shad",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_C}, arch_sh3_nommu_up | arch_sh2a_nofpu_up},
+
+/* 0100nnnnmmmm1101 shld <REG_M>,<REG_N>*/{"shld",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_D}, arch_sh3_nommu_up | arch_sh2a_nofpu_up},
+
+/* 0100nnnn00100000 shal <REG_N> */{"shal",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_0}, arch_sh1_up},
+
+/* 0100nnnn00100001 shar <REG_N> */{"shar",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_1}, arch_sh1_up},
+
+/* 0100nnnn00000000 shll <REG_N> */{"shll",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_0}, arch_sh1_up},
+
+/* 0100nnnn00101000 shll16 <REG_N> */{"shll16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00001000 shll2 <REG_N> */{"shll2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00011000 shll8 <REG_N> */{"shll8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00000001 shlr <REG_N> */{"shlr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_1}, arch_sh1_up},
+
+/* 0100nnnn00101001 shlr16 <REG_N> */{"shlr16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_9}, arch_sh1_up},
+
+/* 0100nnnn00001001 shlr2 <REG_N> */{"shlr2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_9}, arch_sh1_up},
+
+/* 0100nnnn00011001 shlr8 <REG_N> */{"shlr8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_9}, arch_sh1_up},
+
+/* 0000000000011011 sleep */{"sleep",{0},{HEX_0,HEX_0,HEX_1,HEX_B}, arch_sh1_up},
+
+/* 0000nnnn00000010 stc SR,<REG_N> */{"stc",{A_SR,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn00010010 stc GBR,<REG_N> */{"stc",{A_GBR,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn00100010 stc VBR,<REG_N> */{"stc",{A_VBR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn01010010 stc MOD,<REG_N> */{"stc",{A_MOD,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn01110010 stc RE,<REG_N> */{"stc",{A_RE,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn01100010 stc RS,<REG_N> */{"stc",{A_RS,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn00110010 stc SSR,<REG_N> */{"stc",{A_SSR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn01000010 stc SPC,<REG_N> */{"stc",{A_SPC,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn00111010 stc SGR,<REG_N> */{"stc",{A_SGR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn11111010 stc DBR,<REG_N> */{"stc",{A_DBR,A_REG_N},{HEX_0,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn1xxx0010 stc Rn_BANK,<REG_N> */{"stc",{A_REG_B,A_REG_N},{HEX_0,REG_N,REG_B,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn01001010 stc TBR,<REG_N> */ {"stc",{A_TBR,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_A}, arch_sh2a_nofpu_up},
+
+/* 0100nnnn00000011 stc.l SR,@-<REG_N> */{"stc.l",{A_SR,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn00100011 stc.l VBR,@-<REG_N> */{"stc.l",{A_VBR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn01010011 stc.l MOD,@-<REG_N> */{"stc.l",{A_MOD,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn01110011 stc.l RE,@-<REG_N> */{"stc.l",{A_RE,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn01100011 stc.l RS,@-<REG_N> */{"stc.l",{A_RS,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn00110011 stc.l SSR,@-<REG_N> */{"stc.l",{A_SSR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_3}, arch_sh3_nommu_up},
+
+/* 0100nnnn01000011 stc.l SPC,@-<REG_N> */{"stc.l",{A_SPC,A_DEC_N},{HEX_4,REG_N,HEX_4,HEX_3}, arch_sh3_nommu_up},
+
+/* 0100nnnn00010011 stc.l GBR,@-<REG_N> */{"stc.l",{A_GBR,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn00110010 stc.l SGR,@-<REG_N> */{"stc.l",{A_SGR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_2}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn11110010 stc.l DBR,@-<REG_N> */{"stc.l",{A_DBR,A_DEC_N},{HEX_4,REG_N,HEX_F,HEX_2}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx0011 stc.l Rn_BANK,@-<REG_N> */{"stc.l",{A_REG_B,A_DEC_N},{HEX_4,REG_N,REG_B,HEX_3}, arch_sh3_nommu_up},
+
+/* 0000nnnn00001010 sts MACH,<REG_N> */{"sts",{A_MACH,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn00011010 sts MACL,<REG_N> */{"sts",{A_MACL,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn00101010 sts PR,<REG_N> */{"sts",{A_PR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn01101010 sts DSR,<REG_N> */{"sts",{A_DSR,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn01111010 sts A0,<REG_N> */{"sts",{A_A0,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10001010 sts X0,<REG_N> */{"sts",{A_X0,A_REG_N},{HEX_0,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10011010 sts X1,<REG_N> */{"sts",{A_X1,A_REG_N},{HEX_0,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10101010 sts Y0,<REG_N> */{"sts",{A_Y0,A_REG_N},{HEX_0,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10111010 sts Y1,<REG_N> */{"sts",{A_Y1,A_REG_N},{HEX_0,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn01011010 sts FPUL,<REG_N> */{"sts",{FPUL_M,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_A}, arch_sh2e_up},
+
+/* 0000nnnn01101010 sts FPSCR,<REG_N> */{"sts",{FPSCR_M,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn00000010 sts.l MACH,@-<REG_N>*/{"sts.l",{A_MACH,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00010010 sts.l MACL,@-<REG_N>*/{"sts.l",{A_MACL,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00100010 sts.l PR,@-<REG_N> */{"sts.l",{A_PR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn01100110 sts.l DSR,@-<REG_N> */{"sts.l",{A_DSR,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn01110110 sts.l A0,@-<REG_N> */{"sts.l",{A_A0,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10000110 sts.l X0,@-<REG_N> */{"sts.l",{A_X0,A_DEC_N},{HEX_4,REG_N,HEX_8,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10010110 sts.l X1,@-<REG_N> */{"sts.l",{A_X1,A_DEC_N},{HEX_4,REG_N,HEX_9,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10100110 sts.l Y0,@-<REG_N> */{"sts.l",{A_Y0,A_DEC_N},{HEX_4,REG_N,HEX_A,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10110110 sts.l Y1,@-<REG_N> */{"sts.l",{A_Y1,A_DEC_N},{HEX_4,REG_N,HEX_B,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn01010010 sts.l FPUL,@-<REG_N>*/{"sts.l",{FPUL_M,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_2}, arch_sh2e_up},
+
+/* 0100nnnn01100010 sts.l FPSCR,@-<REG_N>*/{"sts.l",{FPSCR_M,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh2e_up},
+
+/* 0011nnnnmmmm1000 sub <REG_M>,<REG_N> */{"sub",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm1010 subc <REG_M>,<REG_N>*/{"subc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 0011nnnnmmmm1011 subv <REG_M>,<REG_N>*/{"subv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 0110nnnnmmmm1000 swap.b <REG_M>,<REG_N>*/{"swap.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 0110nnnnmmmm1001 swap.w <REG_M>,<REG_N>*/{"swap.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_9}, arch_sh1_up},
+
+/* 0000000010101011 synco */{"synco",{0},{HEX_0,HEX_0,HEX_A,HEX_B}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00011011 tas.b @<REG_N> */{"tas.b",{A_IND_N},{HEX_4,REG_N,HEX_1,HEX_B}, arch_sh1_up},
+
+/* 11000011i8*1.... trapa #<imm> */{"trapa",{A_IMM},{HEX_C,HEX_3,IMM0_8}, arch_sh1_up},
+
+/* 11001000i8*1.... tst #<imm>,R0 */{"tst",{A_IMM,A_R0},{HEX_C,HEX_8,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1000 tst <REG_M>,<REG_N> */{"tst",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 11001100i8*1.... tst.b #<imm>,@(R0,GBR)*/{"tst.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_C,IMM0_8}, arch_sh1_up},
+
+/* 11001010i8*1.... xor #<imm>,R0 */{"xor",{A_IMM,A_R0},{HEX_C,HEX_A,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1010 xor <REG_M>,<REG_N> */{"xor",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 11001110i8*1.... xor.b #<imm>,@(R0,GBR)*/{"xor.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_E,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1101 xtrct <REG_M>,<REG_N>*/{"xtrct",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0000nnnnmmmm0111 mul.l <REG_M>,<REG_N>*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00010000 dt <REG_N> */{"dt",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_0}, arch_sh2_up},
+
+/* 0011nnnnmmmm1101 dmuls.l <REG_M>,<REG_N>*/{"dmuls.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_D}, arch_sh2_up},
+
+/* 0011nnnnmmmm0101 dmulu.l <REG_M>,<REG_N>*/{"dmulu.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_5}, arch_sh2_up},
+
+/* 0000nnnnmmmm1111 mac.l @<REG_M>+,@<REG_N>+*/{"mac.l",{A_INC_M,A_INC_N},{HEX_0,REG_N,REG_M,HEX_F}, arch_sh2_up},
+
+/* 0000nnnn00100011 braf <REG_N> */{"braf",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_3}, arch_sh2_up},
+
+/* 0000nnnn00000011 bsrf <REG_N> */{"bsrf",{A_REG_N},{HEX_0,REG_N,HEX_0,HEX_3}, arch_sh2_up},
+
+/* 111101nnmmmm0000 movs.w @-<REG_N>,<DSP_REG_M> */ {"movs.w",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_0}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0001 movs.w @<REG_N>,<DSP_REG_M> */ {"movs.w",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_4}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0010 movs.w @<REG_N>+,<DSP_REG_M> */ {"movs.w",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_8}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0011 movs.w @<REG_N>+r8,<DSP_REG_M> */ {"movs.w",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_C}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0100 movs.w <DSP_REG_M>,@-<REG_N> */ {"movs.w",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_1}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0101 movs.w <DSP_REG_M>,@<REG_N> */ {"movs.w",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_5}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0110 movs.w <DSP_REG_M>,@<REG_N>+ */ {"movs.w",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_9}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0111 movs.w <DSP_REG_M>,@<REG_N>+r8 */ {"movs.w",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_D}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1000 movs.l @-<REG_N>,<DSP_REG_M> */ {"movs.l",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_2}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1001 movs.l @<REG_N>,<DSP_REG_M> */ {"movs.l",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_6}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1010 movs.l @<REG_N>+,<DSP_REG_M> */ {"movs.l",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_A}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1011 movs.l @<REG_N>+r8,<DSP_REG_M> */ {"movs.l",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_E}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1100 movs.l <DSP_REG_M>,@-<REG_N> */ {"movs.l",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_3}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1101 movs.l <DSP_REG_M>,@<REG_N> */ {"movs.l",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_7}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1110 movs.l <DSP_REG_M>,@<REG_N>+ */ {"movs.l",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_B}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1111 movs.l <DSP_REG_M>,@<REG_N>+r8 */ {"movs.l",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_F}, arch_sh_dsp_up},
+
+/* 0*0*0*00** nopx */ {"nopx",{0},{PPI,NOPX}, arch_sh_dsp_up},
+/* *0*0*0**00 nopy */ {"nopy",{0},{PPI,NOPY}, arch_sh_dsp_up},
+/* n*m*0*01** movx.w @<REG_N>,<DSP_REG_X> */ {"movx.w",{AX_IND_N,DSP_REG_X},{PPI,MOVX,HEX_1}, arch_sh_dsp_up},
+/* n*m*0*10** movx.w @<REG_N>+,<DSP_REG_X> */ {"movx.w",{AX_INC_N,DSP_REG_X},{PPI,MOVX,HEX_2}, arch_sh_dsp_up},
+/* n*m*0*11** movx.w @<REG_N>+r8,<DSP_REG_X> */ {"movx.w",{AX_PMOD_N,DSP_REG_X},{PPI,MOVX,HEX_3}, arch_sh_dsp_up},
+/* n*m*1*01** movx.w <DSP_REG_M>,@<REG_N> */ {"movx.w",{DSP_REG_A_M,AX_IND_N},{PPI,MOVX,HEX_9}, arch_sh_dsp_up},
+/* n*m*1*10** movx.w <DSP_REG_M>,@<REG_N>+ */ {"movx.w",{DSP_REG_A_M,AX_INC_N},{PPI,MOVX,HEX_A}, arch_sh_dsp_up},
+/* n*m*1*11** movx.w <DSP_REG_M>,@<REG_N>+r8 */ {"movx.w",{DSP_REG_A_M,AX_PMOD_N},{PPI,MOVX,HEX_B}, arch_sh_dsp_up},
+
+/* nnmm000100 movx.w @<REG_Axy>,<DSP_REG_XY> */ {"movx.w",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm001000 movx.w @<REG_Axy>+,<DSP_REG_XY> */{"movx.w",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm001100 movx.w @<REG_Axy>+r8,<DSP_REG_XY> */{"movx.w",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_C}, arch_sh4al_dsp_up},
+/* nnmm100100 movx.w <DSP_REG_AX>,@<REG_Axy> */ {"movx.w",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_2,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm101000 movx.w <DSP_REG_AX>,@<REG_Axy>+ */{"movx.w",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_2,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm101100 movx.w <DSP_REG_AX>,@<REG_Axy>+r8 */{"movx.w",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_2,HEX_C}, arch_sh4al_dsp_up},
+
+/* nnmm010100 movx.l @<REG_Axy>,<DSP_REG_XY> */ {"movx.l",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm011000 movx.l @<REG_Axy>+,<DSP_REG_XY> */{"movx.l",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm011100 movx.l @<REG_Axy>+r8,<DSP_REG_XY> */{"movx.l",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_C}, arch_sh4al_dsp_up},
+/* nnmm110100 movx.l <DSP_REG_AX>,@<REG_Axy> */ {"movx.l",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_3,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm111000 movx.l <DSP_REG_AX>,@<REG_Axy>+ */{"movx.l",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_3,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm111100 movx.l <DSP_REG_AX>,@<REG_Axy>+r8 */{"movx.l",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_3,HEX_C}, arch_sh4al_dsp_up},
+
+/* *n*m*0**01 movy.w @<REG_N>,<DSP_REG_Y> */ {"movy.w",{AY_IND_N,DSP_REG_Y},{PPI,MOVY,HEX_1}, arch_sh_dsp_up},
+/* *n*m*0**10 movy.w @<REG_N>+,<DSP_REG_Y> */ {"movy.w",{AY_INC_N,DSP_REG_Y},{PPI,MOVY,HEX_2}, arch_sh_dsp_up},
+/* *n*m*0**11 movy.w @<REG_N>+r9,<DSP_REG_Y> */ {"movy.w",{AY_PMOD_N,DSP_REG_Y},{PPI,MOVY,HEX_3}, arch_sh_dsp_up},
+/* *n*m*1**01 movy.w <DSP_REG_M>,@<REG_N> */ {"movy.w",{DSP_REG_A_M,AY_IND_N},{PPI,MOVY,HEX_9}, arch_sh_dsp_up},
+/* *n*m*1**10 movy.w <DSP_REG_M>,@<REG_N>+ */ {"movy.w",{DSP_REG_A_M,AY_INC_N},{PPI,MOVY,HEX_A}, arch_sh_dsp_up},
+/* *n*m*1**11 movy.w <DSP_REG_M>,@<REG_N>+r9 */ {"movy.w",{DSP_REG_A_M,AY_PMOD_N},{PPI,MOVY,HEX_B}, arch_sh_dsp_up},
+
+/* nnmm000001 movy.w @<REG_Ayx>,<DSP_REG_YX> */ {"movy.w",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm000010 movy.w @<REG_Ayx>+,<DSP_REG_YX> */{"movy.w",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm000011 movy.w @<REG_Ayx>+r8,<DSP_REG_YX> */{"movy.w",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_3}, arch_sh4al_dsp_up},
+/* nnmm010001 movy.w <DSP_REG_AY>,@<REG_Ayx> */ {"movy.w",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_1,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm010010 movy.w <DSP_REG_AY>,@<REG_Ayx>+ */{"movy.w",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_1,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm010011 movy.w <DSP_REG_AY>,@<REG_Ayx>+r8 */{"movy.w",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_1,HEX_3}, arch_sh4al_dsp_up},
+
+/* nnmm100001 movy.l @<REG_Ayx>,<DSP_REG_YX> */ {"movy.l",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm100010 movy.l @<REG_Ayx>+,<DSP_REG_YX> */{"movy.l",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm100011 movy.l @<REG_Ayx>+r8,<DSP_REG_YX> */{"movy.l",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_3}, arch_sh4al_dsp_up},
+/* nnmm110001 movy.l <DSP_REG_AY>,@<REG_Ayx> */ {"movy.l",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_3,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm110010 movy.l <DSP_REG_AY>,@<REG_Ayx>+ */{"movy.l",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_3,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm110011 movy.l <DSP_REG_AY>,@<REG_Ayx>+r8 */{"movy.l",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_3,HEX_3}, arch_sh4al_dsp_up},
+
+/* 01aaeeffxxyyggnn pmuls Se,Sf,Dg */ {"pmuls",{DSP_REG_E,DSP_REG_F,DSP_REG_G},{PPI,PMUL}, arch_sh_dsp_up},
+/* 10100000xxyynnnn psubc <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psubc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_0}, arch_sh_dsp_up},
+/* 10110000xxyynnnn paddc <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"paddc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_0}, arch_sh_dsp_up},
+/* 10000100xxyynnnn pcmp <DSP_REG_X>,<DSP_REG_Y> */
+{"pcmp", {DSP_REG_X,DSP_REG_Y},{PPI,PPI3,HEX_8,HEX_4}, arch_sh_dsp_up},
+/* 10100100xxyynnnn pwsb <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pwsb", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_4}, arch_sh_dsp_up},
+/* 10110100xxyynnnn pwad <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pwad", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_4}, arch_sh_dsp_up},
+/* 10001000xxyynnnn pabs <DSP_REG_X>,<DSP_REG_N> */
+{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_8,HEX_8}, arch_sh_dsp_up},
+/* 1000100!xx01nnnn pabs <DSP_REG_X>,<DSP_REG_N> */
+{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9,HEX_1}, arch_sh4al_dsp_up},
+/* 10101000xxyynnnn pabs <DSP_REG_Y>,<DSP_REG_N> */
+{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_A,HEX_8}, arch_sh_dsp_up},
+/* 1010100!01yynnnn pabs <DSP_REG_Y>,<DSP_REG_N> */
+{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9,HEX_4}, arch_sh4al_dsp_up},
+/* 10011000xxyynnnn prnd <DSP_REG_X>,<DSP_REG_N> */
+{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_9,HEX_8}, arch_sh_dsp_up},
+/* 1001100!xx01nnnn prnd <DSP_REG_X>,<DSP_REG_N> */
+{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_1}, arch_sh4al_dsp_up},
+/* 10111000xxyynnnn prnd <DSP_REG_Y>,<DSP_REG_N> */
+{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_B,HEX_8}, arch_sh_dsp_up},
+/* 1011100!01yynnnn prnd <DSP_REG_Y>,<DSP_REG_N> */
+{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_4}, arch_sh4al_dsp_up},
+
+{"dct",{0},{PPI,PDC,HEX_1}, arch_sh_dsp_up},
+{"dcf",{0},{PPI,PDC,HEX_2}, arch_sh_dsp_up},
+
+/* 10000001xxyynnnn pshl <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pshl", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_1}, arch_sh_dsp_up},
+/* 00000iiiiiiinnnn pshl #<imm>,<DSP_REG_N> */ {"pshl",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_0}, arch_sh_dsp_up},
+/* 10010001xxyynnnn psha <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psha", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_1}, arch_sh_dsp_up},
+/* 00010iiiiiiinnnn psha #<imm>,<DSP_REG_N> */ {"psha",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_1}, arch_sh_dsp_up},
+/* 10100001xxyynnnn psub <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psub", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_1}, arch_sh_dsp_up},
+/* 10000101xxyynnnn psub <DSP_REG_Y>,<DSP_REG_X>,<DSP_REG_N> */
+{"psub", {DSP_REG_Y,DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_5}, arch_sh4al_dsp_up},
+/* 10110001xxyynnnn padd <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"padd", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_1}, arch_sh_dsp_up},
+/* 10010101xxyynnnn pand <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pand", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_5}, arch_sh_dsp_up},
+/* 10100101xxyynnnn pxor <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pxor", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_5}, arch_sh_dsp_up},
+/* 10110101xxyynnnn por <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"por", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_5}, arch_sh_dsp_up},
+/* 10001001xxyynnnn pdec <DSP_REG_X>,<DSP_REG_N> */
+{"pdec", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9}, arch_sh_dsp_up},
+/* 10101001xxyynnnn pdec <DSP_REG_Y>,<DSP_REG_N> */
+{"pdec", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9}, arch_sh_dsp_up},
+/* 10011001xx00nnnn pinc <DSP_REG_X>,<DSP_REG_N> */
+{"pinc", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_XX00}, arch_sh_dsp_up},
+/* 1011100100yynnnn pinc <DSP_REG_Y>,<DSP_REG_N> */
+{"pinc", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_00YY}, arch_sh_dsp_up},
+/* 10001101xxyynnnn pclr <DSP_REG_N> */
+{"pclr", {DSP_REG_N},{PPI,PPIC,HEX_8,HEX_D}, arch_sh_dsp_up},
+/* 10011101xx00nnnn pdmsb <DSP_REG_X>,<DSP_REG_N> */
+{"pdmsb", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_XX00}, arch_sh_dsp_up},
+/* 1011110100yynnnn pdmsb <DSP_REG_Y>,<DSP_REG_N> */
+{"pdmsb", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_00YY}, arch_sh_dsp_up},
+/* 11001001xxyynnnn pneg <DSP_REG_X>,<DSP_REG_N> */
+{"pneg", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_9}, arch_sh_dsp_up},
+/* 11101001xxyynnnn pneg <DSP_REG_Y>,<DSP_REG_N> */
+{"pneg", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_E,HEX_9}, arch_sh_dsp_up},
+/* 11011001xxyynnnn pcopy <DSP_REG_X>,<DSP_REG_N> */
+{"pcopy", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_9}, arch_sh_dsp_up},
+/* 11111001xxyynnnn pcopy <DSP_REG_Y>,<DSP_REG_N> */
+{"pcopy", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_F,HEX_9}, arch_sh_dsp_up},
+/* 11001101xxyynnnn psts MACH,<DSP_REG_N> */
+{"psts", {A_MACH,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_D}, arch_sh_dsp_up},
+/* 11011101xxyynnnn psts MACL,<DSP_REG_N> */
+{"psts", {A_MACL,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_D}, arch_sh_dsp_up},
+/* 11101101xxyynnnn plds <DSP_REG_N>,MACH */
+{"plds", {DSP_REG_N,A_MACH},{PPI,PPIC,HEX_E,HEX_D}, arch_sh_dsp_up},
+/* 11111101xxyynnnn plds <DSP_REG_N>,MACL */
+{"plds", {DSP_REG_N,A_MACL},{PPI,PPIC,HEX_F,HEX_D}, arch_sh_dsp_up},
+/* 10011101xx01zzzz pswap <DSP_REG_X>,<DSP_REG_N> */
+{"pswap", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_1}, arch_sh4al_dsp_up},
+/* 1011110101yyzzzz pswap <DSP_REG_Y>,<DSP_REG_N> */
+{"pswap", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_4}, arch_sh4al_dsp_up},
+
+/* 1111nnnn01011101 fabs <F_REG_N> */{"fabs",{F_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh2e_up},
+/* 1111nnn001011101 fabs <D_REG_N> */{"fabs",{D_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0000 fadd <F_REG_M>,<F_REG_N>*/{"fadd",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh2e_up},
+/* 1111nnn0mmm00000 fadd <D_REG_M>,<D_REG_N>*/{"fadd",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0100 fcmp/eq <F_REG_M>,<F_REG_N>*/{"fcmp/eq",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh2e_up},
+/* 1111nnn0mmm00100 fcmp/eq <D_REG_M>,<D_REG_N>*/{"fcmp/eq",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0101 fcmp/gt <F_REG_M>,<F_REG_N>*/{"fcmp/gt",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh2e_up},
+/* 1111nnn0mmm00101 fcmp/gt <D_REG_M>,<D_REG_N>*/{"fcmp/gt",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn010111101 fcnvds <D_REG_N>,FPUL*/{"fcnvds",{D_REG_N,FPUL_M},{HEX_F,REG_N_D,HEX_B,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn010101101 fcnvsd FPUL,<D_REG_N>*/{"fcnvsd",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_A,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0011 fdiv <F_REG_M>,<F_REG_N>*/{"fdiv",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh2e_up},
+/* 1111nnn0mmm00011 fdiv <D_REG_M>,<D_REG_N>*/{"fdiv",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnmm11101101 fipr <V_REG_M>,<V_REG_N>*/{"fipr",{V_REG_M,V_REG_N},{HEX_F,REG_NM,HEX_E,HEX_D}, arch_sh4_up},
+
+/* 1111nnnn10001101 fldi0 <F_REG_N> */{"fldi0",{F_REG_N},{HEX_F,REG_N,HEX_8,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn10011101 fldi1 <F_REG_N> */{"fldi1",{F_REG_N},{HEX_F,REG_N,HEX_9,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn00011101 flds <F_REG_N>,FPUL*/{"flds",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_1,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn00101101 float FPUL,<F_REG_N>*/{"float",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh2e_up},
+/* 1111nnn000101101 float FPUL,<D_REG_N>*/{"float",{FPUL_M,D_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1110 fmac FR0,<F_REG_M>,<F_REG_N>*/{"fmac",{F_FR0,F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_E}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1100 fmov <F_REG_M>,<F_REG_N>*/{"fmov",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh2e_up},
+/* 1111nnn1mmmm1100 fmov <DX_REG_M>,<DX_REG_N>*/{"fmov",{DX_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1000 fmov @<REG_M>,<F_REG_N>*/{"fmov",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up},
+/* 1111nnn1mmmm1000 fmov @<REG_M>,<DX_REG_N>*/{"fmov",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1010 fmov <F_REG_M>,@<REG_N>*/{"fmov",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up},
+/* 1111nnnnmmm11010 fmov <DX_REG_M>,@<REG_N>*/{"fmov",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1001 fmov @<REG_M>+,<F_REG_N>*/{"fmov",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up},
+/* 1111nnn1mmmm1001 fmov @<REG_M>+,<DX_REG_N>*/{"fmov",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1011 fmov <F_REG_M>,@-<REG_N>*/{"fmov",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up},
+/* 1111nnnnmmm11011 fmov <DX_REG_M>,@-<REG_N>*/{"fmov",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0110 fmov @(R0,<REG_M>),<F_REG_N>*/{"fmov",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up},
+/* 1111nnn1mmmm0110 fmov @(R0,<REG_M>),<DX_REG_N>*/{"fmov",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0111 fmov <F_REG_M>,@(R0,<REG_N>)*/{"fmov",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up},
+/* 1111nnnnmmm10111 fmov <DX_REG_M>,@(R0,<REG_N>)*/{"fmov",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm1000 fmov.d @<REG_M>,<DX_REG_N>*/{"fmov.d",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm11010 fmov.d <DX_REG_M>,@<REG_N>*/{"fmov.d",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm1001 fmov.d @<REG_M>+,<DX_REG_N>*/{"fmov.d",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm11011 fmov.d <DX_REG_M>,@-<REG_N>*/{"fmov.d",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm0110 fmov.d @(R0,<REG_M>),<DX_REG_N>*/{"fmov.d",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm10111 fmov.d <DX_REG_M>,@(R0,<REG_N>)*/{"fmov.d",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up},
+/* 0011nnnnmmmm0001 0011dddddddddddd fmov.d <F_REG_M>,@(<DISP12>,<REG_N>) */
+{"fmov.d",{DX_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY8}, arch_sh2a_up | arch_op32},
+/* 0011nnnnmmmm0001 0111dddddddddddd fmov.d @(<DISP12>,<REG_M>),F_REG_N */
+{"fmov.d",{A_DISP_REG_M,DX_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY8}, arch_sh2a_up | arch_op32},
+
+/* 1111nnnnmmmm1000 fmov.s @<REG_M>,<F_REG_N>*/{"fmov.s",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1010 fmov.s <F_REG_M>,@<REG_N>*/{"fmov.s",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1001 fmov.s @<REG_M>+,<F_REG_N>*/{"fmov.s",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1011 fmov.s <F_REG_M>,@-<REG_N>*/{"fmov.s",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0110 fmov.s @(R0,<REG_M>),<F_REG_N>*/{"fmov.s",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0111 fmov.s <F_REG_M>,@(R0,<REG_N>)*/{"fmov.s",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up},
+/* 0011nnnnmmmm0001 0011dddddddddddd fmov.s <F_REG_M>,@(<DISP12>,<REG_N>) */
+{"fmov.s",{F_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY4}, arch_sh2a_up | arch_op32},
+/* 0011nnnnmmmm0001 0111dddddddddddd fmov.s @(<DISP12>,<REG_M>),F_REG_N */
+{"fmov.s",{A_DISP_REG_M,F_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY4}, arch_sh2a_up | arch_op32},
+
+/* 1111nnnnmmmm0010 fmul <F_REG_M>,<F_REG_N>*/{"fmul",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh2e_up},
+/* 1111nnn0mmm00010 fmul <D_REG_M>,<D_REG_N>*/{"fmul",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01001101 fneg <F_REG_N> */{"fneg",{F_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh2e_up},
+/* 1111nnn001001101 fneg <D_REG_N> */{"fneg",{D_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111011111111101 fpchg */{"fpchg",{0},{HEX_F,HEX_7,HEX_F,HEX_D}, arch_sh4a_up},
+
+/* 1111101111111101 frchg */{"frchg",{0},{HEX_F,HEX_B,HEX_F,HEX_D}, arch_sh4_up},
+
+/* 1111nnn011111101 fsca FPUL,<D_REG_N> */{"fsca",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_F,HEX_D}, arch_sh4_up},
+
+/* 1111001111111101 fschg */{"fschg",{0},{HEX_F,HEX_3,HEX_F,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01101101 fsqrt <F_REG_N> */{"fsqrt",{F_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh3e_up | arch_sh2a_up},
+/* 1111nnn001101101 fsqrt <D_REG_N> */{"fsqrt",{D_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01111101 fsrra <F_REG_N> */{"fsrra",{F_REG_N},{HEX_F,REG_N,HEX_7,HEX_D}, arch_sh4_up},
+
+/* 1111nnnn00001101 fsts FPUL,<F_REG_N>*/{"fsts",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_0,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0001 fsub <F_REG_M>,<F_REG_N>*/{"fsub",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh2e_up},
+/* 1111nnn0mmm00001 fsub <D_REG_M>,<D_REG_N>*/{"fsub",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn00111101 ftrc <F_REG_N>,FPUL*/{"ftrc",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh2e_up},
+/* 1111nnnn00111101 ftrc <D_REG_N>,FPUL*/{"ftrc",{D_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nn0111111101 ftrv XMTRX_M4,<V_REG_n>*/{"ftrv",{XMTRX_M4,V_REG_N},{HEX_F,REG_N_B01,HEX_F,HEX_D}, arch_sh4_up},
+
+ /* 10000110nnnn0iii bclr #<imm>, <REG_N> */ {"bclr",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3c}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0000dddddddddddd bclr.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bclr.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000111nnnn1iii bld #<imm>, <REG_N> */ {"bld",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3s}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0011dddddddddddd bld.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bld.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_3,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000110nnnn1iii bset #<imm>, <REG_N> */ {"bset",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3s}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0001dddddddddddd bset.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bset.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_1,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000111nnnn0iii bst #<imm>, <REG_N> */ {"bst",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3c}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0010dddddddddddd bst.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bst.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_2,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 0100nnnn10010001 clips.b <REG_N> */ {"clips.b",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10010101 clips.w <REG_N> */ {"clips.w",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000001 clipu.b <REG_N> */ {"clipu.b",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000101 clipu.w <REG_N> */ {"clipu.w",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10010100 divs R0,<REG_N> */ {"divs",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_9,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000100 divu R0,<REG_N> */ {"divu",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0100mmmm01001011 jsr/n @<REG_M> */ {"jsr/n",{A_IND_M},{HEX_4,REG_M,HEX_4,HEX_B}, arch_sh2a_nofpu_up},
+ /* 10000011dddddddd jsr/n @@(<disp>,TBR) */ {"jsr/n",{A_DISP2_TBR},{HEX_8,HEX_3,IMM0_8BY4}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11100101 ldbank @<REG_M>,R0 */ {"ldbank",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110001 movml.l <REG_M>,@-R15 */ {"movml.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110101 movml.l @R15+,<REG_M> */ {"movml.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110000 movml.l <REG_M>,@-R15 */ {"movmu.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_0}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110100 movml.l @R15+,<REG_M> */ {"movmu.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0000nnnn00111001 movrt <REG_N> */ {"movrt",{A_REG_N},{HEX_0,REG_N,HEX_3,HEX_9}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000000 mulr R0,<REG_N> */ {"mulr",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_0}, arch_sh2a_nofpu_up},
+ /* 0000000001101000 nott */ {"nott",{A_END},{HEX_0,HEX_0,HEX_6,HEX_8}, arch_sh2a_nofpu_up},
+ /* 0000000001011011 resbank */ {"resbank",{A_END},{HEX_0,HEX_0,HEX_5,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0000000001101011 rts/n */ {"rts/n",{A_END},{HEX_0,HEX_0,HEX_6,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0000mmmm01111011 rtv/n <REG_M>*/ {"rtv/n",{A_REG_M},{HEX_0,REG_M,HEX_7,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0100nnnn11100001 stbank R0,@<REG_N>*/ {"stbank",{A_R0,A_IND_N},{HEX_4,REG_N,HEX_E,HEX_1}, arch_sh2a_nofpu_up},
+
+/* 0011nnnn0iii1001 0100dddddddddddd band.b #<imm>,@(<DISP12>,<REG_N>) */
+{"band.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_4,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1100dddddddddddd bandnot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bandnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_C,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1011dddddddddddd bldnot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bldnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_B,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 0101dddddddddddd bor.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_5,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1101dddddddddddd bornot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bornot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_D,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 0110dddddddddddd bxor.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bxor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_6,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnniiii0000 iiiiiiiiiiiiiiii movi20 #<imm>,<REG_N> */
+{"movi20",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_0,IMM0_20}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnniiii0001 iiiiiiiiiiiiiiii movi20s #<imm>,<REG_N> */
+{"movi20s",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_1,IMM0_20BY8}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 1000dddddddddddd movu.b @(<DISP12>,<REG_M>),<REG_N> */
+{"movu.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_8,DISP0_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 1001dddddddddddd movu.w @(<DISP12>,<REG_M>),<REG_N> */
+{"movu.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_9,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+
+{ 0, {0}, {0}, 0 }
+};
+
+#endif
+
+#ifdef ARCH_all
+#define INCLUDE_SHMEDIA
+#endif
+
+static void print_movxy
+ PARAMS ((const sh_opcode_info *, int, int, fprintf_ftype, void *));
+static void print_insn_ddt PARAMS ((int, struct disassemble_info *));
+static void print_dsp_reg PARAMS ((int, fprintf_ftype, void *));
+static void print_insn_ppi PARAMS ((int, struct disassemble_info *));
+
+static void
+print_movxy (op, rn, rm, fprintf_fn, stream)
+ const sh_opcode_info *op;
+ int rn, rm;
+ fprintf_ftype fprintf_fn;
+ void *stream;
+{
+ int n;
+
+ fprintf_fn (stream, "%s\t", op->name);
+ for (n = 0; n < 2; n++)
+ {
+ switch (op->arg[n])
+ {
+ case A_IND_N:
+ case AX_IND_N:
+ case AXY_IND_N:
+ case AY_IND_N:
+ case AYX_IND_N:
+ fprintf_fn (stream, "@r%d", rn);
+ break;
+ case A_INC_N:
+ case AX_INC_N:
+ case AXY_INC_N:
+ case AY_INC_N:
+ case AYX_INC_N:
+ fprintf_fn (stream, "@r%d+", rn);
+ break;
+ case AX_PMOD_N:
+ case AXY_PMOD_N:
+ fprintf_fn (stream, "@r%d+r8", rn);
+ break;
+ case AY_PMOD_N:
+ case AYX_PMOD_N:
+ fprintf_fn (stream, "@r%d+r9", rn);
+ break;
+ case DSP_REG_A_M:
+ fprintf_fn (stream, "a%c", '0' + rm);
+ break;
+ case DSP_REG_X:
+ fprintf_fn (stream, "x%c", '0' + rm);
+ break;
+ case DSP_REG_Y:
+ fprintf_fn (stream, "y%c", '0' + rm);
+ break;
+ case DSP_REG_AX:
+ fprintf_fn (stream, "%c%c",
+ (rm & 1) ? 'x' : 'a',
+ (rm & 2) ? '1' : '0');
+ break;
+ case DSP_REG_XY:
+ fprintf_fn (stream, "%c%c",
+ (rm & 1) ? 'y' : 'x',
+ (rm & 2) ? '1' : '0');
+ break;
+ case DSP_REG_AY:
+ fprintf_fn (stream, "%c%c",
+ (rm & 2) ? 'y' : 'a',
+ (rm & 1) ? '1' : '0');
+ break;
+ case DSP_REG_YX:
+ fprintf_fn (stream, "%c%c",
+ (rm & 2) ? 'x' : 'y',
+ (rm & 1) ? '1' : '0');
+ break;
+ default:
+ abort ();
+ }
+ if (n == 0)
+ fprintf_fn (stream, ",");
+ }
+}
+
+/* Print a double data transfer insn. INSN is just the lower three
+ nibbles of the insn, i.e. field a and the bit that indicates if
+ a parallel processing insn follows.
+ Return nonzero if a field b of a parallel processing insns follows. */
+
+static void
+print_insn_ddt (insn, info)
+ int insn;
+ struct disassemble_info *info;
+{
+ fprintf_ftype fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+
+ /* If this is just a nop, make sure to emit something. */
+ if (insn == 0x000)
+ fprintf_fn (stream, "nopx\tnopy");
+
+ /* If a parallel processing insn was printed before,
+ and we got a non-nop, emit a tab. */
+ if ((insn & 0x800) && (insn & 0x3ff))
+ fprintf_fn (stream, "\t");
+
+ /* Check if either the x or y part is invalid. */
+ if (((insn & 0xc) == 0 && (insn & 0x2a0))
+ || ((insn & 3) == 0 && (insn & 0x150)))
+ if (info->mach != bfd_mach_sh_dsp
+ && info->mach != bfd_mach_sh3_dsp)
+ {
+ static const sh_opcode_info *first_movx, *first_movy;
+ const sh_opcode_info *op;
+ int is_movy;
+
+ if (! first_movx)
+ {
+ for (first_movx = sh_table; first_movx->nibbles[1] != MOVX_NOPY;)
+ first_movx++;
+ for (first_movy = first_movx; first_movy->nibbles[1] != MOVY_NOPX;)
+ first_movy++;
+ }
+
+ is_movy = ((insn & 3) != 0);
+
+ if (is_movy)
+ op = first_movy;
+ else
+ op = first_movx;
+
+ while (op->nibbles[2] != (unsigned) ((insn >> 4) & 3)
+ || op->nibbles[3] != (unsigned) (insn & 0xf))
+ op++;
+
+ print_movxy (op,
+ (4 * ((insn & (is_movy ? 0x200 : 0x100)) == 0)
+ + 2 * is_movy
+ + 1 * ((insn & (is_movy ? 0x100 : 0x200)) != 0)),
+ (insn >> 6) & 3,
+ fprintf_fn, stream);
+ }
+ else
+ fprintf_fn (stream, ".word 0x%x", insn);
+ else
+ {
+ static const sh_opcode_info *first_movx, *first_movy;
+ const sh_opcode_info *opx, *opy;
+ unsigned int insn_x, insn_y;
+
+ if (! first_movx)
+ {
+ for (first_movx = sh_table; first_movx->nibbles[1] != MOVX;)
+ first_movx++;
+ for (first_movy = first_movx; first_movy->nibbles[1] != MOVY;)
+ first_movy++;
+ }
+ insn_x = (insn >> 2) & 0xb;
+ if (insn_x)
+ {
+ for (opx = first_movx; opx->nibbles[2] != insn_x;)
+ opx++;
+ print_movxy (opx, ((insn >> 9) & 1) + 4, (insn >> 7) & 1,
+ fprintf_fn, stream);
+ }
+ insn_y = (insn & 3) | ((insn >> 1) & 8);
+ if (insn_y)
+ {
+ if (insn_x)
+ fprintf_fn (stream, "\t");
+ for (opy = first_movy; opy->nibbles[2] != insn_y;)
+ opy++;
+ print_movxy (opy, ((insn >> 8) & 1) + 6, (insn >> 6) & 1,
+ fprintf_fn, stream);
+ }
+ }
+}
+
+static void
+print_dsp_reg (rm, fprintf_fn, stream)
+ int rm;
+ fprintf_ftype fprintf_fn;
+ void *stream;
+{
+ switch (rm)
+ {
+ case A_A1_NUM:
+ fprintf_fn (stream, "a1");
+ break;
+ case A_A0_NUM:
+ fprintf_fn (stream, "a0");
+ break;
+ case A_X0_NUM:
+ fprintf_fn (stream, "x0");
+ break;
+ case A_X1_NUM:
+ fprintf_fn (stream, "x1");
+ break;
+ case A_Y0_NUM:
+ fprintf_fn (stream, "y0");
+ break;
+ case A_Y1_NUM:
+ fprintf_fn (stream, "y1");
+ break;
+ case A_M0_NUM:
+ fprintf_fn (stream, "m0");
+ break;
+ case A_A1G_NUM:
+ fprintf_fn (stream, "a1g");
+ break;
+ case A_M1_NUM:
+ fprintf_fn (stream, "m1");
+ break;
+ case A_A0G_NUM:
+ fprintf_fn (stream, "a0g");
+ break;
+ default:
+ fprintf_fn (stream, "0x%x", rm);
+ break;
+ }
+}
+
+static void
+print_insn_ppi (field_b, info)
+ int field_b;
+ struct disassemble_info *info;
+{
+ static char *sx_tab[] = { "x0", "x1", "a0", "a1" };
+ static char *sy_tab[] = { "y0", "y1", "m0", "m1" };
+ fprintf_ftype fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+ unsigned int nib1, nib2, nib3;
+ unsigned int altnib1, nib4;
+ char *dc = NULL;
+ const sh_opcode_info *op;
+
+ if ((field_b & 0xe800) == 0)
+ {
+ fprintf_fn (stream, "psh%c\t#%d,",
+ field_b & 0x1000 ? 'a' : 'l',
+ (field_b >> 4) & 127);
+ print_dsp_reg (field_b & 0xf, fprintf_fn, stream);
+ return;
+ }
+ if ((field_b & 0xc000) == 0x4000 && (field_b & 0x3000) != 0x1000)
+ {
+ static char *du_tab[] = { "x0", "y0", "a0", "a1" };
+ static char *se_tab[] = { "x0", "x1", "y0", "a1" };
+ static char *sf_tab[] = { "y0", "y1", "x0", "a1" };
+ static char *sg_tab[] = { "m0", "m1", "a0", "a1" };
+
+ if (field_b & 0x2000)
+ {
+ fprintf_fn (stream, "p%s %s,%s,%s\t",
+ (field_b & 0x1000) ? "add" : "sub",
+ sx_tab[(field_b >> 6) & 3],
+ sy_tab[(field_b >> 4) & 3],
+ du_tab[(field_b >> 0) & 3]);
+ }
+ else if ((field_b & 0xf0) == 0x10
+ && info->mach != bfd_mach_sh_dsp
+ && info->mach != bfd_mach_sh3_dsp)
+ {
+ fprintf_fn (stream, "pclr %s \t", du_tab[(field_b >> 0) & 3]);
+ }
+ else if ((field_b & 0xf3) != 0)
+ {
+ fprintf_fn (stream, ".word 0x%x\t", field_b);
+ }
+ fprintf_fn (stream, "pmuls%c%s,%s,%s",
+ field_b & 0x2000 ? ' ' : '\t',
+ se_tab[(field_b >> 10) & 3],
+ sf_tab[(field_b >> 8) & 3],
+ sg_tab[(field_b >> 2) & 3]);
+ return;
+ }
+
+ nib1 = PPIC;
+ nib2 = field_b >> 12 & 0xf;
+ nib3 = field_b >> 8 & 0xf;
+ nib4 = field_b >> 4 & 0xf;
+ switch (nib3 & 0x3)
+ {
+ case 0:
+ dc = "";
+ nib1 = PPI3;
+ break;
+ case 1:
+ dc = "";
+ break;
+ case 2:
+ dc = "dct ";
+ nib3 -= 1;
+ break;
+ case 3:
+ dc = "dcf ";
+ nib3 -= 2;
+ break;
+ }
+ if (nib1 == PPI3)
+ altnib1 = PPI3NC;
+ else
+ altnib1 = nib1;
+ for (op = sh_table; op->name; op++)
+ {
+ if ((op->nibbles[1] == nib1 || op->nibbles[1] == altnib1)
+ && op->nibbles[2] == nib2
+ && op->nibbles[3] == nib3)
+ {
+ int n;
+
+ switch (op->nibbles[4])
+ {
+ case HEX_0:
+ break;
+ case HEX_XX00:
+ if ((nib4 & 3) != 0)
+ continue;
+ break;
+ case HEX_1:
+ if ((nib4 & 3) != 1)
+ continue;
+ break;
+ case HEX_00YY:
+ if ((nib4 & 0xc) != 0)
+ continue;
+ break;
+ case HEX_4:
+ if ((nib4 & 0xc) != 4)
+ continue;
+ break;
+ default:
+ abort ();
+ }
+ fprintf_fn (stream, "%s%s\t", dc, op->name);
+ for (n = 0; n < 3 && op->arg[n] != A_END; n++)
+ {
+ if (n && op->arg[1] != A_END)
+ fprintf_fn (stream, ",");
+ switch (op->arg[n])
+ {
+ case DSP_REG_N:
+ print_dsp_reg (field_b & 0xf, fprintf_fn, stream);
+ break;
+ case DSP_REG_X:
+ fprintf_fn (stream, sx_tab[(field_b >> 6) & 3]);
+ break;
+ case DSP_REG_Y:
+ fprintf_fn (stream, sy_tab[(field_b >> 4) & 3]);
+ break;
+ case A_MACH:
+ fprintf_fn (stream, "mach");
+ break;
+ case A_MACL:
+ fprintf_fn (stream, "macl");
+ break;
+ default:
+ abort ();
+ }
+ }
+ return;
+ }
+ }
+ /* Not found. */
+ fprintf_fn (stream, ".word 0x%x", field_b);
+}
+
+/* FIXME mvs: movx insns print as ".word 0x%03x", insn & 0xfff
+ (ie. the upper nibble is missing). */
+int
+print_insn_sh (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ fprintf_ftype fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+ unsigned char insn[4];
+ unsigned char nibs[8];
+ int status;
+ bfd_vma relmask = ~(bfd_vma) 0;
+ const sh_opcode_info *op;
+ unsigned int target_arch;
+ int allow_op32;
+
+ switch (info->mach)
+ {
+ case bfd_mach_sh:
+ target_arch = arch_sh1;
+ break;
+ case bfd_mach_sh4:
+ target_arch = arch_sh4;
+ break;
+ case bfd_mach_sh5:
+#ifdef INCLUDE_SHMEDIA
+ status = print_insn_sh64 (memaddr, info);
+ if (status != -2)
+ return status;
+#endif
+ /* When we get here for sh64, it's because we want to disassemble
+ SHcompact, i.e. arch_sh4. */
+ target_arch = arch_sh4;
+ break;
+ default:
+ fprintf (stderr, "sh architecture not supported\n");
+ return -1;
+ }
+
+ status = info->read_memory_func (memaddr, insn, 2, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ {
+ nibs[0] = (insn[1] >> 4) & 0xf;
+ nibs[1] = insn[1] & 0xf;
+
+ nibs[2] = (insn[0] >> 4) & 0xf;
+ nibs[3] = insn[0] & 0xf;
+ }
+ else
+ {
+ nibs[0] = (insn[0] >> 4) & 0xf;
+ nibs[1] = insn[0] & 0xf;
+
+ nibs[2] = (insn[1] >> 4) & 0xf;
+ nibs[3] = insn[1] & 0xf;
+ }
+ status = info->read_memory_func (memaddr + 2, insn + 2, 2, info);
+ if (status != 0)
+ allow_op32 = 0;
+ else
+ {
+ allow_op32 = 1;
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ {
+ nibs[4] = (insn[3] >> 4) & 0xf;
+ nibs[5] = insn[3] & 0xf;
+
+ nibs[6] = (insn[2] >> 4) & 0xf;
+ nibs[7] = insn[2] & 0xf;
+ }
+ else
+ {
+ nibs[4] = (insn[2] >> 4) & 0xf;
+ nibs[5] = insn[2] & 0xf;
+
+ nibs[6] = (insn[3] >> 4) & 0xf;
+ nibs[7] = insn[3] & 0xf;
+ }
+ }
+
+ if (nibs[0] == 0xf && (nibs[1] & 4) == 0
+ && SH_MERGE_ARCH_SET_VALID (target_arch, arch_sh_dsp_up))
+ {
+ if (nibs[1] & 8)
+ {
+ int field_b;
+
+ status = info->read_memory_func (memaddr + 2, insn, 2, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr + 2, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ field_b = insn[1] << 8 | insn[0];
+ else
+ field_b = insn[0] << 8 | insn[1];
+
+ print_insn_ppi (field_b, info);
+ print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info);
+ return 4;
+ }
+ print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info);
+ return 2;
+ }
+ for (op = sh_table; op->name; op++)
+ {
+ int n;
+ int imm = 0;
+ int rn = 0;
+ int rm = 0;
+ int rb = 0;
+ int disp_pc;
+ bfd_vma disp_pc_addr = 0;
+ int disp = 0;
+ int has_disp = 0;
+ int max_n = SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 8 : 4;
+
+ if (!allow_op32
+ && SH_MERGE_ARCH_SET (op->arch, arch_op32))
+ goto fail;
+
+ if (!SH_MERGE_ARCH_SET_VALID (op->arch, target_arch))
+ goto fail;
+ for (n = 0; n < max_n; n++)
+ {
+ int i = op->nibbles[n];
+
+ if (i < 16)
+ {
+ if (nibs[n] == i)
+ continue;
+ goto fail;
+ }
+ switch (i)
+ {
+ case BRANCH_8:
+ imm = (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x80)
+ imm |= ~0xff;
+ imm = ((char) imm) * 2 + 4;
+ goto ok;
+ case BRANCH_12:
+ imm = ((nibs[1]) << 8) | (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x800)
+ imm |= ~0xfff;
+ imm = imm * 2 + 4;
+ goto ok;
+ case IMM0_3c:
+ if (nibs[3] & 0x8)
+ goto fail;
+ imm = nibs[3] & 0x7;
+ break;
+ case IMM0_3s:
+ if (!(nibs[3] & 0x8))
+ goto fail;
+ imm = nibs[3] & 0x7;
+ break;
+ case IMM0_3Uc:
+ if (nibs[2] & 0x8)
+ goto fail;
+ imm = nibs[2] & 0x7;
+ break;
+ case IMM0_3Us:
+ if (!(nibs[2] & 0x8))
+ goto fail;
+ imm = nibs[2] & 0x7;
+ break;
+ case DISP0_12:
+ case DISP1_12:
+ disp = (nibs[5] << 8) | (nibs[6] << 4) | nibs[7];
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY2:
+ case DISP1_12BY2:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 1;
+ relmask = ~(bfd_vma) 1;
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY4:
+ case DISP1_12BY4:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 2;
+ relmask = ~(bfd_vma) 3;
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY8:
+ case DISP1_12BY8:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 3;
+ relmask = ~(bfd_vma) 7;
+ has_disp = 1;
+ goto ok;
+ case IMM0_20_4:
+ break;
+ case IMM0_20:
+ imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8)
+ | (nibs[6] << 4) | nibs[7]);
+ if (imm & 0x80000)
+ imm -= 0x100000;
+ goto ok;
+ case IMM0_20BY8:
+ imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8)
+ | (nibs[6] << 4) | nibs[7]);
+ imm <<= 8;
+ if (imm & 0x8000000)
+ imm -= 0x10000000;
+ goto ok;
+ case IMM0_4:
+ case IMM1_4:
+ imm = nibs[3];
+ goto ok;
+ case IMM0_4BY2:
+ case IMM1_4BY2:
+ imm = nibs[3] << 1;
+ goto ok;
+ case IMM0_4BY4:
+ case IMM1_4BY4:
+ imm = nibs[3] << 2;
+ goto ok;
+ case IMM0_8:
+ case IMM1_8:
+ imm = (nibs[2] << 4) | nibs[3];
+ disp = imm;
+ has_disp = 1;
+ if (imm & 0x80)
+ imm -= 0x100;
+ goto ok;
+ case PCRELIMM_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) << 1;
+ relmask = ~(bfd_vma) 1;
+ goto ok;
+ case PCRELIMM_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) << 2;
+ relmask = ~(bfd_vma) 3;
+ goto ok;
+ case IMM0_8BY2:
+ case IMM1_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) << 1;
+ goto ok;
+ case IMM0_8BY4:
+ case IMM1_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) << 2;
+ goto ok;
+ case REG_N_D:
+ if ((nibs[n] & 1) != 0)
+ goto fail;
+ /* fall through */
+ case REG_N:
+ rn = nibs[n];
+ break;
+ case REG_M:
+ rm = nibs[n];
+ break;
+ case REG_N_B01:
+ if ((nibs[n] & 0x3) != 1 /* binary 01 */)
+ goto fail;
+ rn = (nibs[n] & 0xc) >> 2;
+ break;
+ case REG_NM:
+ rn = (nibs[n] & 0xc) >> 2;
+ rm = (nibs[n] & 0x3);
+ break;
+ case REG_B:
+ rb = nibs[n] & 0x07;
+ break;
+ case SDT_REG_N:
+ /* sh-dsp: single data transfer. */
+ rn = nibs[n];
+ if ((rn & 0xc) != 4)
+ goto fail;
+ rn = rn & 0x3;
+ rn |= (!(rn & 2)) << 2;
+ break;
+ case PPI:
+ case REPEAT:
+ goto fail;
+ default:
+ abort ();
+ }
+ }
+
+ ok:
+ /* sh2a has D_REG but not X_REG. We don't know the pattern
+ doesn't match unless we check the output args to see if they
+ make sense. */
+ if (target_arch == arch_sh2a
+ && ((op->arg[0] == DX_REG_M && (rm & 1) != 0)
+ || (op->arg[1] == DX_REG_N && (rn & 1) != 0)))
+ goto fail;
+
+ fprintf_fn (stream, "%s\t", op->name);
+ disp_pc = 0;
+ for (n = 0; n < 3 && op->arg[n] != A_END; n++)
+ {
+ if (n && op->arg[1] != A_END)
+ fprintf_fn (stream, ",");
+ switch (op->arg[n])
+ {
+ case A_IMM:
+ fprintf_fn (stream, "#%d", imm);
+ break;
+ case A_R0:
+ fprintf_fn (stream, "r0");
+ break;
+ case A_REG_N:
+ fprintf_fn (stream, "r%d", rn);
+ break;
+ case A_INC_N:
+ case AS_INC_N:
+ fprintf_fn (stream, "@r%d+", rn);
+ break;
+ case A_DEC_N:
+ case AS_DEC_N:
+ fprintf_fn (stream, "@-r%d", rn);
+ break;
+ case A_IND_N:
+ case AS_IND_N:
+ fprintf_fn (stream, "@r%d", rn);
+ break;
+ case A_DISP_REG_N:
+ fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rn);
+ break;
+ case AS_PMOD_N:
+ fprintf_fn (stream, "@r%d+r8", rn);
+ break;
+ case A_REG_M:
+ fprintf_fn (stream, "r%d", rm);
+ break;
+ case A_INC_M:
+ fprintf_fn (stream, "@r%d+", rm);
+ break;
+ case A_DEC_M:
+ fprintf_fn (stream, "@-r%d", rm);
+ break;
+ case A_IND_M:
+ fprintf_fn (stream, "@r%d", rm);
+ break;
+ case A_DISP_REG_M:
+ fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rm);
+ break;
+ case A_REG_B:
+ fprintf_fn (stream, "r%d_bank", rb);
+ break;
+ case A_DISP_PC:
+ disp_pc = 1;
+ disp_pc_addr = imm + 4 + (memaddr & relmask);
+ (*info->print_address_func) (disp_pc_addr, info);
+ break;
+ case A_IND_R0_REG_N:
+ fprintf_fn (stream, "@(r0,r%d)", rn);
+ break;
+ case A_IND_R0_REG_M:
+ fprintf_fn (stream, "@(r0,r%d)", rm);
+ break;
+ case A_DISP_GBR:
+ fprintf_fn (stream, "@(%d,gbr)", has_disp?disp:imm);
+ break;
+ case A_TBR:
+ fprintf_fn (stream, "tbr");
+ break;
+ case A_DISP2_TBR:
+ fprintf_fn (stream, "@@(%d,tbr)", has_disp?disp:imm);
+ break;
+ case A_INC_R15:
+ fprintf_fn (stream, "@r15+");
+ break;
+ case A_DEC_R15:
+ fprintf_fn (stream, "@-r15");
+ break;
+ case A_R0_GBR:
+ fprintf_fn (stream, "@(r0,gbr)");
+ break;
+ case A_BDISP12:
+ case A_BDISP8:
+ {
+ bfd_vma addr;
+ addr = imm + memaddr;
+ (*info->print_address_func) (addr, info);
+ }
+ break;
+ case A_SR:
+ fprintf_fn (stream, "sr");
+ break;
+ case A_GBR:
+ fprintf_fn (stream, "gbr");
+ break;
+ case A_VBR:
+ fprintf_fn (stream, "vbr");
+ break;
+ case A_DSR:
+ fprintf_fn (stream, "dsr");
+ break;
+ case A_MOD:
+ fprintf_fn (stream, "mod");
+ break;
+ case A_RE:
+ fprintf_fn (stream, "re");
+ break;
+ case A_RS:
+ fprintf_fn (stream, "rs");
+ break;
+ case A_A0:
+ fprintf_fn (stream, "a0");
+ break;
+ case A_X0:
+ fprintf_fn (stream, "x0");
+ break;
+ case A_X1:
+ fprintf_fn (stream, "x1");
+ break;
+ case A_Y0:
+ fprintf_fn (stream, "y0");
+ break;
+ case A_Y1:
+ fprintf_fn (stream, "y1");
+ break;
+ case DSP_REG_M:
+ print_dsp_reg (rm, fprintf_fn, stream);
+ break;
+ case A_SSR:
+ fprintf_fn (stream, "ssr");
+ break;
+ case A_SPC:
+ fprintf_fn (stream, "spc");
+ break;
+ case A_MACH:
+ fprintf_fn (stream, "mach");
+ break;
+ case A_MACL:
+ fprintf_fn (stream, "macl");
+ break;
+ case A_PR:
+ fprintf_fn (stream, "pr");
+ break;
+ case A_SGR:
+ fprintf_fn (stream, "sgr");
+ break;
+ case A_DBR:
+ fprintf_fn (stream, "dbr");
+ break;
+ case F_REG_N:
+ fprintf_fn (stream, "fr%d", rn);
+ break;
+ case F_REG_M:
+ fprintf_fn (stream, "fr%d", rm);
+ break;
+ case DX_REG_N:
+ if (rn & 1)
+ {
+ fprintf_fn (stream, "xd%d", rn & ~1);
+ break;
+ }
+ case D_REG_N:
+ fprintf_fn (stream, "dr%d", rn);
+ break;
+ case DX_REG_M:
+ if (rm & 1)
+ {
+ fprintf_fn (stream, "xd%d", rm & ~1);
+ break;
+ }
+ case D_REG_M:
+ fprintf_fn (stream, "dr%d", rm);
+ break;
+ case FPSCR_M:
+ case FPSCR_N:
+ fprintf_fn (stream, "fpscr");
+ break;
+ case FPUL_M:
+ case FPUL_N:
+ fprintf_fn (stream, "fpul");
+ break;
+ case F_FR0:
+ fprintf_fn (stream, "fr0");
+ break;
+ case V_REG_N:
+ fprintf_fn (stream, "fv%d", rn * 4);
+ break;
+ case V_REG_M:
+ fprintf_fn (stream, "fv%d", rm * 4);
+ break;
+ case XMTRX_M4:
+ fprintf_fn (stream, "xmtrx");
+ break;
+ default:
+ abort ();
+ }
+ }
+
+#if 0
+ /* This code prints instructions in delay slots on the same line
+ as the instruction which needs the delay slots. This can be
+ confusing, since other disassembler don't work this way, and
+ it means that the instructions are not all in a line. So I
+ disabled it. Ian. */
+ if (!(info->flags & 1)
+ && (op->name[0] == 'j'
+ || (op->name[0] == 'b'
+ && (op->name[1] == 'r'
+ || op->name[1] == 's'))
+ || (op->name[0] == 'r' && op->name[1] == 't')
+ || (op->name[0] == 'b' && op->name[2] == '.')))
+ {
+ info->flags |= 1;
+ fprintf_fn (stream, "\t(slot ");
+ print_insn_sh (memaddr + 2, info);
+ info->flags &= ~1;
+ fprintf_fn (stream, ")");
+ return 4;
+ }
+#endif
+
+ if (disp_pc && strcmp (op->name, "mova") != 0)
+ {
+ int size;
+ bfd_byte bytes[4];
+
+ if (relmask == ~(bfd_vma) 1)
+ size = 2;
+ else
+ size = 4;
+ status = info->read_memory_func (disp_pc_addr, bytes, size, info);
+ if (status == 0)
+ {
+ unsigned int val;
+
+ if (size == 2)
+ {
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ val = bfd_getl16 (bytes);
+ else
+ val = bfd_getb16 (bytes);
+ }
+ else
+ {
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ val = bfd_getl32 (bytes);
+ else
+ val = bfd_getb32 (bytes);
+ }
+ if ((*info->symbol_at_address_func) (val, info))
+ {
+ fprintf_fn (stream, "\t! 0x");
+ (*info->print_address_func) (val, info);
+ }
+ else
+ fprintf_fn (stream, "\t! 0x%x", val);
+ }
+ }
+
+ return SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 4 : 2;
+ fail:
+ ;
+
+ }
+ fprintf_fn (stream, ".word 0x%x%x%x%x", nibs[0], nibs[1], nibs[2], nibs[3]);
+ return 2;
+}
diff --git a/slirp/COPYRIGHT b/slirp/COPYRIGHT
new file mode 100644
index 0000000..2e86862
--- /dev/null
+++ b/slirp/COPYRIGHT
@@ -0,0 +1,64 @@
+Slirp was written by Danny Gasparovski.
+Copyright (c), 1995,1996 All Rights Reserved.
+
+Slirp is maintained by Kelly Price <tygris+slirp@erols.com>
+
+Slirp is free software; "free" as in you don't have to pay for it, and you
+are free to do whatever you want with it. I do not accept any donations,
+monetary or otherwise, for Slirp. Instead, I would ask you to pass this
+potential donation to your favorite charity. In fact, I encourage
+*everyone* who finds Slirp useful to make a small donation to their
+favorite charity (for example, GreenPeace). This is not a requirement, but
+a suggestion from someone who highly values the service they provide.
+
+The copyright terms and conditions:
+
+---BEGIN---
+
+ Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgment:
+ This product includes software developed by Danny Gasparovski.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---END---
+
+This basically means you can do anything you want with the software, except
+1) call it your own, and 2) claim warranty on it. There is no warranty for
+this software. None. Nada. If you lose a million dollars while using
+Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***.
+
+If these conditions cannot be met due to legal restrictions (E.g. where it
+is against the law to give out Software without warranty), you must cease
+using the software and delete all copies you have.
+
+Slirp uses code that is copyrighted by the following people/organizations:
+
+Juha Pirkola.
+Gregory M. Christy.
+The Regents of the University of California.
+Carnegie Mellon University.
+The Australian National University.
+RSA Data Security, Inc.
+
+Please read the top of each source file for the details on the various
+copyrights.
diff --git a/slirp/bootp.c b/slirp/bootp.c
new file mode 100644
index 0000000..62cbcfd
--- /dev/null
+++ b/slirp/bootp.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU BOOTP/DHCP server
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <slirp.h>
+
+/* XXX: only DHCP is supported */
+
+#define NB_ADDR 16
+
+#define START_ADDR 15
+
+#define LEASE_TIME (24 * 3600)
+
+typedef struct {
+ uint8_t allocated;
+ uint8_t macaddr[6];
+} BOOTPClient;
+
+BOOTPClient bootp_clients[NB_ADDR];
+
+static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) \
+if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## args); fflush(dfd); }
+#else
+#define dprintf(fmt, args...)
+#endif
+
+static BOOTPClient *get_new_addr(struct in_addr *paddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_ADDR; i++) {
+ if (!bootp_clients[i].allocated)
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ return bc;
+}
+
+static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_ADDR; i++) {
+ if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ return bc;
+}
+
+static void dhcp_decode(const uint8_t *buf, int size,
+ int *pmsg_type)
+{
+ const uint8_t *p, *p_end;
+ int len, tag;
+
+ *pmsg_type = 0;
+
+ p = buf;
+ p_end = buf + size;
+ if (size < 5)
+ return;
+ if (memcmp(p, rfc1533_cookie, 4) != 0)
+ return;
+ p += 4;
+ while (p < p_end) {
+ tag = p[0];
+ if (tag == RFC1533_PAD) {
+ p++;
+ } else if (tag == RFC1533_END) {
+ break;
+ } else {
+ p++;
+ if (p >= p_end)
+ break;
+ len = *p++;
+ dprintf("dhcp: tag=0x%02x len=%d\n", tag, len);
+
+ switch(tag) {
+ case RFC2132_MSG_TYPE:
+ if (len >= 1)
+ *pmsg_type = p[0];
+ break;
+ default:
+ break;
+ }
+ p += len;
+ }
+ }
+}
+
+static void bootp_reply(struct bootp_t *bp)
+{
+ BOOTPClient *bc;
+ struct mbuf *m;
+ struct bootp_t *rbp;
+ struct sockaddr_in saddr, daddr;
+ struct in_addr dns_addr;
+ int dhcp_msg_type, val;
+ uint8_t *q;
+
+ /* extract exact DHCP msg type */
+ dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);
+ dprintf("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type);
+
+ if (dhcp_msg_type == 0)
+ dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
+
+ if (dhcp_msg_type != DHCPDISCOVER &&
+ dhcp_msg_type != DHCPREQUEST)
+ return;
+ /* XXX: this is a hack to get the client mac address */
+ memcpy(client_ethaddr, bp->bp_hwaddr, 6);
+
+ if ((m = m_get()) == NULL)
+ return;
+ m->m_data += if_maxlinkhdr;
+ rbp = (struct bootp_t *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+ memset(rbp, 0, sizeof(struct bootp_t));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ new_addr:
+ bc = get_new_addr(&daddr.sin_addr);
+ if (!bc) {
+ dprintf("no address left\n");
+ return;
+ }
+ memcpy(bc->macaddr, client_ethaddr, 6);
+ } else {
+ bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
+ if (!bc) {
+ /* if never assigned, behaves as if it was already
+ assigned (windows fix because it remembers its address) */
+ goto new_addr;
+ }
+ }
+ dprintf("offered addr=%08x\n", ntohl(daddr.sin_addr.s_addr));
+
+ saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
+ saddr.sin_port = htons(BOOTP_SERVER);
+
+ daddr.sin_port = htons(BOOTP_CLIENT);
+
+ rbp->bp_op = BOOTP_REPLY;
+ rbp->bp_xid = bp->bp_xid;
+ rbp->bp_htype = 1;
+ rbp->bp_hlen = 6;
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+ rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
+ rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
+
+ q = rbp->bp_vend;
+ memcpy(q, rfc1533_cookie, 4);
+ q += 4;
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPOFFER;
+ } else if (dhcp_msg_type == DHCPREQUEST) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPACK;
+ }
+
+ if (dhcp_msg_type == DHCPDISCOVER ||
+ dhcp_msg_type == DHCPREQUEST) {
+ *q++ = RFC2132_SRV_ID;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_NETMASK;
+ *q++ = 4;
+ *q++ = 0xff;
+ *q++ = 0xff;
+ *q++ = 0xff;
+ *q++ = 0x00;
+
+ *q++ = RFC1533_GATEWAY;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_DNS;
+ *q++ = 4;
+ dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
+ memcpy(q, &dns_addr, 4);
+ q += 4;
+
+ *q++ = RFC2132_LEASE_TIME;
+ *q++ = 4;
+ val = htonl(LEASE_TIME);
+ memcpy(q, &val, 4);
+ q += 4;
+
+ if (*slirp_hostname) {
+ val = strlen(slirp_hostname);
+ *q++ = RFC1533_HOSTNAME;
+ *q++ = val;
+ memcpy(q, slirp_hostname, val);
+ q += val;
+ }
+ }
+ *q++ = RFC1533_END;
+
+ m->m_len = sizeof(struct bootp_t) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(struct mbuf *m)
+{
+ struct bootp_t *bp = mtod(m, struct bootp_t *);
+
+ if (bp->bp_op == BOOTP_REQUEST) {
+ bootp_reply(bp);
+ }
+}
diff --git a/slirp/bootp.h b/slirp/bootp.h
new file mode 100644
index 0000000..e48f53f
--- /dev/null
+++ b/slirp/bootp.h
@@ -0,0 +1,113 @@
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_LEASE_TIME 51
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_RENEWAL_TIME 58
+#define RFC2132_REBIND_TIME 59
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPACK 5
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_MNUOPTS 160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD 184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG 192
+#define RFC1533_VENDOR_NUMOFIMG 16
+
+#define RFC1533_END 255
+#define BOOTP_VENDOR_LEN 64
+#define DHCP_OPT_LEN 312
+
+struct bootp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint8_t bp_op;
+ uint8_t bp_htype;
+ uint8_t bp_hlen;
+ uint8_t bp_hops;
+ uint32_t bp_xid;
+ uint16_t bp_secs;
+ uint16_t unused;
+ struct in_addr bp_ciaddr;
+ struct in_addr bp_yiaddr;
+ struct in_addr bp_siaddr;
+ struct in_addr bp_giaddr;
+ uint8_t bp_hwaddr[16];
+ uint8_t bp_sname[64];
+ uint8_t bp_file[128];
+ uint8_t bp_vend[DHCP_OPT_LEN];
+};
+
+void bootp_input(struct mbuf *m);
diff --git a/slirp/cksum.c b/slirp/cksum.c
new file mode 100644
index 0000000..f8f7512
--- /dev/null
+++ b/slirp/cksum.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include <slirp.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+int cksum(struct mbuf *m, int len)
+{
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ if (m->m_len == 0)
+ goto cont;
+ w = mtod(m, u_int16_t *);
+
+ mlen = m->m_len;
+
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_int8_t *)w;
+ w = (u_int16_t *)((int8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto cont;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(u_int8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(u_int8_t *)w;
+
+cont:
+#ifdef DEBUG
+ if (len) {
+ DEBUG_ERROR((dfd, "cksum: out of data\n"));
+ DEBUG_ERROR((dfd, " len = %d\n", len));
+ }
+#endif
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/slirp/ctl.h b/slirp/ctl.h
new file mode 100644
index 0000000..4a8576d
--- /dev/null
+++ b/slirp/ctl.h
@@ -0,0 +1,7 @@
+#define CTL_CMD 0
+#define CTL_EXEC 1
+#define CTL_ALIAS 2
+#define CTL_DNS 3
+
+#define CTL_SPECIAL "10.0.2.0"
+#define CTL_LOCAL "10.0.2.15"
diff --git a/slirp/debug.c b/slirp/debug.c
new file mode 100644
index 0000000..d3d8c57
--- /dev/null
+++ b/slirp/debug.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * Portions copyright (c) 2000 Kelly Price.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+FILE *dfd = NULL;
+#ifdef DEBUG
+int dostats = 1;
+#else
+int dostats = 0;
+#endif
+int slirp_debug = 0;
+
+extern char *strerror _P((int));
+
+/* Carry over one item from main.c so that the tty's restored.
+ * Only done when the tty being used is /dev/tty --RedWolf */
+extern struct termios slirp_tty_settings;
+extern int slirp_tty_restore;
+
+
+void
+debug_init(file, dbg)
+ char *file;
+ int dbg;
+{
+ /* Close the old debugging file */
+ if (dfd)
+ fclose(dfd);
+
+ dfd = fopen(file,"w");
+ if (dfd != NULL) {
+#if 0
+ fprintf(dfd,"Slirp %s - Debugging Started.\n", SLIRP_VERSION);
+#endif
+ fprintf(dfd,"Debugging Started level %i.\r\n",dbg);
+ fflush(dfd);
+ slirp_debug = dbg;
+ } else {
+ lprint("Error: Debugging file \"%s\" could not be opened: %s\r\n",
+ file, strerror(errno));
+ }
+}
+
+/*
+ * Dump a packet in the same format as tcpdump -x
+ */
+#ifdef DEBUG
+void
+dump_packet(dat, n)
+ void *dat;
+ int n;
+{
+ u_char *pptr = (u_char *)dat;
+ int j,k;
+
+ n /= 16;
+ n++;
+ DEBUG_MISC((dfd, "PACKET DUMPED: \n"));
+ for(j = 0; j < n; j++) {
+ for(k = 0; k < 6; k++)
+ DEBUG_MISC((dfd, "%02x ", *pptr++));
+ DEBUG_MISC((dfd, "\n"));
+ fflush(dfd);
+ }
+}
+#endif
+
+#if 0
+/*
+ * Statistic routines
+ *
+ * These will print statistics to the screen, the debug file (dfd), or
+ * a buffer, depending on "type", so that the stats can be sent over
+ * the link as well.
+ */
+
+void
+ttystats(ttyp)
+ struct ttys *ttyp;
+{
+ struct slirp_ifstats *is = &ttyp->ifstats;
+ char buff[512];
+
+ lprint(" \r\n");
+
+ if (if_comp & IF_COMPRESS)
+ strcpy(buff, "on");
+ else if (if_comp & IF_NOCOMPRESS)
+ strcpy(buff, "off");
+ else
+ strcpy(buff, "off (for now)");
+ lprint("Unit %d:\r\n", ttyp->unit);
+ lprint(" using %s encapsulation (VJ compression is %s)\r\n", (
+#ifdef USE_PPP
+ ttyp->proto==PROTO_PPP?"PPP":
+#endif
+ "SLIP"), buff);
+ lprint(" %d baudrate\r\n", ttyp->baud);
+ lprint(" interface is %s\r\n", ttyp->up?"up":"down");
+ lprint(" using fd %d, guardian pid is %d\r\n", ttyp->fd, ttyp->pid);
+#ifndef FULL_BOLT
+ lprint(" towrite is %d bytes\r\n", ttyp->towrite);
+#endif
+ if (ttyp->zeros)
+ lprint(" %d zeros have been typed\r\n", ttyp->zeros);
+ else if (ttyp->ones)
+ lprint(" %d ones have been typed\r\n", ttyp->ones);
+ lprint("Interface stats:\r\n");
+ lprint(" %6d output packets sent (%d bytes)\r\n", is->out_pkts, is->out_bytes);
+ lprint(" %6d output packets dropped (%d bytes)\r\n", is->out_errpkts, is->out_errbytes);
+ lprint(" %6d input packets received (%d bytes)\r\n", is->in_pkts, is->in_bytes);
+ lprint(" %6d input packets dropped (%d bytes)\r\n", is->in_errpkts, is->in_errbytes);
+ lprint(" %6d bad input packets\r\n", is->in_mbad);
+}
+
+void
+allttystats()
+{
+ struct ttys *ttyp;
+
+ for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+ ttystats(ttyp);
+}
+#endif
+
+void
+ipstats()
+{
+ lprint(" \r\n");
+
+ lprint("IP stats:\r\n");
+ lprint(" %6d total packets received (%d were unaligned)\r\n",
+ ipstat.ips_total, ipstat.ips_unaligned);
+ lprint(" %6d with incorrect version\r\n", ipstat.ips_badvers);
+ lprint(" %6d with bad header checksum\r\n", ipstat.ips_badsum);
+ lprint(" %6d with length too short (len < sizeof(iphdr))\r\n", ipstat.ips_tooshort);
+ lprint(" %6d with length too small (len < ip->len)\r\n", ipstat.ips_toosmall);
+ lprint(" %6d with bad header length\r\n", ipstat.ips_badhlen);
+ lprint(" %6d with bad packet length\r\n", ipstat.ips_badlen);
+ lprint(" %6d fragments received\r\n", ipstat.ips_fragments);
+ lprint(" %6d fragments dropped\r\n", ipstat.ips_fragdropped);
+ lprint(" %6d fragments timed out\r\n", ipstat.ips_fragtimeout);
+ lprint(" %6d packets reassembled ok\r\n", ipstat.ips_reassembled);
+ lprint(" %6d outgoing packets fragmented\r\n", ipstat.ips_fragmented);
+ lprint(" %6d total outgoing fragments\r\n", ipstat.ips_ofragments);
+ lprint(" %6d with bad protocol field\r\n", ipstat.ips_noproto);
+ lprint(" %6d total packets delivered\r\n", ipstat.ips_delivered);
+}
+
+#if 0
+void
+vjstats()
+{
+ lprint(" \r\n");
+
+ lprint("VJ compression stats:\r\n");
+
+ lprint(" %6d outbound packets (%d compressed)\r\n",
+ comp_s.sls_packets, comp_s.sls_compressed);
+ lprint(" %6d searches for connection stats (%d misses)\r\n",
+ comp_s.sls_searches, comp_s.sls_misses);
+ lprint(" %6d inbound uncompressed packets\r\n", comp_s.sls_uncompressedin);
+ lprint(" %6d inbound compressed packets\r\n", comp_s.sls_compressedin);
+ lprint(" %6d inbound unknown type packets\r\n", comp_s.sls_errorin);
+ lprint(" %6d inbound packets tossed due to error\r\n", comp_s.sls_tossed);
+}
+#endif
+
+void
+tcpstats()
+{
+ lprint(" \r\n");
+
+ lprint("TCP stats:\r\n");
+
+ lprint(" %6d packets sent\r\n", tcpstat.tcps_sndtotal);
+ lprint(" %6d data packets (%d bytes)\r\n",
+ tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte);
+ lprint(" %6d data packets retransmitted (%d bytes)\r\n",
+ tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte);
+ lprint(" %6d ack-only packets (%d delayed)\r\n",
+ tcpstat.tcps_sndacks, tcpstat.tcps_delack);
+ lprint(" %6d URG only packets\r\n", tcpstat.tcps_sndurg);
+ lprint(" %6d window probe packets\r\n", tcpstat.tcps_sndprobe);
+ lprint(" %6d window update packets\r\n", tcpstat.tcps_sndwinup);
+ lprint(" %6d control (SYN/FIN/RST) packets\r\n", tcpstat.tcps_sndctrl);
+ lprint(" %6d times tcp_output did nothing\r\n", tcpstat.tcps_didnuttin);
+
+ lprint(" %6d packets received\r\n", tcpstat.tcps_rcvtotal);
+ lprint(" %6d acks (for %d bytes)\r\n",
+ tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte);
+ lprint(" %6d duplicate acks\r\n", tcpstat.tcps_rcvdupack);
+ lprint(" %6d acks for unsent data\r\n", tcpstat.tcps_rcvacktoomuch);
+ lprint(" %6d packets received in sequence (%d bytes)\r\n",
+ tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte);
+ lprint(" %6d completely duplicate packets (%d bytes)\r\n",
+ tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte);
+
+ lprint(" %6d packets with some duplicate data (%d bytes duped)\r\n",
+ tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte);
+ lprint(" %6d out-of-order packets (%d bytes)\r\n",
+ tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte);
+ lprint(" %6d packets of data after window (%d bytes)\r\n",
+ tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin);
+ lprint(" %6d window probes\r\n", tcpstat.tcps_rcvwinprobe);
+ lprint(" %6d window update packets\r\n", tcpstat.tcps_rcvwinupd);
+ lprint(" %6d packets received after close\r\n", tcpstat.tcps_rcvafterclose);
+ lprint(" %6d discarded for bad checksums\r\n", tcpstat.tcps_rcvbadsum);
+ lprint(" %6d discarded for bad header offset fields\r\n",
+ tcpstat.tcps_rcvbadoff);
+
+ lprint(" %6d connection requests\r\n", tcpstat.tcps_connattempt);
+ lprint(" %6d connection accepts\r\n", tcpstat.tcps_accepts);
+ lprint(" %6d connections established (including accepts)\r\n", tcpstat.tcps_connects);
+ lprint(" %6d connections closed (including %d drop)\r\n",
+ tcpstat.tcps_closed, tcpstat.tcps_drops);
+ lprint(" %6d embryonic connections dropped\r\n", tcpstat.tcps_conndrops);
+ lprint(" %6d segments we tried to get rtt (%d succeeded)\r\n",
+ tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated);
+ lprint(" %6d retransmit timeouts\r\n", tcpstat.tcps_rexmttimeo);
+ lprint(" %6d connections dropped by rxmt timeout\r\n",
+ tcpstat.tcps_timeoutdrop);
+ lprint(" %6d persist timeouts\r\n", tcpstat.tcps_persisttimeo);
+ lprint(" %6d keepalive timeouts\r\n", tcpstat.tcps_keeptimeo);
+ lprint(" %6d keepalive probes sent\r\n", tcpstat.tcps_keepprobe);
+ lprint(" %6d connections dropped by keepalive\r\n", tcpstat.tcps_keepdrops);
+ lprint(" %6d correct ACK header predictions\r\n", tcpstat.tcps_predack);
+ lprint(" %6d correct data packet header predictions\n", tcpstat.tcps_preddat);
+ lprint(" %6d TCP cache misses\r\n", tcpstat.tcps_socachemiss);
+
+
+/* lprint(" Packets received too short: %d\r\n", tcpstat.tcps_rcvshort); */
+/* lprint(" Segments dropped due to PAWS: %d\r\n", tcpstat.tcps_pawsdrop); */
+
+}
+
+void
+udpstats()
+{
+ lprint(" \r\n");
+
+ lprint("UDP stats:\r\n");
+ lprint(" %6d datagrams received\r\n", udpstat.udps_ipackets);
+ lprint(" %6d with packets shorter than header\r\n", udpstat.udps_hdrops);
+ lprint(" %6d with bad checksums\r\n", udpstat.udps_badsum);
+ lprint(" %6d with data length larger than packet\r\n", udpstat.udps_badlen);
+ lprint(" %6d UDP socket cache misses\r\n", udpstat.udpps_pcbcachemiss);
+ lprint(" %6d datagrams sent\r\n", udpstat.udps_opackets);
+}
+
+void
+icmpstats()
+{
+ lprint(" \r\n");
+ lprint("ICMP stats:\r\n");
+ lprint(" %6d ICMP packets received\r\n", icmpstat.icps_received);
+ lprint(" %6d were too short\r\n", icmpstat.icps_tooshort);
+ lprint(" %6d with bad checksums\r\n", icmpstat.icps_checksum);
+ lprint(" %6d with type not supported\r\n", icmpstat.icps_notsupp);
+ lprint(" %6d with bad type feilds\r\n", icmpstat.icps_badtype);
+ lprint(" %6d ICMP packets sent in reply\r\n", icmpstat.icps_reflect);
+}
+
+void
+mbufstats()
+{
+ struct mbuf *m;
+ int i;
+
+ lprint(" \r\n");
+
+ lprint("Mbuf stats:\r\n");
+
+ lprint(" %6d mbufs allocated (%d max)\r\n", mbuf_alloced, mbuf_max);
+
+ i = 0;
+ for (m = m_freelist.m_next; m != &m_freelist; m = m->m_next)
+ i++;
+ lprint(" %6d mbufs on free list\r\n", i);
+
+ i = 0;
+ for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next)
+ i++;
+ lprint(" %6d mbufs on used list\r\n", i);
+ lprint(" %6d mbufs queued as packets\r\n\r\n", if_queued);
+}
+
+void
+sockstats()
+{
+ char buff[256];
+ int n;
+ struct socket *so;
+
+ lprint(" \r\n");
+
+ lprint(
+ "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\r\n");
+
+ for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+
+ n = sprintf(buff, "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE");
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s,
+ inet_ntoa(so->so_laddr), ntohs(so->so_lport));
+ lprint("%15s %5d %5d %5d\r\n",
+ inet_ntoa(so->so_faddr), ntohs(so->so_fport),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+
+ for (so = udb.so_next; so != &udb; so = so->so_next) {
+
+ n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000);
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s,
+ inet_ntoa(so->so_laddr), ntohs(so->so_lport));
+ lprint("%15s %5d %5d %5d\r\n",
+ inet_ntoa(so->so_faddr), ntohs(so->so_fport),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+}
+
+#if 0
+void
+slirp_exit(exit_status)
+ int exit_status;
+{
+ struct ttys *ttyp;
+
+ DEBUG_CALL("slirp_exit");
+ DEBUG_ARG("exit_status = %d", exit_status);
+
+ if (dostats) {
+ lprint_print = (int (*) _P((void *, const char *, va_list)))vfprintf;
+ if (!dfd)
+ debug_init("slirp_stats", 0xf);
+ lprint_arg = (char **)&dfd;
+
+ ipstats();
+ tcpstats();
+ udpstats();
+ icmpstats();
+ mbufstats();
+ sockstats();
+ allttystats();
+ vjstats();
+ }
+
+ for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+ tty_detached(ttyp, 1);
+
+ if (slirp_forked) {
+ /* Menendez time */
+ if (kill(getppid(), SIGQUIT) < 0)
+ lprint("Couldn't kill parent process %ld!\n",
+ (long) getppid());
+ }
+
+ /* Restore the terminal if we gotta */
+ if(slirp_tty_restore)
+ tcsetattr(0,TCSANOW, &slirp_tty_settings); /* NOW DAMMIT! */
+ exit(exit_status);
+}
+#endif
diff --git a/slirp/debug.h b/slirp/debug.h
new file mode 100644
index 0000000..6e8444d
--- /dev/null
+++ b/slirp/debug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define PRN_STDERR 1
+#define PRN_SPRINTF 2
+
+extern FILE *dfd;
+extern FILE *lfd;
+extern int dostats;
+extern int slirp_debug;
+
+#define DBG_CALL 0x1
+#define DBG_MISC 0x2
+#define DBG_ERROR 0x4
+#define DEBUG_DEFAULT DBG_CALL|DBG_MISC|DBG_ERROR
+
+#ifdef DEBUG
+#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
+#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
+#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
+#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
+#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
+
+
+#else
+
+#define DEBUG_CALL(x)
+#define DEBUG_ARG(x, y)
+#define DEBUG_ARGS(x)
+#define DEBUG_MISC(x)
+#define DEBUG_ERROR(x)
+
+#endif
+
+void debug_init _P((char *, int));
+//void ttystats _P((struct ttys *));
+void allttystats _P((void));
+void ipstats _P((void));
+void vjstats _P((void));
+void tcpstats _P((void));
+void udpstats _P((void));
+void icmpstats _P((void));
+void mbufstats _P((void));
+void sockstats _P((void));
+void slirp_exit _P((int));
+
diff --git a/slirp/icmp_var.h b/slirp/icmp_var.h
new file mode 100644
index 0000000..03fc8c3
--- /dev/null
+++ b/slirp/icmp_var.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93
+ * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp
+ */
+
+#ifndef _NETINET_ICMP_VAR_H_
+#define _NETINET_ICMP_VAR_H_
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol.
+ */
+struct icmpstat {
+/* statistics related to input messages processed */
+ u_long icps_received; /* #ICMP packets received */
+ u_long icps_tooshort; /* packet < ICMP_MINLEN */
+ u_long icps_checksum; /* bad checksum */
+ u_long icps_notsupp; /* #ICMP packets not supported */
+ u_long icps_badtype; /* #with bad type feild */
+ u_long icps_reflect; /* number of responses */
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */
+#define ICMPCTL_STATS 2 /* statistics (read-only) */
+#define ICMPCTL_MAXID 3
+
+#define ICMPCTL_NAMES { \
+ { 0, 0 }, \
+ { "maskrepl", CTLTYPE_INT }, \
+ { "stats", CTLTYPE_STRUCT }, \
+}
+
+extern struct icmpstat icmpstat;
+
+#endif
diff --git a/slirp/if.c b/slirp/if.c
new file mode 100644
index 0000000..94132ca
--- /dev/null
+++ b/slirp/if.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+int if_mtu, if_mru;
+int if_comp;
+int if_maxlinkhdr;
+int if_queued = 0; /* Number of packets queued so far */
+int if_thresh = 10; /* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+
+struct mbuf if_fastq; /* fast queue (for interactive data) */
+struct mbuf if_batchq; /* queue for non-interactive data */
+struct mbuf *next_m; /* Pointer to next mbuf to output */
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+void
+ifs_insque(ifm, ifmhead)
+ struct mbuf *ifm, *ifmhead;
+{
+ ifm->ifs_next = ifmhead->ifs_next;
+ ifmhead->ifs_next = ifm;
+ ifm->ifs_prev = ifmhead;
+ ifm->ifs_next->ifs_prev = ifm;
+}
+
+void
+ifs_remque(ifm)
+ struct mbuf *ifm;
+{
+ ifm->ifs_prev->ifs_next = ifm->ifs_next;
+ ifm->ifs_next->ifs_prev = ifm->ifs_prev;
+}
+
+void
+if_init()
+{
+#if 0
+ /*
+ * Set if_maxlinkhdr to 48 because it's 40 bytes for TCP/IP,
+ * and 8 bytes for PPP, but need to have it on an 8byte boundary
+ */
+#ifdef USE_PPP
+ if_maxlinkhdr = 48;
+#else
+ if_maxlinkhdr = 40;
+#endif
+#else
+ /* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
+ if_maxlinkhdr = 2 + 14 + 40;
+#endif
+ if_mtu = 1500;
+ if_mru = 1500;
+ if_comp = IF_AUTOCOMP;
+ if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq;
+ if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq;
+ // sl_compress_init(&comp_s);
+ next_m = &if_batchq;
+}
+
+#if 0
+/*
+ * This shouldn't be needed since the modem is blocking and
+ * we don't expect any signals, but what the hell..
+ */
+inline int
+writen(fd, bptr, n)
+ int fd;
+ char *bptr;
+ int n;
+{
+ int ret;
+ int total;
+
+ /* This should succeed most of the time */
+ ret = send(fd, bptr, n,0);
+ if (ret == n || ret <= 0)
+ return ret;
+
+ /* Didn't write everything, go into the loop */
+ total = ret;
+ while (n > total) {
+ ret = send(fd, bptr+total, n-total,0);
+ if (ret <= 0)
+ return ret;
+ total += ret;
+ }
+ return total;
+}
+
+/*
+ * if_input - read() the tty, do "top level" processing (ie: check for any escapes),
+ * and pass onto (*ttyp->if_input)
+ *
+ * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet.
+ */
+#define INBUFF_SIZE 2048 /* XXX */
+void
+if_input(ttyp)
+ struct ttys *ttyp;
+{
+ u_char if_inbuff[INBUFF_SIZE];
+ int if_n;
+
+ DEBUG_CALL("if_input");
+ DEBUG_ARG("ttyp = %lx", (long)ttyp);
+
+ if_n = recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE,0);
+
+ DEBUG_MISC((dfd, " read %d bytes\n", if_n));
+
+ if (if_n <= 0) {
+ if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) {
+ if (ttyp->up)
+ link_up--;
+ tty_detached(ttyp, 0);
+ }
+ return;
+ }
+ if (if_n == 1) {
+ if (*if_inbuff == '0') {
+ ttyp->ones = 0;
+ if (++ttyp->zeros >= 5)
+ slirp_exit(0);
+ return;
+ }
+ if (*if_inbuff == '1') {
+ ttyp->zeros = 0;
+ if (++ttyp->ones >= 5)
+ tty_detached(ttyp, 0);
+ return;
+ }
+ }
+ ttyp->ones = ttyp->zeros = 0;
+
+ (*ttyp->if_input)(ttyp, if_inbuff, if_n);
+}
+#endif
+
+/*
+ * if_output: Queue packet into an output queue.
+ * There are 2 output queue's, if_fastq and if_batchq.
+ * Each output queue is a doubly linked list of double linked lists
+ * of mbufs, each list belonging to one "session" (socket). This
+ * way, we can output packets fairly by sending one packet from each
+ * session, instead of all the packets from one session, then all packets
+ * from the next session, etc. Packets on the if_fastq get absolute
+ * priority, but if one session hogs the link, it gets "downgraded"
+ * to the batchq until it runs out of packets, then it'll return
+ * to the fastq (eg. if the user does an ls -alR in a telnet session,
+ * it'll temporarily get downgraded to the batchq)
+ */
+void
+if_output(so, ifm)
+ struct socket *so;
+ struct mbuf *ifm;
+{
+ struct mbuf *ifq;
+ int on_fastq = 1;
+
+ DEBUG_CALL("if_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ifm = %lx", (long)ifm);
+
+ /*
+ * First remove the mbuf from m_usedlist,
+ * since we're gonna use m_next and m_prev ourselves
+ * XXX Shouldn't need this, gotta change dtom() etc.
+ */
+ if (ifm->m_flags & M_USEDLIST) {
+ remque(ifm);
+ ifm->m_flags &= ~M_USEDLIST;
+ }
+
+ /*
+ * See if there's already a batchq list for this session.
+ * This can include an interactive session, which should go on fastq,
+ * but gets too greedy... hence it'll be downgraded from fastq to batchq.
+ * We mustn't put this packet back on the fastq (or we'll send it out of order)
+ * XXX add cache here?
+ */
+ for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) {
+ if (so == ifq->ifq_so) {
+ /* A match! */
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ }
+
+ /* No match, check which queue to put it on */
+ if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
+ ifq = if_fastq.ifq_prev;
+ on_fastq = 1;
+ /*
+ * Check if this packet is a part of the last
+ * packet's session
+ */
+ if (ifq->ifq_so == so) {
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ } else
+ ifq = if_batchq.ifq_prev;
+
+ /* Create a new doubly linked list for this session */
+ ifm->ifq_so = so;
+ ifs_init(ifm);
+ insque(ifm, ifq);
+
+diddit:
+ ++if_queued;
+
+ if (so) {
+ /* Update *_queued */
+ so->so_queued++;
+ so->so_nqueued++;
+ /*
+ * Check if the interactive session should be downgraded to
+ * the batchq. A session is downgraded if it has queued 6
+ * packets without pausing, and at least 3 of those packets
+ * have been sent over the link
+ * (XXX These are arbitrary numbers, probably not optimal..)
+ */
+ if (on_fastq && ((so->so_nqueued >= 6) &&
+ (so->so_nqueued - so->so_queued) >= 3)) {
+
+ /* Remove from current queue... */
+ remque(ifm->ifs_next);
+
+ /* ...And insert in the new. That'll teach ya! */
+ insque(ifm->ifs_next, &if_batchq);
+ }
+ }
+
+#ifndef FULL_BOLT
+ /*
+ * This prevents us from malloc()ing too many mbufs
+ */
+ if (link_up) {
+ /* if_start will check towrite */
+ if_start();
+ }
+#endif
+}
+
+/*
+ * Send a packet
+ * We choose a packet based on it's position in the output queues;
+ * If there are packets on the fastq, they are sent FIFO, before
+ * everything else. Otherwise we choose the first packet from the
+ * batchq and send it. the next packet chosen will be from the session
+ * after this one, then the session after that one, and so on.. So,
+ * for example, if there are 3 ftp session's fighting for bandwidth,
+ * one packet will be sent from the first session, then one packet
+ * from the second session, then one packet from the third, then back
+ * to the first, etc. etc.
+ */
+void
+if_start(void)
+{
+ struct mbuf *ifm, *ifqt;
+
+ DEBUG_CALL("if_start");
+
+ if (if_queued == 0)
+ return; /* Nothing to do */
+
+ again:
+ /* check if we can really output */
+ if (!slirp_can_output())
+ return;
+
+ /*
+ * See which queue to get next packet from
+ * If there's something in the fastq, select it immediately
+ */
+ if (if_fastq.ifq_next != &if_fastq) {
+ ifm = if_fastq.ifq_next;
+ } else {
+ /* Nothing on fastq, see if next_m is valid */
+ if (next_m != &if_batchq)
+ ifm = next_m;
+ else
+ ifm = if_batchq.ifq_next;
+
+ /* Set which packet to send on next iteration */
+ next_m = ifm->ifq_next;
+ }
+ /* Remove it from the queue */
+ ifqt = ifm->ifq_prev;
+ remque(ifm);
+ --if_queued;
+
+ /* If there are more packets for this session, re-queue them */
+ if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
+ insque(ifm->ifs_next, ifqt);
+ ifs_remque(ifm);
+ }
+
+ /* Update so_queued */
+ if (ifm->ifq_so) {
+ if (--ifm->ifq_so->so_queued == 0)
+ /* If there's no more queued, reset nqueued */
+ ifm->ifq_so->so_nqueued = 0;
+ }
+
+ /* Encapsulate the packet for sending */
+ if_encap(ifm->m_data, ifm->m_len);
+
+ m_free(ifm);
+
+ if (if_queued)
+ goto again;
+}
diff --git a/slirp/if.h b/slirp/if.h
new file mode 100644
index 0000000..5d96a90
--- /dev/null
+++ b/slirp/if.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS 0x01 /* We want compression */
+#define IF_NOCOMPRESS 0x02 /* Do not do compression */
+#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
+#define IF_NOCIDCOMP 0x08 /* CID compression */
+
+/* Needed for FreeBSD */
+#undef if_mtu
+extern int if_mtu;
+extern int if_mru; /* MTU and MRU */
+extern int if_comp; /* Flags for compression */
+extern int if_maxlinkhdr;
+extern int if_queued; /* Number of packets queued so far */
+extern int if_thresh; /* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+
+extern struct mbuf if_fastq; /* fast queue (for interactive data) */
+extern struct mbuf if_batchq; /* queue for non-interactive data */
+extern struct mbuf *next_m;
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+/* Interface statistics */
+struct slirp_ifstats {
+ u_int out_pkts; /* Output packets */
+ u_int out_bytes; /* Output bytes */
+ u_int out_errpkts; /* Output Error Packets */
+ u_int out_errbytes; /* Output Error Bytes */
+ u_int in_pkts; /* Input packets */
+ u_int in_bytes; /* Input bytes */
+ u_int in_errpkts; /* Input Error Packets */
+ u_int in_errbytes; /* Input Error Bytes */
+
+ u_int bytes_saved; /* Number of bytes that compression "saved" */
+ /* ie: number of bytes that didn't need to be sent over the link
+ * because of compression */
+
+ u_int in_mbad; /* Bad incoming packets */
+};
+
+#endif
diff --git a/slirp/ip.h b/slirp/ip.h
new file mode 100644
index 0000000..371537d
--- /dev/null
+++ b/slirp/ip.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#ifdef WORDS_BIGENDIAN
+# ifndef NTOHL
+# define NTOHL(d)
+# endif
+# ifndef NTOHS
+# define NTOHS(d)
+# endif
+# ifndef HTONL
+# define HTONL(d)
+# endif
+# ifndef HTONS
+# define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+# define NTOHL(d) ((d) = ntohl((d)))
+# endif
+# ifndef NTOHS
+# define NTOHS(d) ((d) = ntohs((u_int16_t)(d)))
+# endif
+# ifndef HTONL
+# define HTONL(d) ((d) = htonl((d)))
+# endif
+# ifndef HTONS
+# define HTONS(d) ((d) = htons((u_int16_t)(d)))
+# endif
+#endif
+
+typedef u_int32_t n_long; /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip {
+#ifdef WORDS_BIGENDIAN
+ u_int ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#else
+ u_int ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+ u_int8_t ip_tos; /* type of service */
+ u_int16_t ip_len; /* total length */
+ u_int16_t ip_id; /* identification */
+ u_int16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_int8_t ip_ttl; /* time to live */
+ u_int8_t ip_p; /* protocol */
+ u_int16_t ip_sum; /* checksum */
+ struct in_addr ip_src,ip_dst; /* source and dest address */
+};
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ u_int8_t ipt_code; /* IPOPT_TS */
+ u_int8_t ipt_len; /* size of structure (variable) */
+ u_int8_t ipt_ptr; /* index of current entry */
+#ifdef WORDS_BIGENDIAN
+ u_int ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#else
+ u_int ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#endif
+ union ipt_timestamp {
+ n_long ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ n_long ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
+
+#ifdef HAVE_SYS_TYPES32_H /* Overcome some Solaris 2.x junk */
+#include <sys/types32.h>
+#else
+#if SIZEOF_CHAR_P == 4
+typedef caddr_t caddr32_t;
+#else
+typedef u_int32_t caddr32_t;
+#endif
+#endif
+
+#if SIZEOF_CHAR_P == 4
+typedef struct ipq *ipqp_32;
+typedef struct ipasfrag *ipasfragp_32;
+#else
+typedef caddr32_t ipqp_32;
+typedef caddr32_t ipasfragp_32;
+#endif
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ caddr32_t ih_next, ih_prev; /* for protocol sequence q's */
+ u_int8_t ih_x1; /* (unused) */
+ u_int8_t ih_pr; /* protocol */
+ u_int16_t ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+
+/*
+ * Ip reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ * size 28 bytes
+ */
+struct ipq {
+ ipqp_32 next,prev; /* to other reass headers */
+ u_int8_t ipq_ttl; /* time for reass q to live */
+ u_int8_t ipq_p; /* protocol of this fragment */
+ u_int16_t ipq_id; /* sequence id for reassembly */
+ ipasfragp_32 ipq_next,ipq_prev;
+ /* to ip headers of fragments */
+ struct in_addr ipq_src,ipq_dst;
+};
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_next must be at same offset as ipq_next above
+ */
+struct ipasfrag {
+#ifdef WORDS_BIGENDIAN
+ u_int ip_v:4,
+ ip_hl:4;
+#else
+ u_int ip_hl:4,
+ ip_v:4;
+#endif
+ /* BUG : u_int changed to u_int8_t.
+ * sizeof(u_int)==4 on linux 2.0
+ */
+ u_int8_t ipf_mff; /* XXX overlays ip_tos: use low bit
+ * to avoid destroying tos (PPPDTRuu);
+ * copied from (ip_off&IP_MF) */
+ u_int16_t ip_len;
+ u_int16_t ip_id;
+ u_int16_t ip_off;
+ u_int8_t ip_ttl;
+ u_int8_t ip_p;
+ u_int16_t ip_sum;
+ ipasfragp_32 ipf_next; /* next fragment */
+ ipasfragp_32 ipf_prev; /* previous fragment */
+};
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN 40
+
+struct ipoption {
+ struct in_addr ipopt_dst; /* first-hop dst if source routed */
+ int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
+};
+
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+
+struct ipstat {
+ u_long ips_total; /* total packets received */
+ u_long ips_badsum; /* checksum bad */
+ u_long ips_tooshort; /* packet too short */
+ u_long ips_toosmall; /* not enough data */
+ u_long ips_badhlen; /* ip header length < data size */
+ u_long ips_badlen; /* ip length < ip header length */
+ u_long ips_fragments; /* fragments received */
+ u_long ips_fragdropped; /* frags dropped (dups, out of space) */
+ u_long ips_fragtimeout; /* fragments timed out */
+ u_long ips_forward; /* packets forwarded */
+ u_long ips_cantforward; /* packets rcvd for unreachable dest */
+ u_long ips_redirectsent; /* packets forwarded on same net */
+ u_long ips_noproto; /* unknown or unsupported protocol */
+ u_long ips_delivered; /* datagrams delivered to upper level*/
+ u_long ips_localout; /* total ip packets generated here */
+ u_long ips_odropped; /* lost packets due to nobufs, etc. */
+ u_long ips_reassembled; /* total packets reassembled ok */
+ u_long ips_fragmented; /* datagrams successfully fragmented */
+ u_long ips_ofragments; /* output fragments created */
+ u_long ips_cantfrag; /* don't fragment flag was set, etc. */
+ u_long ips_badoptions; /* error in option processing */
+ u_long ips_noroute; /* packets discarded due to no route */
+ u_long ips_badvers; /* ip version != 4 */
+ u_long ips_rawout; /* total raw ip packets generated */
+ u_long ips_unaligned; /* times the ip packet was not aligned */
+};
+
+extern struct ipstat ipstat;
+extern struct ipq ipq; /* ip reass. queue */
+extern u_int16_t ip_id; /* ip packet ctr, for ids */
+extern int ip_defttl; /* default IP ttl */
+
+#endif
diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c
new file mode 100644
index 0000000..b67a373
--- /dev/null
+++ b/slirp/ip_icmp.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+struct icmpstat icmpstat;
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a psuedo-ping packet */
+char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static int icmp_flush[19] = {
+/* ECHO REPLY (0) */ 0,
+ 1,
+ 1,
+/* DEST UNREACH (3) */ 1,
+/* SOURCE QUENCH (4)*/ 1,
+/* REDIRECT (5) */ 1,
+ 1,
+ 1,
+/* ECHO (8) */ 0,
+/* ROUTERADVERT (9) */ 1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */ 0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */ 0,
+/* INFO REPLY (16) */ 0,
+/* ADDR MASK (17) */ 0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(m, hlen)
+ struct mbuf *m;
+ int hlen;
+{
+ register struct icmp *icp;
+ register struct ip *ip=mtod(m, struct ip *);
+ int icmplen=ip->ip_len;
+ /* int code; */
+
+ DEBUG_CALL("icmp_input");
+ DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ icmpstat.icps_received++;
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that its not corrupted and of at least minimum length.
+ */
+ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
+ icmpstat.icps_tooshort++;
+ freeit:
+ m_freem(m);
+ goto end_error;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icp = mtod(m, struct icmp *);
+ if (cksum(m, icmplen)) {
+ icmpstat.icps_checksum++;
+ goto freeit;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ /* icmpstat.icps_inhist[icp->icmp_type]++; */
+ /* code = icp->icmp_code; */
+
+ DEBUG_ARG("icmp_type = %d", icp->icmp_type);
+ switch (icp->icmp_type) {
+ case ICMP_ECHO:
+ icp->icmp_type = ICMP_ECHOREPLY;
+ ip->ip_len += hlen; /* since ip_input subtracts this */
+ if (ip->ip_dst.s_addr == alias_addr.s_addr) {
+ icmp_reflect(m);
+ } else {
+ struct socket *so;
+ struct sockaddr_in addr;
+ if ((so = socreate()) == NULL) goto freeit;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ m_free(m);
+ goto end_error;
+ }
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_fport = htons(7);
+ so->so_laddr = ip->ip_src;
+ so->so_lport = htons(9);
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+
+ /* Send the packet */
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else {
+ addr.sin_addr = so->so_faddr;
+ }
+ addr.sin_port = so->so_fport;
+ if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ udp_detach(so);
+ }
+ } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
+ break;
+ case ICMP_UNREACH:
+ /* XXX? report error? close socket? */
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TSTAMP:
+ case ICMP_MASKREQ:
+ case ICMP_REDIRECT:
+ icmpstat.icps_notsupp++;
+ m_freem(m);
+ break;
+
+ default:
+ icmpstat.icps_badtype++;
+ m_freem(m);
+ } /* swith */
+
+end_error:
+ /* m is m_free()'d xor put in a socket xor or given to ip_send */
+ return;
+}
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ */
+/*
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * mbuf *msrc is used as a template, but is NOT m_free()'d.
+ * It is reported as the bad ip packet. The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
+ * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
+ */
+
+#define ICMP_MAXDATALEN (IP_MSS-28)
+void
+icmp_error(msrc, type, code, minsize, message)
+ struct mbuf *msrc;
+ u_char type;
+ u_char code;
+ int minsize;
+ char *message;
+{
+ unsigned hlen, shlen, s_ip_len;
+ register struct ip *ip;
+ register struct icmp *icp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("icmp_error");
+ DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_ARG("msrc_len = %d", msrc->m_len);
+
+ if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
+
+ /* check msrc */
+ if(!msrc) goto end_error;
+ ip = mtod(msrc, struct ip *);
+#if DEBUG
+ { char bufa[20], bufb[20];
+ strcpy(bufa, inet_ntoa(ip->ip_src));
+ strcpy(bufb, inet_ntoa(ip->ip_dst));
+ DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
+ }
+#endif
+ if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
+
+ shlen=ip->ip_hl << 2;
+ s_ip_len=ip->ip_len;
+ if(ip->ip_p == IPPROTO_ICMP) {
+ icp = (struct icmp *)((char *)ip + shlen);
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
+ }
+
+ /* make a copy */
+ if(!(m=m_get())) goto end_error; /* get mbuf */
+ { int new_m_size;
+ new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
+ if(new_m_size>m->m_size) m_inc(m, new_m_size);
+ }
+ memcpy(m->m_data, msrc->m_data, msrc->m_len);
+ m->m_len = msrc->m_len; /* copy msrc to m */
+
+ /* make the header of the reply packet */
+ ip = mtod(m, struct ip *);
+ hlen= sizeof(struct ip ); /* no options in reply */
+
+ /* fill in icmp */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ icp = mtod(m, struct icmp *);
+
+ if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */
+ else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */
+ s_ip_len=ICMP_MAXDATALEN;
+
+ m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */
+
+ /* min. size = 8+sizeof(struct ip)+8 */
+
+ icp->icmp_type = type;
+ icp->icmp_code = code;
+ icp->icmp_id = 0;
+ icp->icmp_seq = 0;
+
+ memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
+ HTONS(icp->icmp_ip.ip_len);
+ HTONS(icp->icmp_ip.ip_id);
+ HTONS(icp->icmp_ip.ip_off);
+
+#if DEBUG
+ if(message) { /* DEBUG : append message to ICMP packet */
+ int message_len;
+ char *cpnt;
+ message_len=strlen(message);
+ if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
+ cpnt=(char *)m->m_data+m->m_len;
+ memcpy(cpnt, message, message_len);
+ m->m_len+=message_len;
+ }
+#endif
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, m->m_len);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len = m->m_len;
+
+ ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+
+ ip->ip_ttl = MAXTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_dst = ip->ip_src; /* ip adresses */
+ ip->ip_src = alias_addr;
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+ icmpstat.icps_reflect++;
+
+end_error:
+ return;
+}
+#undef ICMP_MAXDATALEN
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ int optlen = hlen - sizeof(struct ip );
+ register struct icmp *icp;
+
+ /*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ if (optlen > 0) {
+ /*
+ * Strip out original options by copying rest of first
+ * mbuf's data back, and adjust the IP length.
+ */
+ memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
+ (unsigned )(m->m_len - hlen));
+ hlen -= optlen;
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len -= optlen;
+ m->m_len -= optlen;
+ }
+
+ ip->ip_ttl = MAXTTL;
+ { /* swap */
+ struct in_addr icmp_dst;
+ icmp_dst = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = icmp_dst;
+ }
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+ icmpstat.icps_reflect++;
+}
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
new file mode 100644
index 0000000..8c9b5a1
--- /dev/null
+++ b/slirp/ip_icmp.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef u_int32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_char ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ u_short icd_id;
+ u_short icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ u_short ipm_void;
+ u_short ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union {
+ struct id_ts {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ uint32_t id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input _P((struct mbuf *, int));
+void icmp_error _P((struct mbuf *, u_char, u_char, int, char *));
+void icmp_reflect _P((struct mbuf *));
+
+#endif
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
new file mode 100644
index 0000000..4f5bfd9
--- /dev/null
+++ b/slirp/ip_input.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+int ip_defttl;
+struct ipstat ipstat;
+struct ipq ipq;
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init()
+{
+ ipq.next = ipq.prev = (ipqp_32)&ipq;
+ ip_id = tt.tv_sec & 0xffff;
+ udp_init();
+ tcp_init();
+ ip_defttl = IPDEFTTL;
+}
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ip_input(m)
+ struct mbuf *m;
+{
+ register struct ip *ip;
+ int hlen;
+
+ DEBUG_CALL("ip_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ ipstat.ips_total++;
+
+ if (m->m_len < sizeof (struct ip)) {
+ ipstat.ips_toosmall++;
+ return;
+ }
+
+ ip = mtod(m, struct ip *);
+
+ if (ip->ip_v != IPVERSION) {
+ ipstat.ips_badvers++;
+ goto bad;
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
+ ipstat.ips_badhlen++; /* or packet too short */
+ goto bad;
+ }
+
+ /* keep ip header intact for ICMP reply
+ * ip->ip_sum = cksum(m, hlen);
+ * if (ip->ip_sum) {
+ */
+ if(cksum(m,hlen)) {
+ ipstat.ips_badsum++;
+ goto bad;
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ ipstat.ips_badlen++;
+ goto bad;
+ }
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_len < ip->ip_len) {
+ ipstat.ips_tooshort++;
+ goto bad;
+ }
+ /* Should drop packet if mbuf too long? hmmm... */
+ if (m->m_len > ip->ip_len)
+ m_adj(m, ip->ip_len - m->m_len);
+
+ /* check ip_ttl for a correct ICMP reply */
+ if(ip->ip_ttl==0 || ip->ip_ttl==1) {
+ icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
+ goto bad;
+ }
+
+ /*
+ * Process options and, if not destined for us,
+ * ship it on. ip_dooptions returns 1 when an
+ * error was detected (causing an icmp message
+ * to be sent and the original packet to be freed).
+ */
+/* We do no IP options */
+/* if (hlen > sizeof (struct ip) && ip_dooptions(m))
+ * goto next;
+ */
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ *
+ * XXX This should fail, don't fragment yet
+ */
+ if (ip->ip_off &~ IP_DF) {
+ register struct ipq *fp;
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ for (fp = (struct ipq *) ipq.next; fp != &ipq;
+ fp = (struct ipq *) fp->next)
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+ fp = 0;
+ found:
+
+ /*
+ * Adjust ip_len to not reflect header,
+ * set ip_mff if more fragments are expected,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF)
+ ((struct ipasfrag *)ip)->ipf_mff |= 1;
+ else
+ ((struct ipasfrag *)ip)->ipf_mff &= ~1;
+
+ ip->ip_off <<= 3;
+
+ /*
+ * If datagram marked as having more fragments
+ * or if this is not the first fragment,
+ * attempt reassembly; if it succeeds, proceed.
+ */
+ if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
+ ipstat.ips_fragments++;
+ ip = ip_reass((struct ipasfrag *)ip, fp);
+ if (ip == 0)
+ return;
+ ipstat.ips_reassembled++;
+ m = dtom(ip);
+ } else
+ if (fp)
+ ip_freef(fp);
+
+ } else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ ipstat.ips_delivered++;
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcp_input(m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ udp_input(m, hlen);
+ break;
+ case IPPROTO_ICMP:
+ icmp_input(m, hlen);
+ break;
+ default:
+ ipstat.ips_noproto++;
+ m_free(m);
+ }
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram. If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+struct ip *
+ip_reass(ip, fp)
+ register struct ipasfrag *ip;
+ register struct ipq *fp;
+{
+ register struct mbuf *m = dtom(ip);
+ register struct ipasfrag *q;
+ int hlen = ip->ip_hl << 2;
+ int i, next;
+
+ DEBUG_CALL("ip_reass");
+ DEBUG_ARG("ip = %lx", (long)ip);
+ DEBUG_ARG("fp = %lx", (long)fp);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ * Fragment m_data is concatenated.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == 0) {
+ struct mbuf *t;
+ if ((t = m_get()) == NULL) goto dropfrag;
+ fp = mtod(t, struct ipq *);
+ insque_32(fp, &ipq);
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->ipq_next = fp->ipq_prev = (ipasfragp_32)fp;
+ fp->ipq_src = ((struct ip *)ip)->ip_src;
+ fp->ipq_dst = ((struct ip *)ip)->ip_dst;
+ q = (struct ipasfrag *)fp;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = (struct ipasfrag *)fp->ipq_next; q != (struct ipasfrag *)fp;
+ q = (struct ipasfrag *)q->ipf_next)
+ if (q->ip_off > ip->ip_off)
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (q->ipf_prev != (ipasfragp_32)fp) {
+ i = ((struct ipasfrag *)(q->ipf_prev))->ip_off +
+ ((struct ipasfrag *)(q->ipf_prev))->ip_len - ip->ip_off;
+ if (i > 0) {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(dtom(ip), i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
+ i = (ip->ip_off + ip->ip_len) - q->ip_off;
+ if (i < q->ip_len) {
+ q->ip_len -= i;
+ q->ip_off += i;
+ m_adj(dtom(q), i);
+ break;
+ }
+ q = (struct ipasfrag *) q->ipf_next;
+ m_freem(dtom((struct ipasfrag *) q->ipf_prev));
+ ip_deq((struct ipasfrag *) q->ipf_prev);
+ }
+
+insert:
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ */
+ ip_enq(ip, (struct ipasfrag *) q->ipf_prev);
+ next = 0;
+ for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp;
+ q = (struct ipasfrag *) q->ipf_next) {
+ if (q->ip_off != next)
+ return (0);
+ next += q->ip_len;
+ }
+ if (((struct ipasfrag *)(q->ipf_prev))->ipf_mff & 1)
+ return (0);
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ q = (struct ipasfrag *) fp->ipq_next;
+ m = dtom(q);
+
+ q = (struct ipasfrag *) q->ipf_next;
+ while (q != (struct ipasfrag *)fp) {
+ struct mbuf *t;
+ t = dtom(q);
+ q = (struct ipasfrag *) q->ipf_next;
+ m_cat(m, t);
+ }
+
+ /*
+ * Create header for new ip packet by
+ * modifying header of first packet;
+ * dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+ ip = (struct ipasfrag *) fp->ipq_next;
+
+ /*
+ * If the fragments concatenated to an mbuf that's
+ * bigger than the total size of the fragment, then and
+ * m_ext buffer was alloced. But fp->ipq_next points to
+ * the old buffer (in the mbuf), so we must point ip
+ * into the new buffer.
+ */
+ if (m->m_flags & M_EXT) {
+ int delta;
+ delta = (char *)ip - m->m_dat;
+ ip = (struct ipasfrag *)(m->m_ext + delta);
+ }
+
+ /* DEBUG_ARG("ip = %lx", (long)ip);
+ * ip=(struct ipasfrag *)m->m_data; */
+
+ ip->ip_len = next;
+ ip->ipf_mff &= ~1;
+ ((struct ip *)ip)->ip_src = fp->ipq_src;
+ ((struct ip *)ip)->ip_dst = fp->ipq_dst;
+ remque_32(fp);
+ (void) m_free(dtom(fp));
+ m = dtom(ip);
+ m->m_len += (ip->ip_hl << 2);
+ m->m_data -= (ip->ip_hl << 2);
+
+ return ((struct ip *)ip);
+
+dropfrag:
+ ipstat.ips_fragdropped++;
+ m_freem(m);
+ return (0);
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+void
+ip_freef(fp)
+ struct ipq *fp;
+{
+ register struct ipasfrag *q, *p;
+
+ for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp;
+ q = p) {
+ p = (struct ipasfrag *) q->ipf_next;
+ ip_deq(q);
+ m_freem(dtom(q));
+ }
+ remque_32(fp);
+ (void) m_free(dtom(fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+void
+ip_enq(p, prev)
+ register struct ipasfrag *p, *prev;
+{
+ DEBUG_CALL("ip_enq");
+ DEBUG_ARG("prev = %lx", (long)prev);
+ p->ipf_prev = (ipasfragp_32) prev;
+ p->ipf_next = prev->ipf_next;
+ ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = (ipasfragp_32) p;
+ prev->ipf_next = (ipasfragp_32) p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+void
+ip_deq(p)
+ register struct ipasfrag *p;
+{
+ ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
+ ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo()
+{
+ register struct ipq *fp;
+
+ DEBUG_CALL("ip_slowtimo");
+
+ fp = (struct ipq *) ipq.next;
+ if (fp == 0)
+ return;
+
+ while (fp != &ipq) {
+ --fp->ipq_ttl;
+ fp = (struct ipq *) fp->next;
+ if (((struct ipq *)(fp->prev))->ipq_ttl == 0) {
+ ipstat.ips_fragtimeout++;
+ ip_freef((struct ipq *) fp->prev);
+ }
+ }
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+
+#ifdef notdef
+
+int
+ip_dooptions(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register u_char *cp;
+ register struct ip_timestamp *ipt;
+ register struct in_ifaddr *ia;
+/* int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */
+ int opt, optlen, cnt, off, code, type, forward = 0;
+ struct in_addr *sin, dst;
+typedef u_int32_t n_time;
+ n_time ntime;
+
+ dst = ip->ip_dst;
+ cp = (u_char *)(ip + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= 0 || optlen > cnt) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ /*
+ * Source routing with record.
+ * Find interface with current destination address.
+ * If none on this machine then drop if strictly routed,
+ * or do nothing if loosely routed.
+ * Record interface address and bring up next address
+ * component. If strictly routed make sure next
+ * address is on directly accessible net.
+ */
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ ipaddr.sin_addr = ip->ip_dst;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+ if (ia == 0) {
+ if (opt == IPOPT_SSRR) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ /*
+ * Loose routing, and not at next destination
+ * yet; nothing to do except forward.
+ */
+ break;
+ }
+ off--; / * 0 origin * /
+ if (off > optlen - sizeof(struct in_addr)) {
+ /*
+ * End of source route. Should be for us.
+ */
+ save_rte(cp, ip->ip_src);
+ break;
+ }
+ /*
+ * locate outgoing interface
+ */
+ bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ if (opt == IPOPT_SSRR) {
+#define INA struct in_ifaddr *
+#define SA struct sockaddr *
+ if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+ ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+ } else
+ ia = ip_rtaddr(ipaddr.sin_addr);
+ if (ia == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ ip->ip_dst = ipaddr.sin_addr;
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ /*
+ * Let ip_intr's mcast routing check handle mcast pkts
+ */
+ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+ break;
+
+ case IPOPT_RR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ /*
+ * If no space remains, ignore.
+ */
+ off--; * 0 origin *
+ if (off > optlen - sizeof(struct in_addr))
+ break;
+ bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ /*
+ * locate outgoing interface; if we're the destination,
+ * use the incoming interface (should be same).
+ */
+ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+ (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ goto bad;
+ }
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ ipt = (struct ip_timestamp *)cp;
+ if (ipt->ipt_len < 5)
+ goto bad;
+ if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
+ if (++ipt->ipt_oflw == 0)
+ goto bad;
+ break;
+ }
+ sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+ switch (ipt->ipt_flg) {
+
+ case IPOPT_TS_TSONLY:
+ break;
+
+ case IPOPT_TS_TSANDADDR:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ ipaddr.sin_addr = dst;
+ ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
+ m->m_pkthdr.rcvif);
+ if (ia == 0)
+ continue;
+ bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+ (caddr_t)sin, sizeof(struct in_addr));
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS_PRESPEC:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+ sizeof(struct in_addr));
+ if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+ continue;
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ default:
+ goto bad;
+ }
+ ntime = iptime();
+ bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+ sizeof(n_time));
+ ipt->ipt_ptr += sizeof(n_time);
+ }
+ }
+ if (forward) {
+ ip_forward(m, 1);
+ return (1);
+ }
+ }
+ }
+ return (0);
+bad:
+ /* ip->ip_len -= ip->ip_hl << 2; XXX icmp_error adds in hdr length */
+
+/* Not yet */
+ icmp_error(m, type, code, 0, 0);
+
+ ipstat.ips_badoptions++;
+ return (1);
+}
+
+#endif /* notdef */
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(m, mopt)
+ register struct mbuf *m;
+ struct mbuf *mopt;
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+
+ olen = (ip->ip_hl<<2) - sizeof (struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof (struct ip) + olen);
+ memcpy(opts, opts + olen, (unsigned)i);
+ m->m_len -= olen;
+
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
new file mode 100644
index 0000000..f3dc9b7
--- /dev/null
+++ b/slirp/ip_output.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+u_int16_t ip_id;
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(so, m0)
+ struct socket *so;
+ struct mbuf *m0;
+{
+ register struct ip *ip;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof(struct ip );
+ int len, off, error = 0;
+
+ DEBUG_CALL("ip_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m0 = %lx", (long)m0);
+
+ /* We do no options */
+/* if (opt) {
+ * m = ip_insertoptions(m, opt, &len);
+ * hlen = len;
+ * }
+ */
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_hl = hlen >> 2;
+ ipstat.ips_localout++;
+
+ /*
+ * Verify that we have any chance at all of being able to queue
+ * the packet or packet fragments
+ */
+ /* XXX Hmmm... */
+/* if (if_queued > if_thresh && towrite <= 0) {
+ * error = ENOBUFS;
+ * goto bad;
+ * }
+ */
+
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((u_int16_t)ip->ip_len <= if_mtu) {
+ ip->ip_len = htons((u_int16_t)ip->ip_len);
+ ip->ip_off = htons((u_int16_t)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+
+ if_output(so, m);
+ goto done;
+ }
+
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF) {
+ error = -1;
+ ipstat.ips_cantfrag++;
+ goto bad;
+ }
+
+ len = (if_mtu - hlen) &~ 7; /* ip databytes per packet */
+ if (len < 8) {
+ error = -1;
+ goto bad;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = sizeof (struct ip);
+ for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
+ register struct ip *mhip;
+ m = m_get();
+ if (m == 0) {
+ error = -1;
+ ipstat.ips_odropped++;
+ goto sendorfree;
+ }
+ m->m_data += if_maxlinkhdr;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+
+ /* No options */
+/* if (hlen > sizeof (struct ip)) {
+ * mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
+ * mhip->ip_hl = mhlen >> 2;
+ * }
+ */
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (u_int16_t)ip->ip_len)
+ len = (u_int16_t)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = htons((u_int16_t)(len + mhlen));
+
+ if (m_copy(m, m0, off, len) < 0) {
+ error = -1;
+ goto sendorfree;
+ }
+
+ mhip->ip_off = htons((u_int16_t)mhip->ip_off);
+ mhip->ip_sum = 0;
+ mhip->ip_sum = cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ ipstat.ips_ofragments++;
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ */
+ m = m0;
+ m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);
+ ip->ip_len = htons((u_int16_t)m->m_len);
+ ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+sendorfree:
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = 0;
+ if (error == 0)
+ if_output(so, m);
+ else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ ipstat.ips_fragmented++;
+ }
+
+done:
+ return (error);
+
+bad:
+ m_freem(m0);
+ goto done;
+}
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
new file mode 100644
index 0000000..a9260af
--- /dev/null
+++ b/slirp/libslirp.h
@@ -0,0 +1,41 @@
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#ifdef _WIN32
+#include <winsock2.h>
+int inet_aton(const char *cp, struct in_addr *ia);
+#else
+#include <sys/select.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void slirp_init(void);
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_input(const uint8_t *pkt, int pkt_len);
+
+/* you must provide the following functions: */
+int slirp_can_output(void);
+void slirp_output(const uint8_t *pkt, int pkt_len);
+
+int slirp_redir(int is_udp, int host_port,
+ struct in_addr guest_addr, int guest_port);
+int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
+ int guest_port);
+
+extern const char *tftp_prefix;
+extern char slirp_hostname[33];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/slirp/main.h b/slirp/main.h
new file mode 100644
index 0000000..181b6ae
--- /dev/null
+++ b/slirp/main.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define TOWRITEMAX 512
+
+extern struct timeval tt;
+extern int link_up;
+extern int slirp_socket;
+extern int slirp_socket_unit;
+extern int slirp_socket_port;
+extern u_int32_t slirp_socket_addr;
+extern char *slirp_socket_passwd;
+extern int ctty_closed;
+
+/*
+ * Get the difference in 2 times from updtim()
+ * Allow for wraparound times, "just in case"
+ * x is the greater of the 2 (current time) and y is
+ * what it's being compared against.
+ */
+#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
+
+extern char *slirp_tty;
+extern char *exec_shell;
+extern u_int curtime;
+extern fd_set *global_readfds, *global_writefds, *global_xfds;
+extern struct in_addr ctl_addr;
+extern struct in_addr special_addr;
+extern struct in_addr alias_addr;
+extern struct in_addr our_addr;
+extern struct in_addr loopback_addr;
+extern struct in_addr dns_addr;
+extern char *username;
+extern char *socket_path;
+extern int towrite_max;
+extern int ppp_exit;
+extern int so_options;
+extern int tcp_keepintvl;
+extern uint8_t client_ethaddr[6];
+
+#define PROTO_SLIP 0x1
+#ifdef USE_PPP
+#define PROTO_PPP 0x2
+#endif
+
+void if_encap(const uint8_t *ip_data, int ip_data_len);
diff --git a/slirp/mbuf.c b/slirp/mbuf.c
new file mode 100644
index 0000000..3769baf
--- /dev/null
+++ b/slirp/mbuf.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/*
+ * mbuf's in SLiRP are much simpler than the real mbufs in
+ * FreeBSD. They are fixed size, determined by the MTU,
+ * so that one whole packet can fit. Mbuf's cannot be
+ * chained together. If there's more data than the mbuf
+ * could hold, an external malloced buffer is pointed to
+ * by m_ext (and the data pointers) and M_EXT is set in
+ * the flags
+ */
+
+#include <slirp.h>
+
+struct mbuf *mbutl;
+char *mclrefcnt;
+int mbuf_alloced = 0;
+struct mbuf m_freelist, m_usedlist;
+int mbuf_thresh = 30;
+int mbuf_max = 0;
+int msize;
+
+void
+m_init()
+{
+ m_freelist.m_next = m_freelist.m_prev = &m_freelist;
+ m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist;
+ msize_init();
+}
+
+void
+msize_init()
+{
+ /*
+ * Find a nice value for msize
+ * XXX if_maxlinkhdr already in mtu
+ */
+ msize = (if_mtu>if_mru?if_mtu:if_mru) +
+ if_maxlinkhdr + sizeof(struct m_hdr ) + 6;
+}
+
+/*
+ * Get an mbuf from the free list, if there are none
+ * malloc one
+ *
+ * Because fragmentation can occur if we alloc new mbufs and
+ * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
+ * which tells m_free to actually free() it
+ */
+struct mbuf *
+m_get()
+{
+ register struct mbuf *m;
+ int flags = 0;
+
+ DEBUG_CALL("m_get");
+
+ if (m_freelist.m_next == &m_freelist) {
+ m = (struct mbuf *)malloc(msize);
+ if (m == NULL) goto end_error;
+ mbuf_alloced++;
+ if (mbuf_alloced > mbuf_thresh)
+ flags = M_DOFREE;
+ if (mbuf_alloced > mbuf_max)
+ mbuf_max = mbuf_alloced;
+ } else {
+ m = m_freelist.m_next;
+ remque(m);
+ }
+
+ /* Insert it in the used list */
+ insque(m,&m_usedlist);
+ m->m_flags = (flags | M_USEDLIST);
+
+ /* Initialise it */
+ m->m_size = msize - sizeof(struct m_hdr);
+ m->m_data = m->m_dat;
+ m->m_len = 0;
+ m->m_nextpkt = 0;
+ m->m_prevpkt = 0;
+end_error:
+ DEBUG_ARG("m = %lx", (long )m);
+ return m;
+}
+
+void
+m_free(m)
+ struct mbuf *m;
+{
+
+ DEBUG_CALL("m_free");
+ DEBUG_ARG("m = %lx", (long )m);
+
+ if(m) {
+ /* Remove from m_usedlist */
+ if (m->m_flags & M_USEDLIST)
+ remque(m);
+
+ /* If it's M_EXT, free() it */
+ if (m->m_flags & M_EXT)
+ free(m->m_ext);
+
+ /*
+ * Either free() it or put it on the free list
+ */
+ if (m->m_flags & M_DOFREE) {
+ free(m);
+ mbuf_alloced--;
+ } else if ((m->m_flags & M_FREELIST) == 0) {
+ insque(m,&m_freelist);
+ m->m_flags = M_FREELIST; /* Clobber other flags */
+ }
+ } /* if(m) */
+}
+
+/*
+ * Copy data from one mbuf to the end of
+ * the other.. if result is too big for one mbuf, malloc()
+ * an M_EXT data segment
+ */
+void
+m_cat(m, n)
+ register struct mbuf *m, *n;
+{
+ /*
+ * If there's no room, realloc
+ */
+ if (M_FREEROOM(m) < n->m_len)
+ m_inc(m,m->m_size+MINCSIZE);
+
+ memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
+ m->m_len += n->m_len;
+
+ m_free(n);
+}
+
+
+/* make m size bytes large */
+void
+m_inc(m, size)
+ struct mbuf *m;
+ int size;
+{
+ int datasize;
+
+ /* some compiles throw up on gotos. This one we can fake. */
+ if(m->m_size>size) return;
+
+ if (m->m_flags & M_EXT) {
+ datasize = m->m_data - m->m_ext;
+ m->m_ext = (char *)realloc(m->m_ext,size);
+/* if (m->m_ext == NULL)
+ * return (struct mbuf *)NULL;
+ */
+ m->m_data = m->m_ext + datasize;
+ } else {
+ char *dat;
+ datasize = m->m_data - m->m_dat;
+ dat = (char *)malloc(size);
+/* if (dat == NULL)
+ * return (struct mbuf *)NULL;
+ */
+ memcpy(dat, m->m_dat, m->m_size);
+
+ m->m_ext = dat;
+ m->m_data = m->m_ext + datasize;
+ m->m_flags |= M_EXT;
+ }
+
+ m->m_size = size;
+
+}
+
+
+
+void
+m_adj(m, len)
+ struct mbuf *m;
+ int len;
+{
+ if (m == NULL)
+ return;
+ if (len >= 0) {
+ /* Trim from head */
+ m->m_data += len;
+ m->m_len -= len;
+ } else {
+ /* Trim from tail */
+ len = -len;
+ m->m_len -= len;
+ }
+}
+
+
+/*
+ * Copy len bytes from m, starting off bytes into n
+ */
+int
+m_copy(n, m, off, len)
+ struct mbuf *n, *m;
+ int off, len;
+{
+ if (len > M_FREEROOM(n))
+ return -1;
+
+ memcpy((n->m_data + n->m_len), (m->m_data + off), len);
+ n->m_len += len;
+ return 0;
+}
+
+
+/*
+ * Given a pointer into an mbuf, return the mbuf
+ * XXX This is a kludge, I should eliminate the need for it
+ * Fortunately, it's not used often
+ */
+struct mbuf *
+dtom(dat)
+ void *dat;
+{
+ struct mbuf *m;
+
+ DEBUG_CALL("dtom");
+ DEBUG_ARG("dat = %lx", (long )dat);
+
+ /* bug corrected for M_EXT buffers */
+ for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) {
+ if (m->m_flags & M_EXT) {
+ if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
+ return m;
+ } else {
+ if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
+ return m;
+ }
+ }
+
+ DEBUG_ERROR((dfd, "dtom failed"));
+
+ return (struct mbuf *)0;
+}
+
diff --git a/slirp/mbuf.h b/slirp/mbuf.h
new file mode 100644
index 0000000..8cc292b
--- /dev/null
+++ b/slirp/mbuf.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mbuf.h 8.3 (Berkeley) 1/21/94
+ * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
+ */
+
+#ifndef _MBUF_H_
+#define _MBUF_H_
+
+#define m_freem m_free
+
+
+#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
+
+/*
+ * Macros for type conversion
+ * mtod(m,t) - convert mbuf pointer to data pointer of correct type
+ * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX)
+ */
+#define mtod(m,t) ((t)(m)->m_data)
+/* #define dtom(x) ((struct mbuf *)((int)(x) & ~(M_SIZE-1))) */
+
+/* XXX About mbufs for slirp:
+ * Only one mbuf is ever used in a chain, for each "cell" of data.
+ * m_nextpkt points to the next packet, if fragmented.
+ * If the data is too large, the M_EXT is used, and a larger block
+ * is alloced. Therefore, m_free[m] must check for M_EXT and if set
+ * free the m_ext. This is inefficient memory-wise, but who cares.
+ */
+
+/* XXX should union some of these! */
+/* header at beginning of each mbuf: */
+struct m_hdr {
+ struct mbuf *mh_next; /* Linked list of mbufs */
+ struct mbuf *mh_prev;
+ struct mbuf *mh_nextpkt; /* Next packet in queue/record */
+ struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
+ int mh_flags; /* Misc flags */
+
+ int mh_size; /* Size of data */
+ struct socket *mh_so;
+
+ caddr_t mh_data; /* Location of data */
+ int mh_len; /* Amount of data in this mbuf */
+};
+
+/*
+ * How much room is in the mbuf, from m_data to the end of the mbuf
+ */
+#define M_ROOM(m) ((m->m_flags & M_EXT)? \
+ (((m)->m_ext + (m)->m_size) - (m)->m_data) \
+ : \
+ (((m)->m_dat + (m)->m_size) - (m)->m_data))
+
+/*
+ * How much free room there is
+ */
+#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
+#define M_TRAILINGSPACE M_FREEROOM
+
+struct mbuf {
+ struct m_hdr m_hdr;
+ union M_dat {
+ char m_dat_[1]; /* ANSI don't like 0 sized arrays */
+ char *m_ext_;
+ } M_dat;
+};
+
+#define m_next m_hdr.mh_next
+#define m_prev m_hdr.mh_prev
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_prevpkt m_hdr.mh_prevpkt
+#define m_flags m_hdr.mh_flags
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_size m_hdr.mh_size
+#define m_dat M_dat.m_dat_
+#define m_ext M_dat.m_ext_
+#define m_so m_hdr.mh_so
+
+#define ifq_prev m_prev
+#define ifq_next m_next
+#define ifs_prev m_prevpkt
+#define ifs_next m_nextpkt
+#define ifq_so m_so
+
+#define M_EXT 0x01 /* m_ext points to more (malloced) data */
+#define M_FREELIST 0x02 /* mbuf is on free list */
+#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */
+#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free()
+ * it rather than putting it on the free list */
+
+/*
+ * Mbuf statistics. XXX
+ */
+
+struct mbstat {
+ int mbs_alloced; /* Number of mbufs allocated */
+
+};
+
+extern struct mbstat mbstat;
+extern int mbuf_alloced;
+extern struct mbuf m_freelist, m_usedlist;
+extern int mbuf_max;
+
+void m_init _P((void));
+void msize_init _P((void));
+struct mbuf * m_get _P((void));
+void m_free _P((struct mbuf *));
+void m_cat _P((register struct mbuf *, register struct mbuf *));
+void m_inc _P((struct mbuf *, int));
+void m_adj _P((struct mbuf *, int));
+int m_copy _P((struct mbuf *, struct mbuf *, int, int));
+struct mbuf * dtom _P((void *));
+
+#endif
diff --git a/slirp/misc.c b/slirp/misc.c
new file mode 100644
index 0000000..2c42fd1
--- /dev/null
+++ b/slirp/misc.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+
+u_int curtime, time_fasttimo, last_slowtimo, detach_time;
+u_int detach_wait = 600000; /* 10 minutes */
+
+#if 0
+int x_port = -1;
+int x_display = 0;
+int x_screen = 0;
+
+int
+show_x(buff, inso)
+ char *buff;
+ struct socket *inso;
+{
+ if (x_port < 0) {
+ lprint("X Redir: X not being redirected.\r\n");
+ } else {
+ lprint("X Redir: In sh/bash/zsh/etc. type: DISPLAY=%s:%d.%d; export DISPLAY\r\n",
+ inet_ntoa(our_addr), x_port, x_screen);
+ lprint("X Redir: In csh/tcsh/etc. type: setenv DISPLAY %s:%d.%d\r\n",
+ inet_ntoa(our_addr), x_port, x_screen);
+ if (x_display)
+ lprint("X Redir: Redirecting to display %d\r\n", x_display);
+ }
+
+ return CFG_OK;
+}
+
+
+/*
+ * XXX Allow more than one X redirection?
+ */
+void
+redir_x(inaddr, start_port, display, screen)
+ u_int32_t inaddr;
+ int start_port;
+ int display;
+ int screen;
+{
+ int i;
+
+ if (x_port >= 0) {
+ lprint("X Redir: X already being redirected.\r\n");
+ show_x(0, 0);
+ } else {
+ for (i = 6001 + (start_port-1); i <= 6100; i++) {
+ if (solisten(htons(i), inaddr, htons(6000 + display), 0)) {
+ /* Success */
+ x_port = i - 6000;
+ x_display = display;
+ x_screen = screen;
+ show_x(0, 0);
+ return;
+ }
+ }
+ lprint("X Redir: Error: Couldn't redirect a port for X. Weird.\r\n");
+ }
+}
+#endif
+
+#ifndef HAVE_INET_ATON
+int
+inet_aton(cp, ia)
+ const char *cp;
+ struct in_addr *ia;
+{
+ u_int32_t addr = inet_addr(cp);
+ if (addr == 0xffffffff)
+ return 0;
+ ia->s_addr = addr;
+ return 1;
+}
+#endif
+
+/*
+ * Get our IP address and put it in our_addr
+ */
+void
+getouraddr()
+{
+ char buff[256];
+ struct hostent *he = NULL;
+
+ if (gethostname(buff,256) == 0)
+ he = gethostbyname(buff);
+ if (he)
+ our_addr = *(struct in_addr *)he->h_addr;
+ if (our_addr.s_addr == 0)
+ our_addr.s_addr = loopback_addr.s_addr;
+}
+
+#if SIZEOF_CHAR_P == 8
+
+struct quehead_32 {
+ u_int32_t qh_link;
+ u_int32_t qh_rlink;
+};
+
+inline void
+insque_32(a, b)
+ void *a;
+ void *b;
+{
+ register struct quehead_32 *element = (struct quehead_32 *) a;
+ register struct quehead_32 *head = (struct quehead_32 *) b;
+ element->qh_link = head->qh_link;
+ head->qh_link = (u_int32_t)element;
+ element->qh_rlink = (u_int32_t)head;
+ ((struct quehead_32 *)(element->qh_link))->qh_rlink
+ = (u_int32_t)element;
+}
+
+inline void
+remque_32(a)
+ void *a;
+{
+ register struct quehead_32 *element = (struct quehead_32 *) a;
+ ((struct quehead_32 *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead_32 *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = 0;
+}
+
+#endif /* SIZEOF_CHAR_P == 8 */
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+inline void
+insque(a, b)
+ void *a, *b;
+{
+ register struct quehead *element = (struct quehead *) a;
+ register struct quehead *head = (struct quehead *) b;
+ element->qh_link = head->qh_link;
+ head->qh_link = (struct quehead *)element;
+ element->qh_rlink = (struct quehead *)head;
+ ((struct quehead *)(element->qh_link))->qh_rlink
+ = (struct quehead *)element;
+}
+
+inline void
+remque(a)
+ void *a;
+{
+ register struct quehead *element = (struct quehead *) a;
+ ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = NULL;
+ /* element->qh_link = NULL; TCP FIN1 crashes if you do this. Why ? */
+}
+
+/* #endif */
+
+
+int
+add_exec(ex_ptr, do_pty, exec, addr, port)
+ struct ex_list **ex_ptr;
+ int do_pty;
+ char *exec;
+ int addr;
+ int port;
+{
+ struct ex_list *tmp_ptr;
+
+ /* First, check if the port is "bound" */
+ for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
+ if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr)
+ return -1;
+ }
+
+ tmp_ptr = *ex_ptr;
+ *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
+ (*ex_ptr)->ex_fport = port;
+ (*ex_ptr)->ex_addr = addr;
+ (*ex_ptr)->ex_pty = do_pty;
+ (*ex_ptr)->ex_exec = strdup(exec);
+ (*ex_ptr)->ex_next = tmp_ptr;
+ return 0;
+}
+
+#ifndef HAVE_STRERROR
+
+/*
+ * For systems with no strerror
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(error)
+ int error;
+{
+ if (error < sys_nerr)
+ return sys_errlist[error];
+ else
+ return "Unknown error.";
+}
+
+#endif
+
+
+#ifdef _WIN32
+
+int
+fork_exec(so, ex, do_pty)
+ struct socket *so;
+ char *ex;
+ int do_pty;
+{
+ /* not implemented */
+ return 0;
+}
+
+#else
+
+int
+slirp_openpty(amaster, aslave)
+ int *amaster, *aslave;
+{
+ register int master, slave;
+
+#ifdef HAVE_GRANTPT
+ char *ptr;
+
+ if ((master = open("/dev/ptmx", O_RDWR)) < 0 ||
+ grantpt(master) < 0 ||
+ unlockpt(master) < 0 ||
+ (ptr = ptsname(master)) == NULL) {
+ close(master);
+ return -1;
+ }
+
+ if ((slave = open(ptr, O_RDWR)) < 0 ||
+ ioctl(slave, I_PUSH, "ptem") < 0 ||
+ ioctl(slave, I_PUSH, "ldterm") < 0 ||
+ ioctl(slave, I_PUSH, "ttcompat") < 0) {
+ close(master);
+ close(slave);
+ return -1;
+ }
+
+ *amaster = master;
+ *aslave = slave;
+ return 0;
+
+#else
+
+ static char line[] = "/dev/ptyXX";
+ register const char *cp1, *cp2;
+
+ for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
+ line[8] = *cp1;
+ for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
+ line[9] = *cp2;
+ if ((master = open(line, O_RDWR, 0)) == -1) {
+ if (errno == ENOENT)
+ return (-1); /* out of ptys */
+ } else {
+ line[5] = 't';
+ /* These will fail */
+ (void) chown(line, getuid(), 0);
+ (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+#ifdef HAVE_REVOKE
+ (void) revoke(line);
+#endif
+ if ((slave = open(line, O_RDWR, 0)) != -1) {
+ *amaster = master;
+ *aslave = slave;
+ return 0;
+ }
+ (void) close(master);
+ line[5] = 'p';
+ }
+ }
+ }
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+#endif
+}
+
+/*
+ * XXX This is ugly
+ * We create and bind a socket, then fork off to another
+ * process, which connects to this socket, after which we
+ * exec the wanted program. If something (strange) happens,
+ * the accept() call could block us forever.
+ *
+ * do_pty = 0 Fork/exec inetd style
+ * do_pty = 1 Fork/exec using slirp.telnetd
+ * do_ptr = 2 Fork/exec using pty
+ */
+int
+fork_exec(so, ex, do_pty)
+ struct socket *so;
+ char *ex;
+ int do_pty;
+{
+ int s;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ int opt;
+ int master;
+ char *argv[256];
+#if 0
+ char buff[256];
+#endif
+ /* don't want to clobber the original */
+ char *bptr;
+ char *curarg;
+ int c, i, ret;
+
+ DEBUG_CALL("fork_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ex = %lx", (long)ex);
+ DEBUG_ARG("do_pty = %lx", (long)do_pty);
+
+ if (do_pty == 2) {
+ if (slirp_openpty(&master, &s) == -1) {
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+ } else {
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
+ bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
+ listen(s, 1) < 0) {
+ lprint("Error: inet socket: %s\n", strerror(errno));
+ closesocket(s);
+
+ return 0;
+ }
+ }
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(s);
+ if (do_pty == 2)
+ close(master);
+ return 0;
+
+ case 0:
+ /* Set the DISPLAY */
+ if (do_pty == 2) {
+ (void) close(master);
+#ifdef TIOCSCTTY /* XXXXX */
+ (void) setsid();
+ ioctl(s, TIOCSCTTY, (char *)NULL);
+#endif
+ } else {
+ getsockname(s, (struct sockaddr *)&addr, &addrlen);
+ close(s);
+ /*
+ * Connect to the socket
+ * XXX If any of these fail, we're in trouble!
+ */
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ addr.sin_addr = loopback_addr;
+ do {
+ ret = connect(s, (struct sockaddr *)&addr, addrlen);
+ } while (ret < 0 && errno == EINTR);
+ }
+
+#if 0
+ if (x_port >= 0) {
+#ifdef HAVE_SETENV
+ sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ setenv("DISPLAY", buff, 1);
+#else
+ sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ putenv(buff);
+#endif
+ }
+#endif
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ for (s = 3; s <= 255; s++)
+ close(s);
+
+ i = 0;
+ bptr = strdup(ex); /* No need to free() this */
+ if (do_pty == 1) {
+ /* Setup "slirp.telnetd -x" */
+ argv[i++] = "slirp.telnetd";
+ argv[i++] = "-x";
+ argv[i++] = bptr;
+ } else
+ do {
+ /* Change the string into argv[] */
+ curarg = bptr;
+ while (*bptr != ' ' && *bptr != (char)0)
+ bptr++;
+ c = *bptr;
+ *bptr++ = (char)0;
+ argv[i++] = strdup(curarg);
+ } while (c);
+
+ argv[i] = 0;
+ execvp(argv[0], argv);
+
+ /* Ooops, failed, let's tell the user why */
+ {
+ char buff[256];
+
+ sprintf(buff, "Error: execvp of %s failed: %s\n",
+ argv[0], strerror(errno));
+ write(2, buff, strlen(buff)+1);
+ }
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ if (do_pty == 2) {
+ close(s);
+ so->s = master;
+ } else {
+ /*
+ * XXX this could block us...
+ * XXX Should set a timer here, and if accept() doesn't
+ * return after X seconds, declare it a failure
+ * The only reason this will block forever is if socket()
+ * of connect() fail in the child process
+ */
+ do {
+ so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (so->s < 0 && errno == EINTR);
+ closesocket(s);
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ }
+ fd_nonblock(so->s);
+
+ /* Append the telnet options now */
+ if (so->so_m != 0 && do_pty == 1) {
+ sbappend(so, so->so_m);
+ so->so_m = 0;
+ }
+
+ return 1;
+ }
+}
+#endif
+
+#ifndef HAVE_STRDUP
+char *
+strdup(str)
+ const char *str;
+{
+ char *bptr;
+
+ bptr = (char *)malloc(strlen(str)+1);
+ strcpy(bptr, str);
+
+ return bptr;
+}
+#endif
+
+#if 0
+void
+snooze_hup(num)
+ int num;
+{
+ int s, ret;
+#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un sock_un;
+#endif
+ struct sockaddr_in sock_in;
+ char buff[256];
+
+ ret = -1;
+ if (slirp_socket_passwd) {
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ slirp_exit(1);
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = slirp_socket_addr;
+ sock_in.sin_port = htons(slirp_socket_port);
+ if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0)
+ slirp_exit(1); /* just exit...*/
+ sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit);
+ write(s, buff, strlen(buff)+1);
+ }
+#ifndef NO_UNIX_SOCKETS
+ else {
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ slirp_exit(1);
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, socket_path);
+ if (connect(s, (struct sockaddr *)&sock_un,
+ sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0)
+ slirp_exit(1);
+ sprintf(buff, "kill none:%d", slirp_socket_unit);
+ write(s, buff, strlen(buff)+1);
+ }
+#endif
+ slirp_exit(0);
+}
+
+
+void
+snooze()
+{
+ sigset_t s;
+ int i;
+
+ /* Don't need our data anymore */
+ /* XXX This makes SunOS barf */
+/* brk(0); */
+
+ /* Close all fd's */
+ for (i = 255; i >= 0; i--)
+ close(i);
+
+ signal(SIGQUIT, slirp_exit);
+ signal(SIGHUP, snooze_hup);
+ sigemptyset(&s);
+
+ /* Wait for any signal */
+ sigsuspend(&s);
+
+ /* Just in case ... */
+ exit(255);
+}
+
+void
+relay(s)
+ int s;
+{
+ char buf[8192];
+ int n;
+ fd_set readfds;
+ struct ttys *ttyp;
+
+ /* Don't need our data anymore */
+ /* XXX This makes SunOS barf */
+/* brk(0); */
+
+ signal(SIGQUIT, slirp_exit);
+ signal(SIGHUP, slirp_exit);
+ signal(SIGINT, slirp_exit);
+ signal(SIGTERM, slirp_exit);
+
+ /* Fudge to get term_raw and term_restore to work */
+ if (NULL == (ttyp = tty_attach (0, slirp_tty))) {
+ lprint ("Error: tty_attach failed in misc.c:relay()\r\n");
+ slirp_exit (1);
+ }
+ ttyp->fd = 0;
+ ttyp->flags |= TTY_CTTY;
+ term_raw(ttyp);
+
+ while (1) {
+ FD_ZERO(&readfds);
+
+ FD_SET(0, &readfds);
+ FD_SET(s, &readfds);
+
+ n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
+
+ if (n <= 0)
+ slirp_exit(0);
+
+ if (FD_ISSET(0, &readfds)) {
+ n = read(0, buf, 8192);
+ if (n <= 0)
+ slirp_exit(0);
+ n = writen(s, buf, n);
+ if (n <= 0)
+ slirp_exit(0);
+ }
+
+ if (FD_ISSET(s, &readfds)) {
+ n = read(s, buf, 8192);
+ if (n <= 0)
+ slirp_exit(0);
+ n = writen(0, buf, n);
+ if (n <= 0)
+ slirp_exit(0);
+ }
+ }
+
+ /* Just in case.... */
+ exit(1);
+}
+#endif
+
+int (*lprint_print) _P((void *, const char *, va_list));
+char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+
+void
+#ifdef __STDC__
+lprint(const char *format, ...)
+#else
+lprint(va_alist) va_dcl
+#endif
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, format);
+#else
+ char *format;
+ va_start(args);
+ format = va_arg(args, char *);
+#endif
+#if 0
+ /* If we're printing to an sbuf, make sure there's enough room */
+ /* XXX +100? */
+ if (lprint_sb) {
+ if ((lprint_ptr - lprint_sb->sb_wptr) >=
+ (lprint_sb->sb_datalen - (strlen(format) + 100))) {
+ int deltaw = lprint_sb->sb_wptr - lprint_sb->sb_data;
+ int deltar = lprint_sb->sb_rptr - lprint_sb->sb_data;
+ int deltap = lprint_ptr - lprint_sb->sb_data;
+
+ lprint_sb->sb_data = (char *)realloc(lprint_sb->sb_data,
+ lprint_sb->sb_datalen + TCP_SNDSPACE);
+
+ /* Adjust all values */
+ lprint_sb->sb_wptr = lprint_sb->sb_data + deltaw;
+ lprint_sb->sb_rptr = lprint_sb->sb_data + deltar;
+ lprint_ptr = lprint_sb->sb_data + deltap;
+
+ lprint_sb->sb_datalen += TCP_SNDSPACE;
+ }
+ }
+#endif
+ if (lprint_print)
+ lprint_ptr += (*lprint_print)(*lprint_arg, format, args);
+
+ /* Check if they want output to be logged to file as well */
+ if (lfd) {
+ /*
+ * Remove \r's
+ * otherwise you'll get ^M all over the file
+ */
+ int len = strlen(format);
+ char *bptr1, *bptr2;
+
+ bptr1 = bptr2 = strdup(format);
+
+ while (len--) {
+ if (*bptr1 == '\r')
+ memcpy(bptr1, bptr1+1, len+1);
+ else
+ bptr1++;
+ }
+ vfprintf(lfd, bptr2, args);
+ free(bptr2);
+ }
+ va_end(args);
+}
+
+void
+add_emu(buff)
+ char *buff;
+{
+ u_int lport, fport;
+ u_int8_t tos = 0, emu = 0;
+ char buff1[256], buff2[256], buff4[128];
+ char *buff3 = buff4;
+ struct emu_t *emup;
+ struct socket *so;
+
+ if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) {
+ lprint("Error: Bad arguments\r\n");
+ return;
+ }
+
+ if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) {
+ lport = 0;
+ if (sscanf(buff1, "%d", &fport) != 1) {
+ lprint("Error: Bad first argument\r\n");
+ return;
+ }
+ }
+
+ if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) {
+ buff3 = 0;
+ if (sscanf(buff2, "%256s", buff1) != 1) {
+ lprint("Error: Bad second argument\r\n");
+ return;
+ }
+ }
+
+ if (buff3) {
+ if (strcmp(buff3, "lowdelay") == 0)
+ tos = IPTOS_LOWDELAY;
+ else if (strcmp(buff3, "throughput") == 0)
+ tos = IPTOS_THROUGHPUT;
+ else {
+ lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n");
+ return;
+ }
+ }
+
+ if (strcmp(buff1, "ftp") == 0)
+ emu = EMU_FTP;
+ else if (strcmp(buff1, "irc") == 0)
+ emu = EMU_IRC;
+ else if (strcmp(buff1, "none") == 0)
+ emu = EMU_NONE; /* ie: no emulation */
+ else {
+ lprint("Error: Unknown service\r\n");
+ return;
+ }
+
+ /* First, check that it isn't already emulated */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if (emup->lport == lport && emup->fport == fport) {
+ lprint("Error: port already emulated\r\n");
+ return;
+ }
+ }
+
+ /* link it */
+ emup = (struct emu_t *)malloc(sizeof (struct emu_t));
+ emup->lport = (u_int16_t)lport;
+ emup->fport = (u_int16_t)fport;
+ emup->tos = tos;
+ emup->emu = emu;
+ emup->next = tcpemu;
+ tcpemu = emup;
+
+ /* And finally, mark all current sessions, if any, as being emulated */
+ for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+ if ((lport && lport == ntohs(so->so_lport)) ||
+ (fport && fport == ntohs(so->so_fport))) {
+ if (emu)
+ so->so_emu = emu;
+ if (tos)
+ so->so_iptos = tos;
+ }
+ }
+
+ lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport);
+}
+
+#ifdef BAD_SPRINTF
+
+#undef vsprintf
+#undef sprintf
+
+/*
+ * Some BSD-derived systems have a sprintf which returns char *
+ */
+
+int
+vsprintf_len(string, format, args)
+ char *string;
+ const char *format;
+ va_list args;
+{
+ vsprintf(string, format, args);
+ return strlen(string);
+}
+
+int
+#ifdef __STDC__
+sprintf_len(char *string, const char *format, ...)
+#else
+sprintf_len(va_alist) va_dcl
+#endif
+{
+ va_list args;
+#ifdef __STDC__
+ va_start(args, format);
+#else
+ char *string;
+ char *format;
+ va_start(args);
+ string = va_arg(args, char *);
+ format = va_arg(args, char *);
+#endif
+ vsprintf(string, format, args);
+ return strlen(string);
+}
+
+#endif
+
+void
+u_sleep(usec)
+ int usec;
+{
+ struct timeval t;
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+
+ t.tv_sec = 0;
+ t.tv_usec = usec * 1000;
+
+ select(0, &fdset, &fdset, &fdset, &t);
+}
+
+/*
+ * Set fd blocking and non-blocking
+ */
+
+void
+fd_nonblock(fd)
+ int fd;
+{
+#ifdef FIONBIO
+ int opt = 1;
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+void
+fd_block(fd)
+ int fd;
+{
+#ifdef FIONBIO
+ int opt = 0;
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt &= ~O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+
+#if 0
+/*
+ * invoke RSH
+ */
+int
+rsh_exec(so,ns, user, host, args)
+ struct socket *so;
+ struct socket *ns;
+ char *user;
+ char *host;
+ char *args;
+{
+ int fd[2];
+ int fd0[2];
+ int s;
+ char buff[256];
+
+ DEBUG_CALL("rsh_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (pipe(fd)<0) {
+ lprint("Error: pipe failed: %s\n", strerror(errno));
+ return 0;
+ }
+/* #ifdef HAVE_SOCKETPAIR */
+#if 1
+ if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) {
+ close(fd[0]);
+ close(fd[1]);
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+#else
+ if (slirp_openpty(&fd0[0], &fd0[1]) == -1) {
+ close(fd[0]);
+ close(fd[1]);
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+#endif
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(fd[0]);
+ close(fd[1]);
+ close(fd0[0]);
+ close(fd0[1]);
+ return 0;
+
+ case 0:
+ close(fd[0]);
+ close(fd0[0]);
+
+ /* Set the DISPLAY */
+ if (x_port >= 0) {
+#ifdef HAVE_SETENV
+ sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ setenv("DISPLAY", buff, 1);
+#else
+ sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ putenv(buff);
+#endif
+ }
+
+ dup2(fd0[1], 0);
+ dup2(fd0[1], 1);
+ dup2(fd[1], 2);
+ for (s = 3; s <= 255; s++)
+ close(s);
+
+ execlp("rsh","rsh","-l", user, host, args, NULL);
+
+ /* Ooops, failed, let's tell the user why */
+
+ sprintf(buff, "Error: execlp of %s failed: %s\n",
+ "rsh", strerror(errno));
+ write(2, buff, strlen(buff)+1);
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ close(fd[1]);
+ close(fd0[1]);
+ ns->s=fd[0];
+ so->s=fd0[0];
+
+ return 1;
+ }
+}
+#endif
diff --git a/slirp/misc.h b/slirp/misc.h
new file mode 100644
index 0000000..8e6a606
--- /dev/null
+++ b/slirp/misc.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+struct ex_list {
+ int ex_pty; /* Do we want a pty? */
+ int ex_addr; /* The last byte of the address */
+ int ex_fport; /* Port to telnet to */
+ char *ex_exec; /* Command line of what to exec */
+ struct ex_list *ex_next;
+};
+
+extern struct ex_list *exec_list;
+extern u_int curtime, time_fasttimo, last_slowtimo, detach_time, detach_wait;
+
+extern int (*lprint_print) _P((void *, const char *, va_list));
+extern char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+extern struct sbuf *lprint_sb;
+
+#ifndef HAVE_STRDUP
+char *strdup _P((const char *));
+#endif
+
+void do_wait _P((int));
+
+#define EMU_NONE 0x0
+
+/* TCP emulations */
+#define EMU_CTL 0x1
+#define EMU_FTP 0x2
+#define EMU_KSH 0x3
+#define EMU_IRC 0x4
+#define EMU_REALAUDIO 0x5
+#define EMU_RLOGIN 0x6
+#define EMU_IDENT 0x7
+#define EMU_RSH 0x8
+
+#define EMU_NOCONNECT 0x10 /* Don't connect */
+
+/* UDP emulations */
+#define EMU_TALK 0x1
+#define EMU_NTALK 0x2
+#define EMU_CUSEEME 0x3
+
+struct tos_t {
+ u_int16_t lport;
+ u_int16_t fport;
+ u_int8_t tos;
+ u_int8_t emu;
+};
+
+struct emu_t {
+ u_int16_t lport;
+ u_int16_t fport;
+ u_int8_t tos;
+ u_int8_t emu;
+ struct emu_t *next;
+};
+
+extern struct emu_t *tcpemu;
+
+extern int x_port, x_server, x_display;
+
+int show_x _P((char *, struct socket *));
+void redir_x _P((u_int32_t, int, int, int));
+void getouraddr _P((void));
+inline void slirp_insque _P((void *, void *));
+inline void slirp_remque _P((void *));
+int add_exec _P((struct ex_list **, int, char *, int, int));
+int slirp_openpty _P((int *, int *));
+int fork_exec _P((struct socket *, char *, int));
+void snooze_hup _P((int));
+void snooze _P((void));
+void relay _P((int));
+void add_emu _P((char *));
+void u_sleep _P((int));
+void fd_nonblock _P((int));
+void fd_block _P((int));
+int rsh_exec _P((struct socket *, struct socket *, char *, char *, char *));
+
+#endif
diff --git a/slirp/sbuf.c b/slirp/sbuf.c
new file mode 100644
index 0000000..d6726c9
--- /dev/null
+++ b/slirp/sbuf.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/* Done as a macro in socket.h */
+/* int
+ * sbspace(struct sockbuff *sb)
+ * {
+ * return SB_DATALEN - sb->sb_cc;
+ * }
+ */
+
+void
+sbfree(sb)
+ struct sbuf *sb;
+{
+ free(sb->sb_data);
+}
+
+void
+sbdrop(sb, num)
+ struct sbuf *sb;
+ int num;
+{
+ /*
+ * We can only drop how much we have
+ * This should never succeed
+ */
+ if(num > sb->sb_cc)
+ num = sb->sb_cc;
+ sb->sb_cc -= num;
+ sb->sb_rptr += num;
+ if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_rptr -= sb->sb_datalen;
+
+}
+
+void
+sbreserve(sb, size)
+ struct sbuf *sb;
+ int size;
+{
+ if (sb->sb_data) {
+ /* Already alloced, realloc if necessary */
+ if (sb->sb_datalen != size) {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+ } else {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbappend(so, m)
+ struct socket *so;
+ struct mbuf *m;
+{
+ int ret = 0;
+
+ DEBUG_CALL("sbappend");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m->m_len = %d", m->m_len);
+
+ /* Shouldn't happen, but... e.g. foreign host closes connection */
+ if (m->m_len <= 0) {
+ m_free(m);
+ return;
+ }
+
+ /*
+ * If there is urgent data, call sosendoob
+ * if not all was sent, sowrite will take care of the rest
+ * (The rest of this function is just an optimisation)
+ */
+ if (so->so_urgc) {
+ sbappendsb(&so->so_rcv, m);
+ m_free(m);
+ sosendoob(so);
+ return;
+ }
+
+ /*
+ * We only write if there's nothing in the buffer,
+ * ottherwise it'll arrive out of order, and hence corrupt
+ */
+ if (!so->so_rcv.sb_cc)
+ ret = send(so->s, m->m_data, m->m_len, 0);
+
+ if (ret <= 0) {
+ /*
+ * Nothing was written
+ * It's possible that the socket has closed, but
+ * we don't need to check because if it has closed,
+ * it will be detected in the normal way by soread()
+ */
+ sbappendsb(&so->so_rcv, m);
+ } else if (ret != m->m_len) {
+ /*
+ * Something was written, but not everything..
+ * sbappendsb the rest
+ */
+ m->m_len -= ret;
+ m->m_data += ret;
+ sbappendsb(&so->so_rcv, m);
+ } /* else */
+ /* Whatever happened, we free the mbuf */
+ m_free(m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+void
+sbappendsb(sb, m)
+ struct sbuf *sb;
+ struct mbuf *m;
+{
+ int len, n, nn;
+
+ len = m->m_len;
+
+ if (sb->sb_wptr < sb->sb_rptr) {
+ n = sb->sb_rptr - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ } else {
+ /* Do the right edge first */
+ n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ len -= n;
+ if (len) {
+ /* Now the left edge */
+ nn = sb->sb_rptr - sb->sb_data;
+ if (nn > len) nn = len;
+ memcpy(sb->sb_data,m->m_data+n,nn);
+ n += nn;
+ }
+ }
+
+ sb->sb_cc += n;
+ sb->sb_wptr += n;
+ if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_wptr -= sb->sb_datalen;
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbcopy(sb, off, len, to)
+ struct sbuf *sb;
+ int off;
+ int len;
+ char *to;
+{
+ char *from;
+
+ from = sb->sb_rptr + off;
+ if (from >= sb->sb_data + sb->sb_datalen)
+ from -= sb->sb_datalen;
+
+ if (from < sb->sb_wptr) {
+ if (len > sb->sb_cc) len = sb->sb_cc;
+ memcpy(to,from,len);
+ } else {
+ /* re-use off */
+ off = (sb->sb_data + sb->sb_datalen) - from;
+ if (off > len) off = len;
+ memcpy(to,from,off);
+ len -= off;
+ if (len)
+ memcpy(to+off,sb->sb_data,len);
+ }
+}
+
diff --git a/slirp/sbuf.h b/slirp/sbuf.h
new file mode 100644
index 0000000..161e0bb
--- /dev/null
+++ b/slirp/sbuf.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+#define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
+#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+
+struct sbuf {
+ u_int sb_cc; /* actual chars in buffer */
+ u_int sb_datalen; /* Length of data */
+ char *sb_wptr; /* write pointer. points to where the next
+ * bytes should be written in the sbuf */
+ char *sb_rptr; /* read pointer. points to where the next
+ * byte should be read from the sbuf */
+ char *sb_data; /* Actual data */
+};
+
+void sbfree _P((struct sbuf *));
+void sbdrop _P((struct sbuf *, int));
+void sbreserve _P((struct sbuf *, int));
+void sbappend _P((struct socket *, struct mbuf *));
+void sbappendsb _P((struct sbuf *, struct mbuf *));
+void sbcopy _P((struct sbuf *, int, int, char *));
+
+#endif
diff --git a/slirp/slirp.c b/slirp/slirp.c
new file mode 100644
index 0000000..6ba753e
--- /dev/null
+++ b/slirp/slirp.c
@@ -0,0 +1,665 @@
+#include "slirp.h"
+
+/* host address */
+struct in_addr our_addr;
+/* host dns address */
+struct in_addr dns_addr;
+/* host loopback address */
+struct in_addr loopback_addr;
+
+/* address for slirp virtual addresses */
+struct in_addr special_addr;
+/* virtual address alias for host */
+struct in_addr alias_addr;
+
+const uint8_t special_ethaddr[6] = {
+ 0x52, 0x54, 0x00, 0x12, 0x35, 0x00
+};
+
+uint8_t client_ethaddr[6];
+
+int do_slowtimo;
+int link_up;
+struct timeval tt;
+FILE *lfd;
+struct ex_list *exec_list;
+
+/* XXX: suppress those select globals */
+fd_set *global_readfds, *global_writefds, *global_xfds;
+
+char slirp_hostname[33];
+
+#ifdef _WIN32
+
+static int get_dns_addr(struct in_addr *pdns_addr)
+{
+ FIXED_INFO *FixedInfo=NULL;
+ ULONG BufLen;
+ DWORD ret;
+ IP_ADDR_STRING *pIPAddr;
+ struct in_addr tmp_addr;
+
+ FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
+ BufLen = sizeof(FIXED_INFO);
+
+ if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ FixedInfo = GlobalAlloc(GPTR, BufLen);
+ }
+
+ if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
+ printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return -1;
+ }
+
+ pIPAddr = &(FixedInfo->DnsServerList);
+ inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
+ *pdns_addr = tmp_addr;
+#if 0
+ printf( "DNS Servers:\n" );
+ printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String );
+
+ pIPAddr = FixedInfo -> DnsServerList.Next;
+ while ( pIPAddr ) {
+ printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String );
+ pIPAddr = pIPAddr ->Next;
+ }
+#endif
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return 0;
+}
+
+#else
+
+static int get_dns_addr(struct in_addr *pdns_addr)
+{
+ char buff[512];
+ char buff2[256];
+ FILE *f;
+ int found = 0;
+ struct in_addr tmp_addr;
+
+ f = fopen("/etc/resolv.conf", "r");
+ if (!f)
+ return -1;
+
+ lprint("IP address of your DNS(s): ");
+ while (fgets(buff, 512, f) != NULL) {
+ if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
+ if (!inet_aton(buff2, &tmp_addr))
+ continue;
+ if (tmp_addr.s_addr == loopback_addr.s_addr)
+ tmp_addr = our_addr;
+ /* If it's the first one, set it to dns_addr */
+ if (!found)
+ *pdns_addr = tmp_addr;
+ else
+ lprint(", ");
+ if (++found > 3) {
+ lprint("(more)");
+ break;
+ } else
+ lprint("%s", inet_ntoa(tmp_addr));
+ }
+ }
+ fclose(f);
+ if (!found)
+ return -1;
+ return 0;
+}
+
+#endif
+
+#ifdef _WIN32
+void slirp_cleanup(void)
+{
+ WSACleanup();
+}
+#endif
+
+void slirp_init(void)
+{
+ // debug_init("/tmp/slirp.log", DEBUG_DEFAULT);
+
+#ifdef _WIN32
+ {
+ WSADATA Data;
+ WSAStartup(MAKEWORD(2,0), &Data);
+ atexit(slirp_cleanup);
+ }
+#endif
+
+ link_up = 1;
+
+ if_init();
+ ip_init();
+
+ /* Initialise mbufs *after* setting the MTU */
+ m_init();
+
+ /* set default addresses */
+ inet_aton("127.0.0.1", &loopback_addr);
+
+ if (get_dns_addr(&dns_addr) < 0) {
+ dns_addr = loopback_addr;
+ fprintf (stderr, "Warning: No DNS servers found\n");
+ }
+
+ inet_aton(CTL_SPECIAL, &special_addr);
+ alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS);
+ getouraddr();
+}
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
+
+/*
+ * curtime kept to an accuracy of 1ms
+ */
+#ifdef _WIN32
+static void updtime(void)
+{
+ struct _timeb tb;
+
+ _ftime(&tb);
+ curtime = (u_int)tb.time * (u_int)1000;
+ curtime += (u_int)tb.millitm;
+}
+#else
+static void updtime(void)
+{
+ gettimeofday(&tt, 0);
+
+ curtime = (u_int)tt.tv_sec * (u_int)1000;
+ curtime += (u_int)tt.tv_usec / (u_int)1000;
+
+ if ((tt.tv_usec % 1000) >= 500)
+ curtime++;
+}
+#endif
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ struct socket *so, *so_next;
+ struct timeval timeout;
+ int nfds;
+ int tmp_time;
+
+ /* fail safe */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+
+ nfds = *pnfds;
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
+ if (link_up) {
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ do_slowtimo = ((tcb.so_next != &tcb) ||
+ ((struct ipasfrag *)&ipq != (struct ipasfrag *)ipq.next));
+
+ for (so = tcb.so_next; so != &tcb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+ FD_SET(so->s, readfds);
+ FD_SET(so->s, xfds);
+ UPD_NFDS(so->s);
+ }
+ }
+
+ /*
+ * UDP sockets
+ */
+ for (so = udb.so_next; so != &udb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ udp_detach(so);
+ continue;
+ } else
+ do_slowtimo = 1; /* Let socket expire */
+ }
+
+ /*
+ * When UDP packets are received from over the
+ * link, they're sendto()'d straight away, so
+ * no need for setting for writing
+ * Limit the number of packets queued by this session
+ * to 4. Note that even though we try and limit this
+ * to 4 packets, the session could have more queued
+ * if the packets needed to be fragmented
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ }
+ }
+ }
+
+ /*
+ * Setup timeout to use minimum CPU usage, especially when idle
+ */
+
+ /*
+ * First, see the timeout needed by *timo
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = -1;
+ /*
+ * If a slowtimo is needed, set timeout to 500ms from the last
+ * slow timeout. If a fast timeout is needed, set timeout within
+ * 200ms of when it was requested.
+ */
+ if (do_slowtimo) {
+ /* XXX + 10000 because some select()'s aren't that accurate */
+ timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
+ if (timeout.tv_usec < 0)
+ timeout.tv_usec = 0;
+ else if (timeout.tv_usec > 510000)
+ timeout.tv_usec = 510000;
+
+ /* Can only fasttimo if we also slowtimo */
+ if (time_fasttimo) {
+ tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
+ if (tmp_time < 0)
+ tmp_time = 0;
+
+ /* Choose the smallest of the 2 */
+ if (tmp_time < timeout.tv_usec)
+ timeout.tv_usec = (u_int)tmp_time;
+ }
+ }
+ *pnfds = nfds;
+}
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ struct socket *so, *so_next;
+ int ret;
+
+ global_readfds = readfds;
+ global_writefds = writefds;
+ global_xfds = xfds;
+
+ /* Update time */
+ updtime();
+
+ /*
+ * See if anything has timed out
+ */
+ if (link_up) {
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+ tcp_fasttimo();
+ time_fasttimo = 0;
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+ ip_slowtimo();
+ tcp_slowtimo();
+ last_slowtimo = curtime;
+ }
+ }
+
+ /*
+ * Check sockets
+ */
+ if (link_up) {
+ /*
+ * Check TCP sockets
+ */
+ for (so = tcb.so_next; so != &tcb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * FD_ISSET is meaningless on these sockets
+ * (and they can crash the program)
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for readfds below if this succeeds
+ */
+ if (FD_ISSET(so->s, xfds))
+ sorecvoob(so);
+ /*
+ * Check sockets for reading
+ */
+ else if (FD_ISSET(so->s, readfds)) {
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ tcp_connect(so);
+ continue;
+ } /* else */
+ ret = soread(so);
+
+ /* Output it if we read something */
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if (FD_ISSET(so->s, writefds)) {
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ ret = send(so->s, &ret, 0, 0);
+ if (ret < 0) {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+ /*
+ * Continue tcp_input
+ */
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ } else
+ ret = sowrite(so);
+ /*
+ * XXXXX If we wrote something (a lot), there
+ * could be a need for a window update.
+ * In the worst case, the remote will send
+ * a window probe to get things going again
+ */
+ }
+
+ /*
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
+ */
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING) {
+ ret = recv(so->s, (char *)&ret, 0,0);
+
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue; /* Still connecting, continue */
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ } else {
+ ret = send(so->s, &ret, 0,0);
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ } else
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ }
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
+ } /* SS_ISFCONNECTING */
+#endif
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ for (so = udb.so_next; so != &udb; so = so_next) {
+ so_next = so->so_next;
+
+ if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+ sorecvfrom(so);
+ }
+ }
+ }
+
+ /*
+ * See if we can start outputting
+ */
+ if (if_queued && link_up)
+ if_start();
+
+ /* clear global file descriptor sets.
+ * these reside on the stack in vl.c
+ * so they're unusable if we're not in
+ * slirp_select_fill or slirp_select_poll.
+ */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+}
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+};
+
+void arp_input(const uint8_t *pkt, int pkt_len)
+{
+ struct ethhdr *eh = (struct ethhdr *)pkt;
+ struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
+ uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
+ struct ethhdr *reh = (struct ethhdr *)arp_reply;
+ struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
+ int ar_op;
+ struct ex_list *ex_ptr;
+
+ ar_op = ntohs(ah->ar_op);
+ switch(ar_op) {
+ case ARPOP_REQUEST:
+ if (!memcmp(ah->ar_tip, &special_addr, 3)) {
+ if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS)
+ goto arp_ok;
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_addr == ah->ar_tip[3])
+ goto arp_ok;
+ }
+ return;
+ arp_ok:
+ /* XXX: make an ARP request to have the client address */
+ memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
+
+ /* ARP request for alias/dns mac address */
+ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
+ reh->h_source[5] = ah->ar_tip[3];
+ reh->h_proto = htons(ETH_P_ARP);
+
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REPLY);
+ memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
+ memcpy(rah->ar_sip, ah->ar_tip, 4);
+ memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
+ memcpy(rah->ar_tip, ah->ar_sip, 4);
+ slirp_output(arp_reply, sizeof(arp_reply));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void slirp_input(const uint8_t *pkt, int pkt_len)
+{
+ struct mbuf *m;
+ int proto;
+
+ if (pkt_len < ETH_HLEN)
+ return;
+
+ proto = ntohs(*(uint16_t *)(pkt + 12));
+ switch(proto) {
+ case ETH_P_ARP:
+ arp_input(pkt, pkt_len);
+ break;
+ case ETH_P_IP:
+ m = m_get();
+ if (!m)
+ return;
+ /* Note: we add to align the IP header */
+ m->m_len = pkt_len + 2;
+ memcpy(m->m_data + 2, pkt, pkt_len);
+
+ m->m_data += 2 + ETH_HLEN;
+ m->m_len -= 2 + ETH_HLEN;
+
+ ip_input(m);
+ break;
+ default:
+ break;
+ }
+}
+
+/* output the IP packet to the ethernet device */
+void if_encap(const uint8_t *ip_data, int ip_data_len)
+{
+ uint8_t buf[1600];
+ struct ethhdr *eh = (struct ethhdr *)buf;
+
+ if (ip_data_len + ETH_HLEN > sizeof(buf))
+ return;
+
+ memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
+ memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
+ /* XXX: not correct */
+ eh->h_source[5] = CTL_ALIAS;
+ eh->h_proto = htons(ETH_P_IP);
+ memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
+ slirp_output(buf, ip_data_len + ETH_HLEN);
+}
+
+int slirp_redir(int is_udp, int host_port,
+ struct in_addr guest_addr, int guest_port)
+{
+ if (is_udp) {
+ if (!udp_listen(htons(host_port), guest_addr.s_addr,
+ htons(guest_port), 0))
+ return -1;
+ } else {
+ if (!solisten(htons(host_port), guest_addr.s_addr,
+ htons(guest_port), 0))
+ return -1;
+ }
+ return 0;
+}
+
+int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
+ int guest_port)
+{
+ return add_exec(&exec_list, do_pty, (char *)args,
+ addr_low_byte, htons(guest_port));
+}
diff --git a/slirp/slirp.h b/slirp/slirp.h
new file mode 100644
index 0000000..1ff68cb
--- /dev/null
+++ b/slirp/slirp.h
@@ -0,0 +1,339 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define CONFIG_QEMU
+
+#define DEBUG 1
+
+#ifndef CONFIG_QEMU
+#include "version.h"
+#endif
+#include "config.h"
+#include "slirp_config.h"
+
+#ifdef _WIN32
+# include <inttypes.h>
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+typedef char *caddr_t;
+
+# include <windows.h>
+# include <winsock2.h>
+# include <sys/timeb.h>
+# include <iphlpapi.h>
+
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define EINPROGRESS WSAEINPROGRESS
+# define ENOTCONN WSAENOTCONN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# define ENETUNREACH WSAENETUNREACH
+# define ECONNREFUSED WSAECONNREFUSED
+#else
+# define ioctlsocket ioctl
+# define closesocket(s) close(s)
+# define O_BINARY 0
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#include <sys/time.h>
+
+#ifdef NEED_TYPEDEFS
+typedef char int8_t;
+typedef unsigned char u_int8_t;
+
+# if SIZEOF_SHORT == 2
+ typedef short int16_t;
+ typedef unsigned short u_int16_t;
+# else
+# if SIZEOF_INT == 2
+ typedef int int16_t;
+ typedef unsigned int u_int16_t;
+# else
+ #error Cannot find a type with sizeof() == 2
+# endif
+# endif
+
+# if SIZEOF_SHORT == 4
+ typedef short int32_t;
+ typedef unsigned short u_int32_t;
+# else
+# if SIZEOF_INT == 4
+ typedef int int32_t;
+ typedef unsigned int u_int32_t;
+# else
+ #error Cannot find a type with sizeof() == 4
+# endif
+# endif
+#endif /* NEED_TYPEDEFS */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_MEMMOVE
+#define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#ifndef _P
+#ifndef NO_PROTOTYPES
+# define _P(x) x
+#else
+# define _P(x) ()
+#endif
+#endif
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef GETTIMEOFDAY_ONE_ARG
+#define gettimeofday(x, y) gettimeofday(x)
+#endif
+
+/* Systems lacking strdup() definition in <string.h>. */
+#if defined(ultrix)
+char *strdup _P((const char *));
+#endif
+
+/* Systems lacking malloc() definition in <stdlib.h>. */
+#if defined(ultrix) || defined(hcx)
+void *malloc _P((size_t arg));
+void free _P((void *ptr));
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton _P((const char *cp, struct in_addr *ia));
+#endif
+
+#include <fcntl.h>
+#ifndef NO_UNIX_SOCKETS
+#include <sys/un.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef USE_PPP
+#include <ppp/slirppp.h>
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <sys/stat.h>
+
+/* Avoid conflicting with the libc insque() and remque(), which
+ have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#endif
+
+#include "debug.h"
+
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "icmp_var.h"
+#include "mbuf.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "if.h"
+#include "main.h"
+#include "misc.h"
+#include "ctl.h"
+#ifdef USE_PPP
+#include "ppp/pppd.h"
+#include "ppp/ppp.h"
+#endif
+
+#include "bootp.h"
+#include "tftp.h"
+#include "libslirp.h"
+
+extern struct ttys *ttys_unit[MAX_INTERFACES];
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef FULL_BOLT
+void if_start _P((void));
+#else
+void if_start _P((struct ttys *));
+#endif
+
+#ifdef BAD_SPRINTF
+# define vsprintf vsprintf_len
+# define sprintf sprintf_len
+ extern int vsprintf_len _P((char *, const char *, va_list));
+ extern int sprintf_len _P((char *, const char *, ...));
+#endif
+
+#ifdef DECLARE_SPRINTF
+# ifndef BAD_SPRINTF
+ extern int vsprintf _P((char *, const char *, va_list));
+# endif
+ extern int vfprintf _P((FILE *, const char *, va_list));
+#endif
+
+#ifndef HAVE_STRERROR
+ extern char *strerror _P((int error));
+#endif
+
+#ifndef HAVE_INDEX
+ char *index _P((const char *, int));
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid _P((void));
+#endif
+
+void lprint _P((const char *, ...));
+
+extern int do_echo;
+
+#if SIZEOF_CHAR_P == 4
+# define insque_32 insque
+# define remque_32 remque
+#else
+ inline void insque_32 _P((void *, void *));
+ inline void remque_32 _P((void *));
+#endif
+
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+
+#define DEFAULT_BAUD 115200
+
+/* cksum.c */
+int cksum(struct mbuf *m, int len);
+
+/* if.c */
+void if_init _P((void));
+void if_output _P((struct socket *, struct mbuf *));
+
+/* ip_input.c */
+void ip_init _P((void));
+void ip_input _P((struct mbuf *));
+struct ip * ip_reass _P((register struct ipasfrag *, register struct ipq *));
+void ip_freef _P((struct ipq *));
+void ip_enq _P((register struct ipasfrag *, register struct ipasfrag *));
+void ip_deq _P((register struct ipasfrag *));
+void ip_slowtimo _P((void));
+void ip_stripoptions _P((register struct mbuf *, struct mbuf *));
+
+/* ip_output.c */
+int ip_output _P((struct socket *, struct mbuf *));
+
+/* tcp_input.c */
+int tcp_reass _P((register struct tcpcb *, register struct tcpiphdr *, struct mbuf *));
+void tcp_input _P((register struct mbuf *, int, struct socket *));
+void tcp_dooptions _P((struct tcpcb *, u_char *, int, struct tcpiphdr *));
+void tcp_xmit_timer _P((register struct tcpcb *, int));
+int tcp_mss _P((register struct tcpcb *, u_int));
+
+/* tcp_output.c */
+int tcp_output _P((register struct tcpcb *));
+void tcp_setpersist _P((register struct tcpcb *));
+
+/* tcp_subr.c */
+void tcp_init _P((void));
+void tcp_template _P((struct tcpcb *));
+void tcp_respond _P((struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int));
+struct tcpcb * tcp_newtcpcb _P((struct socket *));
+struct tcpcb * tcp_close _P((register struct tcpcb *));
+void tcp_drain _P((void));
+void tcp_sockclosed _P((struct tcpcb *));
+int tcp_fconnect _P((struct socket *));
+void tcp_connect _P((struct socket *));
+int tcp_attach _P((struct socket *));
+u_int8_t tcp_tos _P((struct socket *));
+int tcp_emu _P((struct socket *, struct mbuf *));
+int tcp_ctl _P((struct socket *));
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
+
+#ifdef USE_PPP
+#define MIN_MRU MINMRU
+#define MAX_MRU MAXMRU
+#else
+#define MIN_MRU 128
+#define MAX_MRU 16384
+#endif
+
+#ifndef _WIN32
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef _WIN32
+#undef errno
+#define errno (WSAGetLastError())
+#endif
+
+#endif
diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h
new file mode 100644
index 0000000..e7e95dd
--- /dev/null
+++ b/slirp/slirp_config.h
@@ -0,0 +1,206 @@
+/*
+ * User definable configuration options
+ */
+
+/* Undefine if you don't want talk emulation */
+#undef EMULATE_TALK
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/* Define this if your compiler doesn't like prototypes */
+#ifndef __STDC__
+#define NO_PROTOTYPES
+#endif
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+/* Ignore this */
+#undef DUMMY_PPP
+
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef _WIN32
+#define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define if you have strerror */
+#define HAVE_STRERROR
+
+/* Define if you have strdup() */
+#define HAVE_STRDUP
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+//#undef WORDS_BIGENDIAN
+
+/* Define if your sprintf returns char * instead of int */
+#undef BAD_SPRINTF
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef _WIN32
+#define DECLARE_IOVEC
+#endif
+
+/* Define if a declaration of sprintf/fprintf is needed */
+#undef DECLARE_SPRINTF
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef _WIN32
+#define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef _WIN32
+#define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+#define inline inline
+
+/* Define to whatever your compiler thinks const should be */
+#define const const
+
+/* Define if your compiler doesn't like prototypes */
+#undef NO_PROTOTYPES
+
+/* Define if you don't have u_int32_t etc. typedef'd */
+#undef NEED_TYPEDEFS
+#ifdef __sun__
+#define NEED_TYPEDEFS
+#endif
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have inet_aton */
+#undef HAVE_INET_ATON
+#ifndef _WIN32
+#define HAVE_INET_ATON
+#endif
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#undef HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#undef HAVE_GETHOSTID
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef _WIN32
+#define NO_UNIX_SOCKETS
+#endif
+
+/* Define if gettimeofday only takes one argument */
+#undef GETTIMEOFDAY_ONE_ARG
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
diff --git a/slirp/socket.c b/slirp/socket.c
new file mode 100644
index 0000000..0ae1f87
--- /dev/null
+++ b/slirp/socket.c
@@ -0,0 +1,717 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+#include "ip_icmp.h"
+#include "main.h"
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+
+void
+so_init()
+{
+ /* Nothing yet */
+}
+
+
+struct socket *
+solookup(head, laddr, lport, faddr, fport)
+ struct socket *head;
+ struct in_addr laddr;
+ u_int lport;
+ struct in_addr faddr;
+ u_int fport;
+{
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (so->so_lport == lport &&
+ so->so_laddr.s_addr == laddr.s_addr &&
+ so->so_faddr.s_addr == faddr.s_addr &&
+ so->so_fport == fport)
+ break;
+ }
+
+ if (so == head)
+ return (struct socket *)NULL;
+ return so;
+
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate()
+{
+ struct socket *so;
+
+ so = (struct socket *)malloc(sizeof(struct socket));
+ if(so) {
+ memset(so, 0, sizeof(struct socket));
+ so->so_state = SS_NOFDREF;
+ so->s = -1;
+ }
+ return(so);
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(so)
+ struct socket *so;
+{
+ if (so->so_emu==EMU_RSH && so->extra) {
+ sofree(so->extra);
+ so->extra=NULL;
+ }
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ else if (so == udp_last_so)
+ udp_last_so = &udb;
+
+ m_free(so->so_m);
+
+ if(so->so_next && so->so_prev)
+ remque(so); /* crashes if so is not in a queue */
+
+ free(so);
+}
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(so)
+ struct socket *so;
+{
+ int n, nn, lss, total;
+ struct sbuf *sb = &so->so_snd;
+ int len = sb->sb_datalen - sb->sb_cc;
+ struct iovec iov[2];
+ int mss = so->so_tcpcb->t_maxseg;
+
+ DEBUG_CALL("soread");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+
+ len = sb->sb_datalen - sb->sb_cc;
+
+ iov[0].iov_base = sb->sb_wptr;
+ if (sb->sb_wptr < sb->sb_rptr) {
+ iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+ if(iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ total = iov[0].iov_len + iov[1].iov_len;
+ if (total > mss) {
+ lss = total%mss;
+ if (iov[1].iov_len > lss) {
+ iov[1].iov_len -= lss;
+ n = 2;
+ } else {
+ lss -= iov[1].iov_len;
+ iov[0].iov_len -= lss;
+ n = 1;
+ }
+ } else
+ n = 2;
+ } else {
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ }
+
+#ifdef HAVE_READV
+ nn = readv(so->s, (struct iovec *)iov, n);
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#else
+ nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ if (nn <= 0) {
+ if (nn < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+ else {
+ DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+ }
+
+#ifndef HAVE_READV
+ /*
+ * If there was no error, try and read the second time round
+ * We read again if n = 2 (ie, there's another part of the buffer)
+ * and we read as much as we could in the first read
+ * We don't test for <= 0 this time, because there legitimately
+ * might not be any more data (since the socket is non-blocking),
+ * a close will be detected on next iteration.
+ * A return of -1 wont (shouldn't) happen, since it didn't happen above
+ */
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#endif
+
+ /* Update fields */
+ sb->sb_cc += nn;
+ sb->sb_wptr += nn;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return nn;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(so)
+ struct socket *so;
+{
+ struct tcpcb *tp = sototcpcb(so);
+
+ DEBUG_CALL("sorecvoob");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ /*
+ * We take a guess at how much urgent data has arrived.
+ * In most situations, when urgent data arrives, the next
+ * read() should get all the urgent data. This guess will
+ * be wrong however if more data arrives just after the
+ * urgent data, or the read() doesn't return all the
+ * urgent data.
+ */
+ soread(so);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ tcp_output(tp);
+ tp->t_force = 0;
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(so)
+ struct socket *so;
+{
+ struct sbuf *sb = &so->so_rcv;
+ char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+
+ int n, len;
+
+ DEBUG_CALL("sosendoob");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
+
+ if (so->so_urgc > 2048)
+ so->so_urgc = 2048; /* XXXX */
+
+ if (sb->sb_rptr < sb->sb_wptr) {
+ /* We can send it directly */
+ n = send(so->s, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+ so->so_urgc -= n;
+
+ DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ } else {
+ /*
+ * Since there's no sendv or sendtov like writev,
+ * we must copy all data to a linear buffer then
+ * send it all
+ */
+ len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (len > so->so_urgc) len = so->so_urgc;
+ memcpy(buff, sb->sb_rptr, len);
+ so->so_urgc -= len;
+ if (so->so_urgc) {
+ n = sb->sb_wptr - sb->sb_data;
+ if (n > so->so_urgc) n = so->so_urgc;
+ memcpy((buff + len), sb->sb_data, n);
+ so->so_urgc -= n;
+ len += n;
+ }
+ n = send(so->s, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+ if (n != len)
+ DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
+#endif
+ DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ }
+
+ sb->sb_cc -= n;
+ sb->sb_rptr += n;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(so)
+ struct socket *so;
+{
+ int n,nn;
+ struct sbuf *sb = &so->so_rcv;
+ int len = sb->sb_cc;
+ struct iovec iov[2];
+
+ DEBUG_CALL("sowrite");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_urgc) {
+ sosendoob(so);
+ if (sb->sb_cc == 0)
+ return 0;
+ }
+
+ /*
+ * No need to check if there's something to write,
+ * sowrite wouldn't have been called otherwise
+ */
+
+ len = sb->sb_cc;
+
+ iov[0].iov_base = sb->sb_rptr;
+ if (sb->sb_rptr < sb->sb_wptr) {
+ iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+ if (iov[1].iov_len > len) iov[1].iov_len = len;
+ n = 2;
+ } else
+ n = 1;
+ }
+ /* Check if there's urgent data to send, and if so, send it */
+
+#ifdef HAVE_READV
+ nn = writev(so->s, (const struct iovec *)iov, n);
+
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#else
+ nn = send(so->s, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ /* This should never happen, but people tell me it does *shrug* */
+ if (nn < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+
+ if (nn <= 0) {
+ DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
+ so->so_state, errno));
+ sofcantsendmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+
+#ifndef HAVE_READV
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = send(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#endif
+
+ /* Update sbuf */
+ sb->sb_cc -= nn;
+ sb->sb_rptr += nn;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ /*
+ * If in DRAIN mode, and there's no more data, set
+ * it CANTSENDMORE
+ */
+ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+ sofcantsendmore(so);
+
+ return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(so)
+ struct socket *so;
+{
+ struct sockaddr_in addr;
+ int addrlen = sizeof(struct sockaddr_in);
+
+ DEBUG_CALL("sorecvfrom");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
+ char buff[256];
+ int len;
+
+ len = recvfrom(so->s, buff, 256, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ /* XXX Check if reply is "correct"? */
+
+ if(len == -1 || len == 0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = 0; /* Don't m_free() it again! */
+ }
+ /* No need for this socket anymore, udp_detach it */
+ udp_detach(so);
+ } else { /* A "normal" UDP packet */
+ struct mbuf *m;
+ int len, n;
+
+ if (!(m = m_get())) return;
+ m->m_data += if_maxlinkhdr;
+
+ /*
+ * XXX Shouldn't FIONREAD packets destined for port 53,
+ * but I don't know the max packet size for DNS lookups
+ */
+ len = M_FREEROOM(m);
+ /* if (so->so_fport != htons(53)) { */
+ ioctlsocket(so->s, FIONREAD, &n);
+
+ if (n > len) {
+ n = (m->m_data - m->m_dat) + m->m_len + n + 1;
+ m_inc(m, n);
+ len = M_FREEROOM(m);
+ }
+ /* } */
+
+ m->m_len = recvfrom(so->s, m->m_data, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
+ m->m_len, errno,strerror(errno)));
+ if(m->m_len<0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ m_free(m);
+ } else {
+ /*
+ * Hack: domain name lookup will be used the most for UDP,
+ * and since they'll only be used once there's no need
+ * for the 4 minute (or whatever) timeout... So we time them
+ * out much quicker (10 seconds for now...)
+ */
+ if (so->so_expire) {
+ if (so->so_fport == htons(53))
+ so->so_expire = curtime + SO_EXPIREFAST;
+ else
+ so->so_expire = curtime + SO_EXPIRE;
+ }
+
+ /* if (m->m_len == len) {
+ * m_inc(m, MINCSIZE);
+ * m->m_len = 0;
+ * }
+ */
+
+ /*
+ * If this packet was destined for CTL_ADDR,
+ * make it look like that's where it came from, done by udp_output
+ */
+ udp_output(so, m, &addr);
+ } /* rx error */
+ } /* if ping packet */
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(so, m)
+ struct socket *so;
+ struct mbuf *m;
+{
+ int ret;
+ struct sockaddr_in addr;
+
+ DEBUG_CALL("sosendto");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+
+ /* Don't care what port we get */
+ ret = sendto(so->s, m->m_data, m->m_len, 0,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr));
+ if (ret < 0)
+ return -1;
+
+ /*
+ * Kill the socket if there's no reply in 4 minutes,
+ * but only if it's an expirable socket
+ */
+ if (so->so_expire)
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */
+ return 0;
+}
+
+/*
+ * XXX This should really be tcp_listen
+ */
+struct socket *
+solisten(port, laddr, lport, flags)
+ u_int port;
+ u_int32_t laddr;
+ u_int lport;
+ int flags;
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ int s, addrlen = sizeof(addr), opt = 1;
+
+ DEBUG_CALL("solisten");
+ DEBUG_ARG("port = %d", port);
+ DEBUG_ARG("laddr = %x", laddr);
+ DEBUG_ARG("lport = %d", lport);
+ DEBUG_ARG("flags = %x", flags);
+
+ if ((so = socreate()) == NULL) {
+ /* free(so); Not sofree() ??? free(NULL) == NOP */
+ return NULL;
+ }
+
+ /* Don't tcp_attach... we don't need so_snd nor so_rcv */
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
+ free(so);
+ return NULL;
+ }
+ insque(so,&tcb);
+
+ /*
+ * SS_FACCEPTONCE sockets must time out.
+ */
+ if (flags & SS_FACCEPTONCE)
+ so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+ so->so_state = (SS_FACCEPTCONN|flags);
+ so->so_lport = lport; /* Kept in network format */
+ so->so_laddr.s_addr = laddr; /* Ditto */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = port;
+
+ if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) ||
+ (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) ||
+ (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
+ (listen(s,1) < 0)) {
+ int tmperrno = errno; /* Don't clobber the real reason we failed */
+
+ close(s);
+ sofree(so);
+ /* Restore the real errno */
+#ifdef _WIN32
+ WSASetLastError(tmperrno);
+#else
+ errno = tmperrno;
+#endif
+ return NULL;
+ }
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+
+ getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->s = s;
+ return so;
+}
+
+/*
+ * Data is available in so_rcv
+ * Just write() the data to the socket
+ * XXX not yet...
+ */
+void
+sorwakeup(so)
+ struct socket *so;
+{
+/* sowrite(so); */
+/* FD_CLR(so->s,&writefds); */
+}
+
+/*
+ * Data has been freed in so_snd
+ * We have room for a read() if we want to
+ * For now, don't read, it'll be done in the main loop
+ */
+void
+sowwakeup(so)
+ struct socket *so;
+{
+ /* Nothing, yet */
+}
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(so)
+ register struct socket *so;
+{
+ so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+ SS_FCANTSENDMORE|SS_FWDRAIN);
+ so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(so)
+ register struct socket *so;
+{
+ so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+ so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+}
+
+void
+sofcantrcvmore(so)
+ struct socket *so;
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,0);
+ if(global_writefds) {
+ FD_CLR(so->s,global_writefds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTSENDMORE)
+ so->so_state = SS_NOFDREF; /* Don't select it */ /* XXX close() here as well? */
+ else
+ so->so_state |= SS_FCANTRCVMORE;
+}
+
+void
+sofcantsendmore(so)
+ struct socket *so;
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,1); /* send FIN to fhost */
+ if (global_readfds) {
+ FD_CLR(so->s,global_readfds);
+ }
+ if (global_xfds) {
+ FD_CLR(so->s,global_xfds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTRCVMORE)
+ so->so_state = SS_NOFDREF; /* as above */
+ else
+ so->so_state |= SS_FCANTSENDMORE;
+}
+
+void
+soisfdisconnected(so)
+ struct socket *so;
+{
+/* so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED); */
+/* close(so->s); */
+/* so->so_state = SS_ISFDISCONNECTED; */
+ /*
+ * XXX Do nothing ... ?
+ */
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(so)
+ struct socket *so;
+{
+ if (so->so_rcv.sb_cc)
+ so->so_state |= SS_FWDRAIN;
+ else
+ sofcantsendmore(so);
+}
+
diff --git a/slirp/socket.h b/slirp/socket.h
new file mode 100644
index 0000000..d05354c
--- /dev/null
+++ b/slirp/socket.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/* MINE */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket {
+ struct socket *so_next,*so_prev; /* For a linked list of sockets */
+
+ int s; /* The actual socket */
+
+ /* XXX union these with not-yet-used sbuf params */
+ struct mbuf *so_m; /* Pointer to the original SYN packet,
+ * for non-blocking connect()'s, and
+ * PING reply's */
+ struct tcpiphdr *so_ti; /* Pointer to the original ti within
+ * so_mconn, for non-blocking connections */
+ int so_urgc;
+ struct in_addr so_faddr; /* foreign host table entry */
+ struct in_addr so_laddr; /* local host table entry */
+ u_int16_t so_fport; /* foreign port */
+ u_int16_t so_lport; /* local port */
+
+ u_int8_t so_iptos; /* Type of service */
+ u_int8_t so_emu; /* Is the socket emulated? */
+
+ u_char so_type; /* Type of socket, UDP or TCP */
+ int so_state; /* internal state flags SS_*, below */
+
+ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
+ u_int so_expire; /* When the socket will expire */
+
+ int so_queued; /* Number of packets queued from this socket */
+ int so_nqueued; /* Number of packets queued in a row
+ * Used to determine when to "downgrade" a session
+ * from fastq to batchq */
+
+ struct sbuf so_rcv; /* Receive buffer */
+ struct sbuf so_snd; /* Send buffer */
+ void * extra; /* Extra pointer */
+};
+
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF 0x001 /* No fd reference */
+
+#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
+#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
+/* #define SS_ISFDISCONNECTED 0x020*/ /* Socket has disconnected from peer, in 2MSL state */
+#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+#define SS_CTL 0x080
+#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
+
+extern struct socket tcb;
+
+
+#if defined(DECLARE_IOVEC) && !defined(HAVE_READV)
+struct iovec {
+ char *iov_base;
+ size_t iov_len;
+};
+#endif
+
+void so_init _P((void));
+struct socket * solookup _P((struct socket *, struct in_addr, u_int, struct in_addr, u_int));
+struct socket * socreate _P((void));
+void sofree _P((struct socket *));
+int soread _P((struct socket *));
+void sorecvoob _P((struct socket *));
+int sosendoob _P((struct socket *));
+int sowrite _P((struct socket *));
+void sorecvfrom _P((struct socket *));
+int sosendto _P((struct socket *, struct mbuf *));
+struct socket * solisten _P((u_int, u_int32_t, u_int, int));
+void sorwakeup _P((struct socket *));
+void sowwakeup _P((struct socket *));
+void soisfconnecting _P((register struct socket *));
+void soisfconnected _P((register struct socket *));
+void sofcantrcvmore _P((struct socket *));
+void sofcantsendmore _P((struct socket *));
+void soisfdisconnected _P((struct socket *));
+void sofwdrain _P((struct socket *));
+
+#endif /* _SOCKET_H_ */
diff --git a/slirp/tcp.h b/slirp/tcp.h
new file mode 100644
index 0000000..cd7e891
--- /dev/null
+++ b/slirp/tcp.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+typedef u_int32_t tcp_seq;
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
+
+extern int tcp_rcvspace;
+extern int tcp_sndspace;
+extern struct socket *tcp_last_so;
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ u_int16_t th_sport; /* source port */
+ u_int16_t th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#ifdef WORDS_BIGENDIAN
+ u_int th_off:4, /* data offset */
+ th_x2:4; /* (unused) */
+#else
+ u_int th_x2:4, /* (unused) */
+ th_off:4; /* data offset */
+#endif
+ u_int8_t th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ u_int16_t th_win; /* window */
+ u_int16_t th_sum; /* checksum */
+ u_int16_t th_urp; /* urgent pointer */
+};
+
+#include "tcp_var.h"
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define TCP_MSS 1460
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ */
+/* #define TCP_NODELAY 0x01 */ /* don't delay send to coalesce packets */
+/* #define TCP_MAXSEG 0x02 */ /* set maximum segment size */
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+extern tcp_seq tcp_iss; /* tcp initial send seq # */
+
+extern char *tcpstates[];
+
+#endif
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
new file mode 100644
index 0000000..c015161
--- /dev/null
+++ b/slirp/tcp_input.c
@@ -0,0 +1,1725 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+struct socket tcb;
+
+int tcprexmtthresh = 3;
+struct socket *tcp_last_so = &tcb;
+
+tcp_seq tcp_iss; /* tcp initial send seq # */
+
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#ifdef TCP_ACK_HACK
+#define TCP_REASS(tp, ti, m, so, flags) {\
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ (tp)->seg_next == (tcpiphdrp_32)(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) {\
+ if (ti->ti_flags & TH_PUSH) \
+ tp->t_flags |= TF_ACKNOW; \
+ else \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ tcpstat.tcps_rcvpack++;\
+ tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend((so), (m)); \
+ } else \
+ sbappend((so), (m)); \
+/* sorwakeup(so); */ \
+ } else {\
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#else
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ (tp)->seg_next == (tcpiphdrp_32)(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ tcpstat.tcps_rcvpack++;\
+ tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend(so, (m)); \
+ } else \
+ sbappend((so), (m)); \
+/* sorwakeup(so); */ \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#endif
+
+int
+tcp_reass(tp, ti, m)
+ register struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ struct mbuf *m;
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_socket;
+ int flags;
+
+ /*
+ * Call with ti==0 after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == 0)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = (struct tcpiphdr *)tp->seg_next; q != (struct tcpiphdr *)tp;
+ q = (struct tcpiphdr *)q->ti_next)
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) {
+ register int i;
+ q = (struct tcpiphdr *)q->ti_prev;
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ m_freem(m);
+ /*
+ * Try to present any queued data
+ * at the left window edge to the user.
+ * This is needed after the 3-WHS
+ * completes.
+ */
+ goto present; /* ??? */
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = (struct tcpiphdr *)(q->ti_next);
+ }
+ tcpstat.tcps_rcvoopack++;
+ tcpstat.tcps_rcvoobyte += ti->ti_len;
+ REASS_MBUF(ti) = (mbufp_32) m; /* XXX */
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct tcpiphdr *)tp) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj((struct mbuf *) REASS_MBUF(q), i);
+ break;
+ }
+ q = (struct tcpiphdr *)q->ti_next;
+ m = (struct mbuf *) REASS_MBUF((struct tcpiphdr *)q->ti_prev);
+ remque_32((void *)(q->ti_prev));
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque_32(ti, (void *)(q->ti_prev));
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (!TCPS_HAVEESTABLISHED(tp->t_state))
+ return (0);
+ ti = (struct tcpiphdr *) tp->seg_next;
+ if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque_32(ti);
+ m = (struct mbuf *) REASS_MBUF(ti); /* XXX */
+ ti = (struct tcpiphdr *)ti->ti_next;
+/* if (so->so_state & SS_FCANTRCVMORE) */
+ if (so->so_state & SS_FCANTSENDMORE)
+ m_freem(m);
+ else {
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+ }
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+/* sorwakeup(so); */
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(m, iphlen, inso)
+ register struct mbuf *m;
+ int iphlen;
+ struct socket *inso;
+{
+ struct ip save_ip, *ip;
+ register struct tcpiphdr *ti;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = 0;
+ register int tiflags;
+ struct socket *so = 0;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+/* int dropsocket = 0; */
+ int iss = 0;
+ u_long tiwin;
+ int ret;
+/* int ts_present = 0; */
+
+ DEBUG_CALL("tcp_input");
+ DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n",
+ (long )m, iphlen, (long )inso ));
+
+ /*
+ * If called with m == 0, then we're continuing the connect
+ */
+ if (m == NULL) {
+ so = inso;
+
+ /* Re-set a few variables */
+ tp = sototcpcb(so);
+ m = so->so_m;
+ so->so_m = 0;
+ ti = so->so_ti;
+ tiwin = ti->ti_win;
+ tiflags = ti->ti_flags;
+
+ goto cont_conn;
+ }
+
+
+ tcpstat.tcps_rcvtotal++;
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof(struct ip )) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen=sizeof(struct ip );
+ }
+ /* XXX Check if too short */
+
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ ip=mtod(m, struct ip *);
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen;
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_len = htons((u_int16_t)tlen);
+ len = sizeof(struct ip ) + tlen;
+ /* keep checksum for ICMP reply
+ * ti->ti_sum = cksum(m, len);
+ * if (ti->ti_sum) { */
+ if(cksum(m, len)) {
+ tcpstat.tcps_rcvbadsum++;
+ goto drop;
+ }
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ tcpstat.tcps_rcvbadoff++;
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+
+ /*
+ * Do quick retrieval of timestamp options ("options
+ * prediction?"). If timestamp is the only option and it's
+ * formatted as recommended in RFC 1323 appendix A, we
+ * quickly get the values now and not bother calling
+ * tcp_dooptions(), etc.
+ */
+/* if ((optlen == TCPOLEN_TSTAMP_APPA ||
+ * (optlen > TCPOLEN_TSTAMP_APPA &&
+ * optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ * *(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ * (ti->ti_flags & TH_SYN) == 0) {
+ * ts_present = 1;
+ * ts_val = ntohl(*(u_int32_t *)(optp + 4));
+ * ts_ecr = ntohl(*(u_int32_t *)(optp + 8));
+ * optp = NULL; / * we've parsed the options * /
+ * }
+ */
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ /*
+ * Locate pcb for segment.
+ */
+findso:
+ so = tcp_last_so;
+ if (so->so_fport != ti->ti_dport ||
+ so->so_lport != ti->ti_sport ||
+ so->so_laddr.s_addr != ti->ti_src.s_addr ||
+ so->so_faddr.s_addr != ti->ti_dst.s_addr) {
+ so = solookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport);
+ if (so)
+ tcp_last_so = so;
+ ++tcpstat.tcps_socachemiss;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ *
+ * state == CLOSED means we've done socreate() but haven't
+ * attached it to a protocol yet...
+ *
+ * XXX If a TCB does not exist, and the TH_SYN flag is
+ * the only flag set, then create a session, mark it
+ * as if it was LISTENING, and continue...
+ */
+ if (so == 0) {
+ if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+ goto dropwithreset;
+
+ if ((so = socreate()) == NULL)
+ goto dropwithreset;
+ if (tcp_attach(so) < 0) {
+ free(so); /* Not sofree (if it failed, it's not insqued) */
+ goto dropwithreset;
+ }
+
+ sbreserve(&so->so_snd, tcp_sndspace);
+ sbreserve(&so->so_rcv, tcp_rcvspace);
+
+ /* tcp_last_so = so; */ /* XXX ? */
+ /* tp = sototcpcb(so); */
+
+ so->so_laddr = ti->ti_src;
+ so->so_lport = ti->ti_sport;
+ so->so_faddr = ti->ti_dst;
+ so->so_fport = ti->ti_dport;
+
+ if ((so->so_iptos = tcp_tos(so)) == 0)
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+ tp = sototcpcb(so);
+ tp->t_state = TCPS_LISTEN;
+ }
+
+ /*
+ * If this is a still-connecting socket, this probably
+ * a retransmit of the SYN. Whether it's a retransmit SYN
+ * or something else, we nuke it.
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ goto drop;
+
+ tp = sototcpcb(so);
+
+ /* XXX Should never fail */
+ if (tp == 0)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ /* Unscale the window into a 32-bit value. */
+/* if ((tiflags & TH_SYN) == 0)
+ * tiwin = ti->ti_win << tp->snd_scale;
+ * else
+ */
+ tiwin = ti->ti_win;
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ if (so_options)
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+/* , */
+/* &ts_present, &ts_val, &ts_ecr); */
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ *
+ * XXX Some of these tests are not needed
+ * eg: the tiwin == tp->snd_wnd prevents many more
+ * predictions.. with no *real* advantage..
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+/* (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) && */
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record the timestamp.
+ */
+/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) {
+ * tp->ts_recent_age = tcp_now;
+ * tp->ts_recent = ts_val;
+ * }
+ */
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ ++tcpstat.tcps_predack;
+/* if (ts_present)
+ * tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ * else
+ */ if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ /*
+ * There's room in so_snd, sowwakup will read()
+ * from the socket if we can
+ */
+/* if (so->so_snd.sb_flags & SB_NOTIFY)
+ * sowwakeup(so);
+ */
+ /*
+ * This is called because sowwakeup might have
+ * put data into so_snd. Since we don't so sowwakeup,
+ * we don't need this.. XXX???
+ */
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tp->seg_next == (tcpiphdrp_32)tp &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ ++tcpstat.tcps_preddat;
+ tp->rcv_nxt += ti->ti_len;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += ti->ti_len;
+ /*
+ * Add data to socket buffer.
+ */
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+
+ /*
+ * XXX This is called when data arrives. Later, check
+ * if we can actually write() to the socket
+ * XXX Need to check? It's be NON_BLOCKING
+ */
+/* sorwakeup(so); */
+
+ /*
+ * If this is a short packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * It is better to not delay acks at all to maximize
+ * TCP throughput. See RFC 2581.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ return;
+ }
+ } /* header prediction */
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+
+ /*
+ * This has way too many gotos...
+ * But a bit of spaghetti code never hurt anybody :)
+ */
+
+ /*
+ * If this is destined for the control address, then flag to
+ * tcp_ctl once connected, otherwise connect
+ */
+ if ((so->so_faddr.s_addr&htonl(0xffffff00)) == special_addr.s_addr) {
+ int lastbyte=ntohl(so->so_faddr.s_addr) & 0xff;
+ if (lastbyte!=CTL_ALIAS && lastbyte!=CTL_DNS) {
+#if 0
+ if(lastbyte==CTL_CMD || lastbyte==CTL_EXEC) {
+ /* Command or exec adress */
+ so->so_state |= SS_CTL;
+ } else
+#endif
+ {
+ /* May be an add exec */
+ struct ex_list *ex_ptr;
+ for(ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if(ex_ptr->ex_fport == so->so_fport &&
+ lastbyte == ex_ptr->ex_addr) {
+ so->so_state |= SS_CTL;
+ break;
+ }
+ }
+ }
+ if(so->so_state & SS_CTL) goto cont_input;
+ }
+ /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
+ }
+
+ if (so->so_emu & EMU_NOCONNECT) {
+ so->so_emu &= ~EMU_NOCONNECT;
+ goto cont_input;
+ }
+
+ if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
+ u_char code=ICMP_UNREACH_NET;
+ DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n",
+ errno,strerror(errno)));
+ if(errno == ECONNREFUSED) {
+ /* ACK the SYN, send RST to refuse the connection */
+ tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ } else {
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ HTONL(ti->ti_seq); /* restore tcp header */
+ HTONL(ti->ti_ack);
+ HTONS(ti->ti_win);
+ HTONS(ti->ti_urp);
+ m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ *ip=save_ip;
+ icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+ }
+ tp = tcp_close(tp);
+ m_free(m);
+ } else {
+ /*
+ * Haven't connected yet, save the current mbuf
+ * and ti, and return
+ * XXX Some OS's don't tell us whether the connect()
+ * succeeded or not. So we must time it out.
+ */
+ so->so_m = m;
+ so->so_ti = ti;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ }
+ return;
+
+ cont_conn:
+ /* m==NULL
+ * Check if the connect succeeded
+ */
+ if (so->so_state & SS_NOFDREF) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+ cont_input:
+ tcp_template(tp);
+
+ if (optp)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+ /* , */
+ /* &ts_present, &ts_val, &ts_ecr); */
+
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tcpstat.tcps_accepts++;
+ goto trimthenstep6;
+ } /* case TCPS_LISTEN */
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK)
+ tp = tcp_drop(tp,0); /* XXX Check t_softerror! */
+ goto drop;
+ }
+
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ tcpstat.tcps_connects++;
+ soisfconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+
+ /* Do window scaling on this connection? */
+/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ * (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ * tp->snd_scale = tp->requested_s_scale;
+ * tp->rcv_scale = tp->request_r_scale;
+ * }
+ */
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ tcpstat.tcps_rcvpackafterwin++;
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ } /* switch tp->t_state */
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * First check timestamp, if present.
+ * Then check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ *
+ * RFC 1323 PAWS: If we have a timestamp reply on this segment
+ * and it's less than ts_recent, drop it.
+ */
+/* if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
+ * TSTMP_LT(ts_val, tp->ts_recent)) {
+ *
+ */ /* Check to see if ts_recent is over 24 days old. */
+/* if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ */ /*
+ * * Invalidate ts_recent. If this segment updates
+ * * ts_recent, the age will be reset later and ts_recent
+ * * will get a valid value. If it does not, setting
+ * * ts_recent to zero will at least satisfy the
+ * * requirement that zero be placed in the timestamp
+ * * echo reply when ts_recent isn't valid. The
+ * * age isn't reset until we get a valid ts_recent
+ * * because we don't want out-of-order segments to be
+ * * dropped when ts_recent is old.
+ * */
+/* tp->ts_recent = 0;
+ * } else {
+ * tcpstat.tcps_rcvduppack++;
+ * tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ * tcpstat.tcps_pawsdrop++;
+ * goto dropafterack;
+ * }
+ * }
+ */
+
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ /*
+ * Following if statement from Stevens, vol. 2, p. 960.
+ */
+ if (todrop > ti->ti_len
+ || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
+ /*
+ * Any valid FIN must be to the left of the window.
+ * At this point the FIN must be a duplicate or out
+ * of sequence; drop it.
+ */
+ tiflags &= ~TH_FIN;
+
+ /*
+ * Send an ACK to resynchronize and drop any data.
+ * But keep on processing for RST or ACK.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ todrop = ti->ti_len;
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += todrop;
+ } else {
+ tcpstat.tcps_rcvpartduppack++;
+ tcpstat.tcps_rcvpartdupbyte += todrop;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ tcpstat.tcps_rcvafterclose++;
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ tcpstat.tcps_rcvpackafterwin++;
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findso;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_rcvwinprobe++;
+ } else
+ goto dropafterack;
+ } else
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record its timestamp.
+ */
+/* if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ * SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
+ * ((tiflags & (TH_SYN|TH_FIN)) != 0))) {
+ * tp->ts_recent_age = tcp_now;
+ * tp->ts_recent = ts_val;
+ * }
+ */
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+/* so->so_error = ECONNREFUSED; */
+ goto close;
+
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+/* so->so_error = ECONNRESET; */
+ close:
+ tp->t_state = TCPS_CLOSED;
+ tcpstat.tcps_drops++;
+ tp = tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tp = tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp,0);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0) goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST. una<=ack<=max
+ */
+ case TCPS_SYN_RECEIVED:
+
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tcpstat.tcps_connects++;
+ tp->t_state = TCPS_ESTABLISHED;
+ /*
+ * The sent SYN is ack'ed with our sequence number +1
+ * The first data byte already in the buffer will get
+ * lost if no correction is made. This is only needed for
+ * SS_CTL since the buffer is empty otherwise.
+ * tp->snd_una++; or:
+ */
+ tp->snd_una=ti->ti_ack;
+ if (so->so_state & SS_CTL) {
+ /* So tcp_ctl reports the right state */
+ ret = tcp_ctl(so);
+ if (ret == 1) {
+ soisfconnected(so);
+ so->so_state &= ~SS_CTL; /* success XXX */
+ } else if (ret == 2) {
+ so->so_state = SS_NOFDREF; /* CTL_CMD */
+ } else {
+ needoutput = 1;
+ tp->t_state = TCPS_FIN_WAIT_1;
+ }
+ } else {
+ soisfconnected(so);
+ }
+
+ /* Do window scaling? */
+/* if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ * (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ * tp->snd_scale = tp->requested_s_scale;
+ * tp->rcv_scale = tp->request_r_scale;
+ * }
+ */
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* Avoid ack processing; snd_una==ti_ack => dup ack */
+ goto synrx_to_est;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ tcpstat.tcps_rcvdupack++;
+ DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n",
+ (long )m, (long )so));
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == tcprexmtthresh) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > tcprexmtthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ synrx_to_est:
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > tcprexmtthresh &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ tcpstat.tcps_rcvacktoomuch++;
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+
+ /*
+ * If we have a timestamp reply, update smoothed
+ * round trip time. If no timestamp is present but
+ * transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+/* if (ts_present)
+ * tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ * else
+ */
+ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet).
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int )so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ /*
+ * XXX sowwakup is called when data is acked and there's room for
+ * for more data... it should read() the socket
+ */
+/* if (so->so_snd.sb_flags & SB_NOTIFY)
+ * sowwakeup(so);
+ */
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_FCANTRCVMORE) {
+ soisfdisconnected(so);
+ tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisfdisconnected(so);
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tp = tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ } /* switch(tp->t_state) */
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
+ (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) {
+ /* keep track of pure window updates */
+ if (ti->ti_len == 0 &&
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)
+ tcpstat.tcps_rcvwinupd++;
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) {
+ ti->ti_urp = 0;
+ tiflags &= ~TH_URG;
+ goto dodata;
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_urgc = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt); /* -1; */
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+
+ }
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata:
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ /*
+ * Note the amount of data that peer has sent into
+ * our window, in order to estimate the sender's
+ * buffer size.
+ */
+ len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt);
+ } else {
+ m_free(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * If we receive a FIN we can't send more data,
+ * set it SS_FDRAIN
+ * Shutdown the socket if there is no rx data in the
+ * buffer.
+ * soread() is called on completion of shutdown() and
+ * will got to TCPS_LAST_ACK, and use tcp_output()
+ * to send the FIN.
+ */
+/* sofcantrcvmore(so); */
+ sofwdrain(so);
+
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ if(so->so_emu == EMU_CTL) /* no shutdown on socket */
+ tp->t_state = TCPS_LAST_ACK;
+ else
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisfdisconnected(so);
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * See above.
+ */
+/* if (ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg) {
+ */
+/* if ((ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg &&
+ * (so->so_iptos & IPTOS_LOWDELAY) == 0) ||
+ * ((so->so_iptos & IPTOS_LOWDELAY) &&
+ * ((struct tcpiphdr_2 *)ti)->first_char == (char)27)) {
+ */
+ if (ti->ti_len && (unsigned)ti->ti_len <= 5 &&
+ ((struct tcpiphdr_2 *)ti)->first_char == (char)27) {
+ tp->t_flags |= TF_ACKNOW;
+ }
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW)) {
+ (void) tcp_output(tp);
+ }
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /* reuses m if m!=NULL, m_free() unnecessary */
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN) ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ m_free(m);
+
+ return;
+}
+
+ /* , ts_present, ts_val, ts_ecr) */
+/* int *ts_present;
+ * u_int32_t *ts_val, *ts_ecr;
+ */
+void
+tcp_dooptions(tp, cp, cnt, ti)
+ struct tcpcb *tp;
+ u_char *cp;
+ int cnt;
+ struct tcpiphdr *ti;
+{
+ u_int16_t mss;
+ int opt, optlen;
+
+ DEBUG_CALL("tcp_dooptions");
+ DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt));
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ memcpy((char *) &mss, (char *) cp + 2, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+
+/* case TCPOPT_WINDOW:
+ * if (optlen != TCPOLEN_WINDOW)
+ * continue;
+ * if (!(ti->ti_flags & TH_SYN))
+ * continue;
+ * tp->t_flags |= TF_RCVD_SCALE;
+ * tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ * break;
+ */
+/* case TCPOPT_TIMESTAMP:
+ * if (optlen != TCPOLEN_TIMESTAMP)
+ * continue;
+ * *ts_present = 1;
+ * memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val));
+ * NTOHL(*ts_val);
+ * memcpy((char *) ts_ecr, (char *)cp + 6, sizeof(*ts_ecr));
+ * NTOHL(*ts_ecr);
+ *
+ */ /*
+ * * A timestamp received in a SYN makes
+ * * it ok to send timestamp requests and replies.
+ * */
+/* if (ti->ti_flags & TH_SYN) {
+ * tp->t_flags |= TF_RCVD_TSTMP;
+ * tp->ts_recent = *ts_val;
+ * tp->ts_recent_age = tcp_now;
+ * }
+ */ break;
+ }
+ }
+}
+
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+
+#ifdef notdef
+
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next; /* XXX WRONG! Fix it! */
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+#endif /* notdef */
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+void
+tcp_xmit_timer(tp, rtt)
+ register struct tcpcb *tp;
+ int rtt;
+{
+ register short delta;
+
+ DEBUG_CALL("tcp_xmit_timer");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("rtt = %d", rtt);
+
+ tcpstat.tcps_rttupdated++;
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+
+int
+tcp_mss(tp, offer)
+ register struct tcpcb *tp;
+ u_int offer;
+{
+ struct socket *so = tp->t_socket;
+ int mss;
+
+ DEBUG_CALL("tcp_mss");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("offer = %d", offer);
+
+ mss = min(if_mtu, if_mru) - sizeof(struct tcpiphdr);
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32);
+ if (mss < tp->t_maxseg || offer != 0)
+ tp->t_maxseg = mss;
+
+ tp->snd_cwnd = mss;
+
+ sbreserve(&so->so_snd, tcp_sndspace+((tcp_sndspace%mss)?(mss-(tcp_sndspace%mss)):0));
+ sbreserve(&so->so_rcv, tcp_rcvspace+((tcp_rcvspace%mss)?(mss-(tcp_rcvspace%mss)):0));
+
+ DEBUG_MISC((dfd, " returning mss = %d\n", mss));
+
+ return mss;
+}
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
new file mode 100644
index 0000000..b79bcf1
--- /dev/null
+++ b/slirp/tcp_output.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/*
+ * Since this is only used in "stats socket", we give meaning
+ * names instead of the REAL names
+ */
+char *tcpstates[] = {
+/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */
+ "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD",
+ "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
+ "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
+};
+
+u_char tcp_outflags[TCP_NSTATES] = {
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
+ TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(tp)
+ register struct tcpcb *tp;
+{
+ register struct socket *so = tp->t_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+
+ DEBUG_CALL("tcp_output");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+
+ DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
+
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force) {
+ if (win == 0) {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < so->so_snd.sb_cc)
+ flags &= ~TH_FIN;
+ win = 1;
+ } else {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(so->so_snd.sb_cc, win) - off;
+
+ if (len < 0) {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+
+ if (len > tp->t_maxseg) {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len) {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((1 || idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= so->so_snd.sb_cc)
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0) {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+ (tp->rcv_adv - tp->rcv_nxt);
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) so->so_rcv.sb_datalen)
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if (flags & TH_FIN &&
+ ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->t_timer[TCPT_PERSIST] == 0) {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ tcpstat.tcps_didnuttin++;
+
+ return (0);
+
+send:
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN) {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0) {
+ u_int16_t mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((u_int16_t) tcp_mss(tp, 0));
+ memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+ optlen = 4;
+
+/* if ((tp->t_flags & TF_REQ_SCALE) &&
+ * ((flags & TH_ACK) == 0 ||
+ * (tp->t_flags & TF_RCVD_SCALE))) {
+ * *((u_int32_t *) (opt + optlen)) = htonl(
+ * TCPOPT_NOP << 24 |
+ * TCPOPT_WINDOW << 16 |
+ * TCPOLEN_WINDOW << 8 |
+ * tp->request_r_scale);
+ * optlen += 4;
+ * }
+ */
+ }
+ }
+
+ /*
+ * Send a timestamp and echo-reply if this is a SYN and our side
+ * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
+ * and our peer have sent timestamps in our SYN's.
+ */
+/* if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
+ * (flags & TH_RST) == 0 &&
+ * ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
+ * (tp->t_flags & TF_RCVD_TSTMP))) {
+ * u_int32_t *lp = (u_int32_t *)(opt + optlen);
+ *
+ * / * Form timestamp option as shown in appendix A of RFC 1323. * /
+ * *lp++ = htonl(TCPOPT_TSTAMP_HDR);
+ * *lp++ = htonl(tcp_now);
+ * *lp = htonl(tp->ts_recent);
+ * optlen += TCPOLEN_TSTAMP_APPA;
+ * }
+ */
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen) {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ }
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len) {
+ if (tp->t_force && len == 1)
+ tcpstat.tcps_sndprobe++;
+ else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+ tcpstat.tcps_sndrexmitpack++;
+ tcpstat.tcps_sndrexmitbyte += len;
+ } else {
+ tcpstat.tcps_sndpack++;
+ tcpstat.tcps_sndbyte += len;
+ }
+
+ m = m_get();
+ if (m == NULL) {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += if_maxlinkhdr;
+ m->m_len = hdrlen;
+
+ /*
+ * This will always succeed, since we make sure our mbufs
+ * are big enough to hold one MSS packet + header + ... etc.
+ */
+/* if (len <= MHLEN - hdrlen - max_linkhdr) { */
+
+ sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+
+/* } else {
+ * m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
+ * if (m->m_next == 0)
+ * len = 0;
+ * }
+ */
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == so->so_snd.sb_cc)
+ flags |= TH_PUSH;
+ } else {
+ if (tp->t_flags & TF_ACKNOW)
+ tcpstat.tcps_sndacks++;
+ else if (flags & (TH_SYN|TH_FIN|TH_RST))
+ tcpstat.tcps_sndctrl++;
+ else if (SEQ_GT(tp->snd_up, tp->snd_una))
+ tcpstat.tcps_sndurg++;
+ else
+ tcpstat.tcps_sndwinup++;
+
+ m = m_get();
+ if (m == NULL) {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += if_maxlinkhdr;
+ m->m_len = hdrlen;
+ }
+
+ ti = mtod(m, struct tcpiphdr *);
+
+ memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
+ tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = htonl(tp->snd_nxt);
+ else
+ ti->ti_seq = htonl(tp->snd_max);
+ ti->ti_ack = htonl(tp->rcv_nxt);
+ if (optlen) {
+ memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
+ ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale));
+
+ if (SEQ_GT(tp->snd_up, tp->snd_una)) {
+ ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+#ifdef notdef
+ if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+ ti->ti_urp = htons((u_int16_t)(tp->snd_up - tp->snd_nxt));
+#endif
+ ti->ti_flags |= TH_URG;
+ } else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN)) {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN) {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0) {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ tcpstat.tcps_segstimed++;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->snd_nxt != tp->snd_una) {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST]) {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ } else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+
+ {
+
+ ((struct ip *)ti)->ip_len = m->m_len;
+
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+ ((struct ip *)ti)->ip_tos = so->so_iptos;
+
+/* #if BSD >= 43 */
+ /* Don't do IP options... */
+/* error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ * so->so_options & SO_DONTROUTE, 0);
+ */
+ error = ip_output(so, m);
+
+/* #else
+ * error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route,
+ * so->so_options & SO_DONTROUTE);
+ * #endif
+ */
+ }
+ if (error) {
+out:
+/* if (error == ENOBUFS) {
+ * tcp_quench(tp->t_inpcb, 0);
+ * return (0);
+ * }
+ */
+/* if ((error == EHOSTUNREACH || error == ENETDOWN)
+ * && TCPS_HAVERCVDSYN(tp->t_state)) {
+ * tp->t_softerror = error;
+ * return (0);
+ * }
+ */
+ return (error);
+ }
+ tcpstat.tcps_sndtotal++;
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+
+ return (0);
+}
+
+void
+tcp_setpersist(tp)
+ register struct tcpcb *tp;
+{
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+/* if (tp->t_timer[TCPT_REXMT])
+ * panic("tcp_output REXMT");
+ */
+ /*
+ * Start/restart persistence timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
new file mode 100644
index 0000000..e66987e
--- /dev/null
+++ b/slirp/tcp_subr.c
@@ -0,0 +1,1324 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+
+/* patchable/settable parameters for tcp */
+int tcp_mssdflt = TCP_MSS;
+int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
+int tcp_do_rfc1323 = 0; /* Don't do rfc1323 performance enhancements */
+int tcp_rcvspace; /* You may want to change this */
+int tcp_sndspace; /* Keep small if you have an error prone link */
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init()
+{
+ tcp_iss = 1; /* wrong */
+ tcb.so_next = tcb.so_prev = &tcb;
+
+ /* tcp_rcvspace = our Window we advertise to the remote */
+ tcp_rcvspace = TCP_RCVSPACE;
+ tcp_sndspace = TCP_SNDSPACE;
+
+ /* Make sure tcp_sndspace is at least 2*MSS */
+ if (tcp_sndspace < 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr)))
+ tcp_sndspace = 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr));
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+/* struct tcpiphdr * */
+void
+tcp_template(tp)
+ struct tcpcb *tp;
+{
+ struct socket *so = tp->t_socket;
+ register struct tcpiphdr *n = &tp->t_template;
+
+ n->ti_next = n->ti_prev = 0;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(tp, ti, m, ack, seq, flags)
+ struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ register struct mbuf *m;
+ tcp_seq ack, seq;
+ int flags;
+{
+ register int tlen;
+ int win = 0;
+
+ DEBUG_CALL("tcp_respond");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("ti = %lx", (long)ti);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("ack = %u", ack);
+ DEBUG_ARG("seq = %u", seq);
+ DEBUG_ARG("flags = %x", flags);
+
+ if (tp)
+ win = sbspace(&tp->t_socket->so_rcv);
+ if (m == 0) {
+ if ((m = m_get()) == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += if_maxlinkhdr;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ /*
+ * ti points into m so the next line is just making
+ * the mbuf point to ti
+ */
+ m->m_data = (caddr_t)ti;
+
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t);
+ xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((u_int16_t)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+
+ if(flags & TH_RST)
+ ((struct ip *)ti)->ip_ttl = MAXTTL;
+ else
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+
+ (void) ip_output((struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(so)
+ struct socket *so;
+{
+ register struct tcpcb *tp;
+
+ tp = (struct tcpcb *)malloc(sizeof(*tp));
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+
+ memset((char *) tp, 0, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (tcpiphdrp_32)tp;
+ tp->t_maxseg = tcp_mssdflt;
+
+ tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_socket = so;
+
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+ tp->t_rttmin = TCPTV_MIN;
+
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->t_state = TCPS_CLOSED;
+
+ so->so_tcpcb = tp;
+
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
+{
+/* tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+*/
+
+ DEBUG_CALL("tcp_drop");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("errno = %d", errno);
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ tcpstat.tcps_drops++;
+ } else
+ tcpstat.tcps_conndrops++;
+/* if (errno == ETIMEDOUT && tp->t_softerror)
+ * errno = tp->t_softerror;
+ */
+/* so->so_error = errno; */
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(tp)
+ register struct tcpcb *tp;
+{
+ register struct tcpiphdr *t;
+ struct socket *so = tp->t_socket;
+ register struct mbuf *m;
+
+ DEBUG_CALL("tcp_close");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /* free the reassembly queue, if any */
+ t = (struct tcpiphdr *) tp->seg_next;
+ while (t != (struct tcpiphdr *)tp) {
+ t = (struct tcpiphdr *)t->ti_next;
+ m = (struct mbuf *) REASS_MBUF((struct tcpiphdr *)t->ti_prev);
+ remque_32((struct tcpiphdr *) t->ti_prev);
+ m_freem(m);
+ }
+ /* It's static */
+/* if (tp->t_template)
+ * (void) m_free(dtom(tp->t_template));
+ */
+/* free(tp, M_PCB); */
+ free(tp);
+ so->so_tcpcb = 0;
+ soisfdisconnected(so);
+ /* clobber input socket cache if we're closing the cached connection */
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ closesocket(so->s);
+ sbfree(&so->so_rcv);
+ sbfree(&so->so_snd);
+ sofree(so);
+ tcpstat.tcps_closed++;
+ return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain()
+{
+ /* XXX */
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+
+#ifdef notdef
+
+void
+tcp_quench(i, errno)
+
+ int errno;
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
+
+#endif /* notdef */
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+void
+tcp_sockclosed(tp)
+ struct tcpcb *tp;
+{
+
+ DEBUG_CALL("tcp_sockclosed");
+ DEBUG_ARG("tp = %lx", (long)tp);
+
+ switch (tp->t_state) {
+
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ tp->t_state = TCPS_CLOSED;
+ tp = tcp_close(tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_FIN_WAIT_1;
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_LAST_ACK;
+ break;
+ }
+/* soisfdisconnecting(tp->t_socket); */
+ if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
+ soisfdisconnected(tp->t_socket);
+ if (tp)
+ tcp_output(tp);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking. Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(so)
+ struct socket *so;
+{
+ int ret=0;
+
+ DEBUG_CALL("tcp_fconnect");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if( (ret=so->s=socket(AF_INET,SOCK_STREAM,0)) >= 0) {
+ int opt, s=so->s;
+ struct sockaddr_in addr;
+
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
+ "addr.sin_addr.s_addr=%.16s\n",
+ ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ /* We don't care what port we get */
+ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+
+ /*
+ * If it's not in progress, it failed, so we just return 0,
+ * without clearing SS_NOFDREF
+ */
+ soisfconnecting(so);
+ }
+
+ return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(inso)
+ struct socket *inso;
+{
+ struct socket *so;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(struct sockaddr_in);
+ struct tcpcb *tp;
+ int s, opt;
+
+ DEBUG_CALL("tcp_connect");
+ DEBUG_ARG("inso = %lx", (long)inso);
+
+ /*
+ * If it's an SS_ACCEPTONCE socket, no need to socreate()
+ * another socket, just use the accept() socket.
+ */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ /* FACCEPTONCE already have a tcpcb */
+ so = inso;
+ } else {
+ if ((so = socreate()) == NULL) {
+ /* If it failed, get rid of the pending connection */
+ closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
+ return;
+ }
+ if (tcp_attach(so) < 0) {
+ free(so); /* NOT sofree */
+ return;
+ }
+ so->so_laddr = inso->so_laddr;
+ so->so_lport = inso->so_lport;
+ }
+
+ (void) tcp_mss(sototcpcb(so), 0);
+
+ if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) {
+ tcp_close(sototcpcb(so)); /* This will sofree() as well */
+ return;
+ }
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+
+ so->so_fport = addr.sin_port;
+ so->so_faddr = addr.sin_addr;
+ /* Translate connections from localhost to the real hostname */
+ if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+
+ /* Close the accept() socket, set right state */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ closesocket(so->s); /* If we only accept once, close the accept() socket */
+ so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+ /* if it's not FACCEPTONCE, it's already NOFDREF */
+ }
+ so->s = s;
+
+ so->so_iptos = tcp_tos(so);
+ tp = sototcpcb(so);
+
+ tcp_template(tp);
+
+ /* Compute window scaling to request. */
+/* while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ * tp->request_r_scale++;
+ */
+
+/* soisconnecting(so); */ /* NOFDREF used instead */
+ tcpstat.tcps_connattempt++;
+
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(so)
+ struct socket *so;
+{
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
+ return -1;
+
+ insque(so, &tcb);
+
+ return 0;
+}
+
+/*
+ * Set the socket's type of service field
+ */
+struct tos_t tcptos[] = {
+ {0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */
+ {21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */
+ {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */
+ {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */
+ {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */
+ {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */
+ {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */
+ {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */
+ {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */
+ {0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */
+ {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
+ {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
+ {0, 0, 0, 0}
+};
+
+struct emu_t *tcpemu = 0;
+
+/*
+ * Return TOS according to the above table
+ */
+u_int8_t
+tcp_tos(so)
+ struct socket *so;
+{
+ int i = 0;
+ struct emu_t *emup;
+
+ while(tcptos[i].tos) {
+ if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||
+ (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {
+ so->so_emu = tcptos[i].emu;
+ return tcptos[i].tos;
+ }
+ i++;
+ }
+
+ /* Nope, lets see if there's a user-added one */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) ||
+ (emup->lport && (ntohs(so->so_lport) == emup->lport))) {
+ so->so_emu = emup->emu;
+ return emup->tos;
+ }
+ }
+
+ return 0;
+}
+
+int do_echo = -1;
+
+/*
+ * Emulate programs that try and connect to us
+ * This includes ftp (the data connection is
+ * initiated by the server) and IRC (DCC CHAT and
+ * DCC SEND) for now
+ *
+ * NOTE: It's possible to crash SLiRP by sending it
+ * unstandard strings to emulate... if this is a problem,
+ * more checks are needed here
+ *
+ * XXX Assumes the whole command came in one packet
+ *
+ * XXX Some ftp clients will have their TOS set to
+ * LOWDELAY and so Nagel will kick in. Because of this,
+ * we'll get the first letter, followed by the rest, so
+ * we simply scan for ORT instead of PORT...
+ * DCC doesn't have this problem because there's other stuff
+ * in the packet before the DCC command.
+ *
+ * Return 1 if the mbuf m is still valid and should be
+ * sbappend()ed
+ *
+ * NOTE: if you return 0 you MUST m_free() the mbuf!
+ */
+int
+tcp_emu(so, m)
+ struct socket *so;
+ struct mbuf *m;
+{
+ u_int n1, n2, n3, n4, n5, n6;
+ char buff[256];
+ u_int32_t laddr;
+ u_int lport;
+ char *bptr;
+
+ DEBUG_CALL("tcp_emu");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ switch(so->so_emu) {
+ int x, i;
+
+ case EMU_IDENT:
+ /*
+ * Identification protocol as per rfc-1413
+ */
+
+ {
+ struct socket *tmpso;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(struct sockaddr_in);
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m->m_data[m->m_len] = 0; /* NULL terminate */
+ if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
+ if (sscanf(so_rcv->sb_data, "%d%*[ ,]%d", &n1, &n2) == 2) {
+ HTONS(n1);
+ HTONS(n2);
+ /* n2 is the one on our host */
+ for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) {
+ if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&
+ tmpso->so_lport == n2 &&
+ tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&
+ tmpso->so_fport == n1) {
+ if (getsockname(tmpso->s,
+ (struct sockaddr *)&addr, &addrlen) == 0)
+ n2 = ntohs(addr.sin_port);
+ break;
+ }
+ }
+ }
+ so_rcv->sb_cc = sprintf(so_rcv->sb_data, "%d,%d\r\n", n1, n2);
+ so_rcv->sb_rptr = so_rcv->sb_data;
+ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
+ }
+ m_free(m);
+ return 0;
+ }
+
+#if 0
+ case EMU_RLOGIN:
+ /*
+ * Rlogin emulation
+ * First we accumulate all the initial option negotiation,
+ * then fork_exec() rlogin according to the options
+ */
+ {
+ int i, i2, n;
+ char *ptr;
+ char args[100];
+ char term[100];
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /* First check if they have a priveladged port, or too much data has arrived */
+ if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 ||
+ (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ m_free(m);
+ return 0;
+ }
+
+ /* Append the current data */
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m_free(m);
+
+ /*
+ * Check if we have all the initial options,
+ * and build argument list to rlogin while we're here
+ */
+ n = 0;
+ ptr = so_rcv->sb_data;
+ args[0] = 0;
+ term[0] = 0;
+ while (ptr < so_rcv->sb_wptr) {
+ if (*ptr++ == 0) {
+ n++;
+ if (n == 2) {
+ sprintf(args, "rlogin -l %s %s",
+ ptr, inet_ntoa(so->so_faddr));
+ } else if (n == 3) {
+ i2 = so_rcv->sb_wptr - ptr;
+ for (i = 0; i < i2; i++) {
+ if (ptr[i] == '/') {
+ ptr[i] = 0;
+#ifdef HAVE_SETENV
+ sprintf(term, "%s", ptr);
+#else
+ sprintf(term, "TERM=%s", ptr);
+#endif
+ ptr[i] = '/';
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (n != 4)
+ return 0;
+
+ /* We have it, set our term variable and fork_exec() */
+#ifdef HAVE_SETENV
+ setenv("TERM", term, 1);
+#else
+ putenv(term);
+#endif
+ fork_exec(so, args, 2);
+ term[0] = 0;
+ so->so_emu = 0;
+
+ /* And finally, send the client a 0 character */
+ so_snd->sb_wptr[0] = 0;
+ so_snd->sb_wptr++;
+ so_snd->sb_cc++;
+
+ return 0;
+ }
+
+ case EMU_RSH:
+ /*
+ * rsh emulation
+ * First we accumulate all the initial option negotiation,
+ * then rsh_exec() rsh according to the options
+ */
+ {
+ int n;
+ char *ptr;
+ char *user;
+ char *args;
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /* First check if they have a priveladged port, or too much data has arrived */
+ if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 ||
+ (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ m_free(m);
+ return 0;
+ }
+
+ /* Append the current data */
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m_free(m);
+
+ /*
+ * Check if we have all the initial options,
+ * and build argument list to rlogin while we're here
+ */
+ n = 0;
+ ptr = so_rcv->sb_data;
+ user="";
+ args="";
+ if (so->extra==NULL) {
+ struct socket *ns;
+ struct tcpcb* tp;
+ int port=atoi(ptr);
+ if (port <= 0) return 0;
+ if (port > 1023 || port < 512) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ return 0;
+ }
+ if ((ns=socreate()) == NULL)
+ return 0;
+ if (tcp_attach(ns)<0) {
+ free(ns);
+ return 0;
+ }
+
+ ns->so_laddr=so->so_laddr;
+ ns->so_lport=htons(port);
+
+ (void) tcp_mss(sototcpcb(ns), 0);
+
+ ns->so_faddr=so->so_faddr;
+ ns->so_fport=htons(IPPORT_RESERVED-1); /* Use a fake port. */
+
+ if (ns->so_faddr.s_addr == 0 ||
+ ns->so_faddr.s_addr == loopback_addr.s_addr)
+ ns->so_faddr = alias_addr;
+
+ ns->so_iptos = tcp_tos(ns);
+ tp = sototcpcb(ns);
+
+ tcp_template(tp);
+
+ /* Compute window scaling to request. */
+ /* while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ * (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ * tp->request_r_scale++;
+ */
+
+ /*soisfconnecting(ns);*/
+
+ tcpstat.tcps_connattempt++;
+
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+ so->extra=ns;
+ }
+ while (ptr < so_rcv->sb_wptr) {
+ if (*ptr++ == 0) {
+ n++;
+ if (n == 2) {
+ user=ptr;
+ } else if (n == 3) {
+ args=ptr;
+ }
+ }
+ }
+
+ if (n != 4)
+ return 0;
+
+ rsh_exec(so,so->extra, user, inet_ntoa(so->so_faddr), args);
+ so->so_emu = 0;
+ so->extra=NULL;
+
+ /* And finally, send the client a 0 character */
+ so_snd->sb_wptr[0] = 0;
+ so_snd->sb_wptr++;
+ so_snd->sb_cc++;
+
+ return 0;
+ }
+
+ case EMU_CTL:
+ {
+ int num;
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /*
+ * If there is binary data here, we save it in so->so_m
+ */
+ if (!so->so_m) {
+ int rxlen;
+ char *rxdata;
+ rxdata=mtod(m, char *);
+ for (rxlen=m->m_len; rxlen; rxlen--) {
+ if (*rxdata++ & 0x80) {
+ so->so_m = m;
+ return 0;
+ }
+ }
+ } /* if(so->so_m==NULL) */
+
+ /*
+ * Append the line
+ */
+ sbappendsb(so_rcv, m);
+
+ /* To avoid going over the edge of the buffer, we reset it */
+ if (so_snd->sb_cc == 0)
+ so_snd->sb_wptr = so_snd->sb_rptr = so_snd->sb_data;
+
+ /*
+ * A bit of a hack:
+ * If the first packet we get here is 1 byte long, then it
+ * was done in telnet character mode, therefore we must echo
+ * the characters as they come. Otherwise, we echo nothing,
+ * because in linemode, the line is already echoed
+ * XXX two or more control connections won't work
+ */
+ if (do_echo == -1) {
+ if (m->m_len == 1) do_echo = 1;
+ else do_echo = 0;
+ }
+ if (do_echo) {
+ sbappendsb(so_snd, m);
+ m_free(m);
+ tcp_output(sototcpcb(so)); /* XXX */
+ } else
+ m_free(m);
+
+ num = 0;
+ while (num < so->so_rcv.sb_cc) {
+ if (*(so->so_rcv.sb_rptr + num) == '\n' ||
+ *(so->so_rcv.sb_rptr + num) == '\r') {
+ int n;
+
+ *(so_rcv->sb_rptr + num) = 0;
+ if (ctl_password && !ctl_password_ok) {
+ /* Need a password */
+ if (sscanf(so_rcv->sb_rptr, "pass %256s", buff) == 1) {
+ if (strcmp(buff, ctl_password) == 0) {
+ ctl_password_ok = 1;
+ n = sprintf(so_snd->sb_wptr,
+ "Password OK.\r\n");
+ goto do_prompt;
+ }
+ }
+ n = sprintf(so_snd->sb_wptr,
+ "Error: Password required, log on with \"pass PASSWORD\"\r\n");
+ goto do_prompt;
+ }
+ cfg_quitting = 0;
+ n = do_config(so_rcv->sb_rptr, so, PRN_SPRINTF);
+ if (!cfg_quitting) {
+ /* Register the printed data */
+do_prompt:
+ so_snd->sb_cc += n;
+ so_snd->sb_wptr += n;
+ /* Add prompt */
+ n = sprintf(so_snd->sb_wptr, "Slirp> ");
+ so_snd->sb_cc += n;
+ so_snd->sb_wptr += n;
+ }
+ /* Drop so_rcv data */
+ so_rcv->sb_cc = 0;
+ so_rcv->sb_wptr = so_rcv->sb_rptr = so_rcv->sb_data;
+ tcp_output(sototcpcb(so)); /* Send the reply */
+ }
+ num++;
+ }
+ return 0;
+ }
+#endif
+ case EMU_FTP: /* ftp */
+ *(m->m_data+m->m_len) = 0; /* NULL terminate for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
+ /*
+ * Need to emulate the PORT command
+ */
+ x = sscanf(bptr, "ORT %d,%d,%d,%d,%d,%d\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += sprintf(bptr,"ORT %d,%d,%d,%d,%d,%d\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+ return 1;
+ } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
+ /*
+ * Need to emulate the PASV response
+ */
+ x = sscanf(bptr, "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += sprintf(bptr,"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+
+ return 1;
+ }
+
+ return 1;
+
+ case EMU_KSH:
+ /*
+ * The kshell (Kerberos rsh) and shell services both pass
+ * a local port port number to carry signals to the server
+ * and stderr to the client. It is passed at the beginning
+ * of the connection as a NUL-terminated decimal ASCII string.
+ */
+ so->so_emu = 0;
+ for (lport = 0, i = 0; i < m->m_len-1; ++i) {
+ if (m->m_data[i] < '0' || m->m_data[i] > '9')
+ return 1; /* invalid number */
+ lport *= 10;
+ lport += m->m_data[i] - '0';
+ }
+ if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
+ (so = solisten(0, so->so_laddr.s_addr, htons(lport), SS_FACCEPTONCE)) != NULL)
+ m->m_len = sprintf(m->m_data, "%d", ntohs(so->so_fport))+1;
+ return 1;
+
+ case EMU_IRC:
+ /*
+ * Need to emulate DCC CHAT, DCC SEND and DCC MOVE
+ */
+ *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
+ return 1;
+
+ /* The %256s is for the broken mIRC */
+ if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += sprintf(bptr, "DCC CHAT chat %lu %u%c\n",
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), 1);
+ } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += sprintf(bptr, "DCC SEND %s %lu %u %u%c\n",
+ buff, (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += sprintf(bptr, "DCC MOVE %s %lu %u %u%c\n",
+ buff, (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ }
+ return 1;
+
+ case EMU_REALAUDIO:
+ /*
+ * RealAudio emulation - JP. We must try to parse the incoming
+ * data and try to find the two characters that contain the
+ * port number. Then we redirect an udp port and replace the
+ * number with the real port we got.
+ *
+ * The 1.0 beta versions of the player are not supported
+ * any more.
+ *
+ * A typical packet for player version 1.0 (release version):
+ *
+ * 0000:50 4E 41 00 05
+ * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 .....×..gælÜc..P
+ * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
+ * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
+ * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
+ *
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * second packet. This time we received five bytes first and
+ * then the rest. You never know how many bytes you get.
+ *
+ * A typical packet for player version 2.0 (beta):
+ *
+ * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA...........Á.
+ * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .guxõc..Win2.0.0
+ * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
+ * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
+ * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B
+ *
+ * Port number 0x1BC1 is found at offset 0x0d.
+ *
+ * This is just a horrible switch statement. Variable ra tells
+ * us where we're going.
+ */
+
+ bptr = m->m_data;
+ while (bptr < m->m_data + m->m_len) {
+ u_short p;
+ static int ra = 0;
+ char ra_tbl[4];
+
+ ra_tbl[0] = 0x50;
+ ra_tbl[1] = 0x4e;
+ ra_tbl[2] = 0x41;
+ ra_tbl[3] = 0;
+
+ switch (ra) {
+ case 0:
+ case 2:
+ case 3:
+ if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 1:
+ /*
+ * We may get 0x50 several times, ignore them
+ */
+ if (*bptr == 0x50) {
+ ra = 1;
+ bptr++;
+ continue;
+ } else if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 4:
+ /*
+ * skip version number
+ */
+ bptr++;
+ break;
+
+ case 5:
+ /*
+ * The difference between versions 1.0 and
+ * 2.0 is here. For future versions of
+ * the player this may need to be modified.
+ */
+ if (*(bptr + 1) == 0x02)
+ bptr += 8;
+ else
+ bptr += 4;
+ break;
+
+ case 6:
+ /* This is the field containing the port
+ * number that RA-player is listening to.
+ */
+ lport = (((u_char*)bptr)[0] << 8)
+ + ((u_char *)bptr)[1];
+ if (lport < 6970)
+ lport += 256; /* don't know why */
+ if (lport < 6970 || lport > 7170)
+ return 1; /* failed */
+
+ /* try to get udp port between 6970 - 7170 */
+ for (p = 6970; p < 7071; p++) {
+ if (udp_listen( htons(p),
+ so->so_laddr.s_addr,
+ htons(lport),
+ SS_FACCEPTONCE)) {
+ break;
+ }
+ }
+ if (p == 7071)
+ p = 0;
+ *(u_char *)bptr++ = (p >> 8) & 0xff;
+ *(u_char *)bptr++ = p & 0xff;
+ ra = 0;
+ return 1; /* port redirected, we're done */
+ break;
+
+ default:
+ ra = 0;
+ }
+ ra++;
+ }
+ return 1;
+
+ default:
+ /* Ooops, not emulated, won't call tcp_emu again */
+ so->so_emu = 0;
+ return 1;
+ }
+}
+
+/*
+ * Do misc. config of SLiRP while its running.
+ * Return 0 if this connections is to be closed, 1 otherwise,
+ * return 2 if this is a command-line connection
+ */
+int
+tcp_ctl(so)
+ struct socket *so;
+{
+ struct sbuf *sb = &so->so_snd;
+ int command;
+ struct ex_list *ex_ptr;
+ int do_pty;
+ // struct socket *tmpso;
+
+ DEBUG_CALL("tcp_ctl");
+ DEBUG_ARG("so = %lx", (long )so);
+
+#if 0
+ /*
+ * Check if they're authorised
+ */
+ if (ctl_addr.s_addr && (ctl_addr.s_addr == -1 || (so->so_laddr.s_addr != ctl_addr.s_addr))) {
+ sb->sb_cc = sprintf(sb->sb_wptr,"Error: Permission denied.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+ }
+#endif
+ command = (ntohl(so->so_faddr.s_addr) & 0xff);
+
+ switch(command) {
+ default: /* Check for exec's */
+
+ /*
+ * Check if it's pty_exec
+ */
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == so->so_fport &&
+ command == ex_ptr->ex_addr) {
+ do_pty = ex_ptr->ex_pty;
+ goto do_exec;
+ }
+ }
+
+ /*
+ * Nothing bound..
+ */
+ /* tcp_fconnect(so); */
+
+ /* FALLTHROUGH */
+ case CTL_ALIAS:
+ sb->sb_cc = sprintf(sb->sb_wptr,
+ "Error: No application configured.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return(0);
+
+ do_exec:
+ DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec));
+ return(fork_exec(so, ex_ptr->ex_exec, do_pty));
+
+#if 0
+ case CTL_CMD:
+ for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) {
+ if (tmpso->so_emu == EMU_CTL &&
+ !(tmpso->so_tcpcb?
+ (tmpso->so_tcpcb->t_state & (TCPS_TIME_WAIT|TCPS_LAST_ACK))
+ :0)) {
+ /* Ooops, control connection already active */
+ sb->sb_cc = sprintf(sb->sb_wptr,"Sorry, already connected.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+ }
+ }
+ so->so_emu = EMU_CTL;
+ ctl_password_ok = 0;
+ sb->sb_cc = sprintf(sb->sb_wptr, "Slirp command-line ready (type \"help\" for help).\r\nSlirp> ");
+ sb->sb_wptr += sb->sb_cc;
+ do_echo=-1;
+ return(2);
+#endif
+ }
+}
diff --git a/slirp/tcp_timer.c b/slirp/tcp_timer.c
new file mode 100644
index 0000000..d3146db
--- /dev/null
+++ b/slirp/tcp_timer.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include <slirp.h>
+
+int tcp_keepidle = TCPTV_KEEP_IDLE;
+int tcp_keepintvl = TCPTV_KEEPINTVL;
+int tcp_maxidle;
+int so_options = DO_KEEPALIVE;
+
+struct tcpstat tcpstat; /* tcp statistics */
+u_int32_t tcp_now; /* for RFC 1323 timestamps */
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo()
+{
+ register struct socket *so;
+ register struct tcpcb *tp;
+
+ DEBUG_CALL("tcp_fasttimo");
+
+ so = tcb.so_next;
+ if (so)
+ for (; so != &tcb; so = so->so_next)
+ if ((tp = (struct tcpcb *)so->so_tcpcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_delack++;
+ (void) tcp_output(tp);
+ }
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo()
+{
+ register struct socket *ip, *ipnxt;
+ register struct tcpcb *tp;
+ register int i;
+
+ DEBUG_CALL("tcp_slowtimo");
+
+ tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = tcb.so_next;
+ if (ip == 0)
+ return;
+ for (; ip != &tcb; ip = ipnxt) {
+ ipnxt = ip->so_next;
+ tp = sototcpcb(ip);
+ if (tp == 0)
+ continue;
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ tcp_timers(tp,i);
+ if (ipnxt->so_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ }
+ tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
+#ifdef TCP_COMPAT_42
+ if ((int)tcp_iss < 0)
+ tcp_iss = 0; /* XXX */
+#endif
+ tcp_now++; /* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(tp)
+ struct tcpcb *tp;
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+struct tcpcb *
+tcp_timers(tp, timer)
+ register struct tcpcb *tp;
+ int timer;
+{
+ register int rexmt;
+
+ DEBUG_CALL("tcp_timers");
+
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= tcp_maxidle)
+ tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+
+ /*
+ * XXXXX If a packet has timed out, then remove all the queued
+ * packets for that session.
+ */
+
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ /*
+ * This is a hack to suit our terminal server here at the uni of canberra
+ * since they have trouble with zeroes... It usually lets them through
+ * unharmed, but under some conditions, it'll eat the zeros. If we
+ * keep retransmitting it, it'll keep eating the zeroes, so we keep
+ * retransmitting, and eventually the connection dies...
+ * (this only happens on incoming data)
+ *
+ * So, if we were gonna drop the connection from too many retransmits,
+ * don't... instead halve the t_maxseg, which might break up the NULLs and
+ * let them through
+ *
+ * *sigh*
+ */
+
+ tp->t_maxseg >>= 1;
+ if (tp->t_maxseg < 32) {
+ /*
+ * We tried our best, now the connection must die!
+ */
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tcpstat.tcps_timeoutdrop++;
+ tp = tcp_drop(tp, tp->t_softerror);
+ /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+ return (tp); /* XXX */
+ }
+
+ /*
+ * Set rxtshift to 6, which is still at the maximum
+ * backoff time
+ */
+ tp->t_rxtshift = 6;
+ }
+ tcpstat.tcps_rexmttimeo++;
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+/* in_losing(tp->t_inpcb); */
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshold size.
+ * For a threshold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Persistence timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcpstat.tcps_persisttimeo++;
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ tcpstat.tcps_keeptimeo++;
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+
+/* if (tp->t_socket->so_options & SO_KEEPALIVE && */
+ if ((so_options) && tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
+ /*
+ * The keepalive packet must have nonzero length
+ * to get a 4.2 host to respond.
+ */
+ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ } else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ break;
+
+ dropit:
+ tcpstat.tcps_keepdrops++;
+ tp = tcp_drop(tp, 0); /* ETIMEDOUT); */
+ break;
+ }
+
+ return (tp);
+}
diff --git a/slirp/tcp_timer.h b/slirp/tcp_timer.h
new file mode 100644
index 0000000..59933bc
--- /dev/null
+++ b/slirp/tcp_timer.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistence */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
+
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+/* #define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) */ /* max allowable REXMT value */
+#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+
+#ifdef TCPTIMERS
+char *tcptimers[] =
+ { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+extern int tcp_keepidle; /* time before keepalive probes begin */
+extern int tcp_keepintvl; /* time between keepalive probes */
+extern int tcp_maxidle; /* time to drop after starting probes */
+extern int tcp_ttl; /* time to live for TCP segs */
+extern int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo _P((void));
+void tcp_slowtimo _P((void));
+void tcp_canceltimers _P((struct tcpcb *));
+struct tcpcb * tcp_timers _P((register struct tcpcb *, int));
+
+#endif
diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h
new file mode 100644
index 0000000..0d6cd24
--- /dev/null
+++ b/slirp/tcp_var.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+#if SIZEOF_CHAR_P == 4
+ typedef struct tcpiphdr *tcpiphdrp_32;
+#else
+ typedef u_int32_t tcpiphdrp_32;
+#endif
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+ tcpiphdrp_32 seg_next; /* sequencing queue */
+ tcpiphdrp_32 seg_prev;
+ short t_state; /* state of this connection */
+ short t_timer[TCPT_NTIMERS]; /* tcp timers */
+ short t_rxtshift; /* log(2) of rexmt exp. backoff */
+ short t_rxtcur; /* current retransmit value */
+ short t_dupacks; /* consecutive dup acks recd */
+ u_short t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ u_short t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ /* Make it static for now */
+/* struct tcpiphdr *t_template; / * skeletal packet for transmit */
+ struct tcpiphdr t_template;
+
+ struct socket *t_socket; /* back pointer to socket */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ u_int32_t snd_wnd; /* send window */
+/* receive sequence variables */
+ u_int32_t rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+/* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+ u_int32_t snd_cwnd; /* congestion-controlled window */
+ u_int32_t snd_ssthresh; /* snd_cwnd size threshold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ short t_idle; /* inactivity time */
+ short t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ short t_srtt; /* smoothed round-trip time */
+ short t_rttvar; /* variance in round-trip time */
+ u_short t_rttmin; /* minimum rtt allowed */
+ u_int32_t max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ u_char snd_scale; /* window scaling for send window */
+ u_char rcv_scale; /* window scaling for recv window */
+ u_char request_r_scale; /* pending window scaling */
+ u_char requested_s_scale;
+ u_int32_t ts_recent; /* timestamp echo data */
+ u_int32_t ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+};
+
+#define sototcpcb(so) ((so)->so_tcpcb)
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+/* XXX
+ * We want to avoid doing m_pullup on incoming packets but that
+ * means avoiding dtom on the tcp reassembly code. That in turn means
+ * keeping an mbuf pointer in the reassembly queue (since we might
+ * have a cluster). As a quick hack, the source & destination
+ * port numbers (which are no longer needed once we've located the
+ * tcpcb) are overlayed with an mbuf pointer.
+ */
+#if SIZEOF_CHAR_P == 4
+typedef struct mbuf *mbufp_32;
+#else
+typedef u_int32_t mbufp_32;
+#endif
+#define REASS_MBUF(ti) (*(mbufp_32 *)&((ti)->ti_t))
+
+/*
+ * TCP statistics.
+ * Many of these should be kept per connection,
+ * but that's inconvenient at the moment.
+ */
+struct tcpstat {
+ u_long tcps_connattempt; /* connections initiated */
+ u_long tcps_accepts; /* connections accepted */
+ u_long tcps_connects; /* connections established */
+ u_long tcps_drops; /* connections dropped */
+ u_long tcps_conndrops; /* embryonic connections dropped */
+ u_long tcps_closed; /* conn. closed (includes drops) */
+ u_long tcps_segstimed; /* segs where we tried to get rtt */
+ u_long tcps_rttupdated; /* times we succeeded */
+ u_long tcps_delack; /* delayed acks sent */
+ u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */
+ u_long tcps_rexmttimeo; /* retransmit timeouts */
+ u_long tcps_persisttimeo; /* persist timeouts */
+ u_long tcps_keeptimeo; /* keepalive timeouts */
+ u_long tcps_keepprobe; /* keepalive probes sent */
+ u_long tcps_keepdrops; /* connections dropped in keepalive */
+
+ u_long tcps_sndtotal; /* total packets sent */
+ u_long tcps_sndpack; /* data packets sent */
+ u_long tcps_sndbyte; /* data bytes sent */
+ u_long tcps_sndrexmitpack; /* data packets retransmitted */
+ u_long tcps_sndrexmitbyte; /* data bytes retransmitted */
+ u_long tcps_sndacks; /* ack-only packets sent */
+ u_long tcps_sndprobe; /* window probes sent */
+ u_long tcps_sndurg; /* packets sent with URG only */
+ u_long tcps_sndwinup; /* window update-only packets sent */
+ u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */
+
+ u_long tcps_rcvtotal; /* total packets received */
+ u_long tcps_rcvpack; /* packets received in sequence */
+ u_long tcps_rcvbyte; /* bytes received in sequence */
+ u_long tcps_rcvbadsum; /* packets received with ccksum errs */
+ u_long tcps_rcvbadoff; /* packets received with bad offset */
+/* u_long tcps_rcvshort; */ /* packets received too short */
+ u_long tcps_rcvduppack; /* duplicate-only packets received */
+ u_long tcps_rcvdupbyte; /* duplicate-only bytes received */
+ u_long tcps_rcvpartduppack; /* packets with some duplicate data */
+ u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */
+ u_long tcps_rcvoopack; /* out-of-order packets received */
+ u_long tcps_rcvoobyte; /* out-of-order bytes received */
+ u_long tcps_rcvpackafterwin; /* packets with data after window */
+ u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */
+ u_long tcps_rcvafterclose; /* packets rcvd after "close" */
+ u_long tcps_rcvwinprobe; /* rcvd window probe packets */
+ u_long tcps_rcvdupack; /* rcvd duplicate acks */
+ u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */
+ u_long tcps_rcvackpack; /* rcvd ack packets */
+ u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */
+ u_long tcps_rcvwinupd; /* rcvd window update packets */
+/* u_long tcps_pawsdrop; */ /* segments dropped due to PAWS */
+ u_long tcps_predack; /* times hdr predict ok for acks */
+ u_long tcps_preddat; /* times hdr predict ok for data pkts */
+ u_long tcps_socachemiss; /* tcp_last_so misses */
+ u_long tcps_didnuttin; /* Times tcp_output didn't do anything XXX */
+};
+
+extern struct tcpstat tcpstat; /* tcp statistics */
+extern u_int32_t tcp_now; /* for RFC 1323 timestamps */
+
+#endif
diff --git a/slirp/tcpip.h b/slirp/tcpip.h
new file mode 100644
index 0000000..82708b0
--- /dev/null
+++ b/slirp/tcpip.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+#define ti_next ti_i.ih_next
+#define ti_prev ti_i.ih_prev
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2 {
+ struct tcpiphdr dummy;
+ char first_char;
+};
+
+#endif
diff --git a/slirp/tftp.c b/slirp/tftp.c
new file mode 100644
index 0000000..c9946d6
--- /dev/null
+++ b/slirp/tftp.c
@@ -0,0 +1,333 @@
+/*
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+
+struct tftp_session {
+ int in_use;
+ unsigned char filename[TFTP_FILENAME_MAX];
+
+ struct in_addr client_ip;
+ u_int16_t client_port;
+
+ int timestamp;
+};
+
+struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+
+const char *tftp_prefix;
+
+static void tftp_session_update(struct tftp_session *spt)
+{
+ spt->timestamp = curtime;
+ spt->in_use = 1;
+}
+
+static void tftp_session_terminate(struct tftp_session *spt)
+{
+ spt->in_use = 0;
+}
+
+static int tftp_session_allocate(struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &tftp_sessions[k];
+
+ if (!spt->in_use)
+ goto found;
+
+ /* sessions time out after 5 inactive seconds */
+ if ((int)(curtime - spt->timestamp) > 5000)
+ goto found;
+ }
+
+ return -1;
+
+ found:
+ memset(spt, 0, sizeof(*spt));
+ memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
+ spt->client_port = tp->udp.uh_sport;
+
+ tftp_session_update(spt);
+
+ return k;
+}
+
+static int tftp_session_find(struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &tftp_sessions[k];
+
+ if (spt->in_use) {
+ if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
+ if (spt->client_port == tp->udp.uh_sport) {
+ return k;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
+ u_int8_t *buf, int len)
+{
+ int fd;
+ int bytes_read = 0;
+
+ fd = open(spt->filename, O_RDONLY | O_BINARY);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (len) {
+ lseek(fd, block_nr * 512, SEEK_SET);
+
+ bytes_read = read(fd, buf, len);
+ }
+
+ close(fd);
+
+ return bytes_read;
+}
+
+static int tftp_send_error(struct tftp_session *spt,
+ u_int16_t errorcode, const char *msg,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int nobytes;
+
+ m = m_get();
+
+ if (!m) {
+ return -1;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += if_maxlinkhdr;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_ERROR);
+ tp->x.tp_error.tp_error_code = htons(errorcode);
+ strcpy(tp->x.tp_error.tp_msg, msg);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ nobytes = 2;
+
+ m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ tftp_session_terminate(spt);
+
+ return 0;
+}
+
+static int tftp_send_data(struct tftp_session *spt,
+ u_int16_t block_nr,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int nobytes;
+
+ if (block_nr < 1) {
+ return -1;
+ }
+
+ m = m_get();
+
+ if (!m) {
+ return -1;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += if_maxlinkhdr;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_DATA);
+ tp->x.tp_data.tp_block_nr = htons(block_nr);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
+
+ if (nobytes < 0) {
+ m_free(m);
+
+ /* send "file not found" error back */
+
+ tftp_send_error(spt, 1, "File not found", tp);
+
+ return -1;
+ }
+
+ m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ if (nobytes == 512) {
+ tftp_session_update(spt);
+ }
+ else {
+ tftp_session_terminate(spt);
+ }
+
+ return 0;
+}
+
+static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
+{
+ struct tftp_session *spt;
+ int s, k, n;
+ u_int8_t *src, *dst;
+
+ s = tftp_session_allocate(tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ spt = &tftp_sessions[s];
+
+ src = tp->x.tp_buf;
+ dst = spt->filename;
+ n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
+
+ /* get name */
+
+ for (k = 0; k < n; k++) {
+ if (k < TFTP_FILENAME_MAX) {
+ dst[k] = src[k];
+ }
+ else {
+ return;
+ }
+
+ if (src[k] == '\0') {
+ break;
+ }
+ }
+
+ if (k >= n) {
+ return;
+ }
+
+ k++;
+
+ /* check mode */
+ if ((n - k) < 6) {
+ return;
+ }
+
+ if (memcmp(&src[k], "octet\0", 6) != 0) {
+ tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
+ return;
+ }
+
+ /* do sanity checks on the filename */
+
+ if ((spt->filename[0] != '/')
+ || (spt->filename[strlen(spt->filename) - 1] == '/')
+ || strstr(spt->filename, "/../")) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* only allow exported prefixes */
+
+ if (!tftp_prefix
+ || (strncmp(spt->filename, tftp_prefix, strlen(tftp_prefix)) != 0)) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* check if the file exists */
+
+ if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
+ tftp_send_error(spt, 1, "File not found", tp);
+ return;
+ }
+
+ tftp_send_data(spt, 1, tp);
+}
+
+static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
+{
+ int s;
+
+ s = tftp_session_find(tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ if (tftp_send_data(&tftp_sessions[s],
+ ntohs(tp->x.tp_data.tp_block_nr) + 1,
+ tp) < 0) {
+ return;
+ }
+}
+
+void tftp_input(struct mbuf *m)
+{
+ struct tftp_t *tp = (struct tftp_t *)m->m_data;
+
+ switch(ntohs(tp->tp_op)) {
+ case TFTP_RRQ:
+ tftp_handle_rrq(tp, m->m_len);
+ break;
+
+ case TFTP_ACK:
+ tftp_handle_ack(tp, m->m_len);
+ break;
+ }
+}
diff --git a/slirp/tftp.h b/slirp/tftp.h
new file mode 100644
index 0000000..f0560b6
--- /dev/null
+++ b/slirp/tftp.h
@@ -0,0 +1,32 @@
+/* tftp defines */
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER 69
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+
+#define TFTP_FILENAME_MAX 512
+
+struct tftp_t {
+ struct ip ip;
+ struct udphdr udp;
+ u_int16_t tp_op;
+ union {
+ struct {
+ u_int16_t tp_block_nr;
+ u_int8_t tp_buf[512];
+ } tp_data;
+ struct {
+ u_int16_t tp_error_code;
+ u_int8_t tp_msg[512];
+ } tp_error;
+ u_int8_t tp_buf[512 + 2];
+ } x;
+};
+
+void tftp_input(struct mbuf *m);
diff --git a/slirp/udp.c b/slirp/udp.c
new file mode 100644
index 0000000..0d70970
--- /dev/null
+++ b/slirp/udp.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+struct udpstat udpstat;
+
+struct socket udb;
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#ifndef COMPAT_42
+int udpcksum = 1;
+#else
+int udpcksum = 0; /* XXX */
+#endif
+
+struct socket *udp_last_so = &udb;
+
+void
+udp_init()
+{
+ udb.so_next = udb.so_prev = &udb;
+}
+/* m->m_data points at ip packet header
+ * m->m_len length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct ip *ip;
+ register struct udphdr *uh;
+/* struct mbuf *opts = 0;*/
+ int len;
+ struct ip save_ip;
+ struct socket *so;
+
+ DEBUG_CALL("udp_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("iphlen = %d", iphlen);
+
+ udpstat.udps_ipackets++;
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if(iphlen > sizeof(struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((u_int16_t)uh->uh_ulen);
+
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ udpstat.udps_badlen++;
+ goto bad;
+ }
+ m_adj(m, len - ip->ip_len);
+ ip->ip_len = len;
+ }
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (udpcksum && uh->uh_sum) {
+ ((struct ipovly *)ip)->ih_next = 0;
+ ((struct ipovly *)ip)->ih_prev = 0;
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ /* keep uh_sum for ICMP reply
+ * uh->uh_sum = cksum(m, len + sizeof (struct ip));
+ * if (uh->uh_sum) {
+ */
+ if(cksum(m, len + sizeof(struct ip))) {
+ udpstat.udps_badsum++;
+ goto bad;
+ }
+ }
+
+ /*
+ * handle DHCP/BOOTP
+ */
+ if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
+ bootp_input(m);
+ goto bad;
+ }
+
+ /*
+ * handle TFTP
+ */
+ if (ntohs(uh->uh_dport) == TFTP_SERVER) {
+ tftp_input(m);
+ goto bad;
+ }
+
+ /*
+ * Locate pcb for datagram.
+ */
+ so = udp_last_so;
+ if (so->so_lport != uh->uh_sport ||
+ so->so_laddr.s_addr != ip->ip_src.s_addr) {
+ struct socket *tmp;
+
+ for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) {
+ if (tmp->so_lport == uh->uh_sport &&
+ tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
+ tmp->so_faddr.s_addr = ip->ip_dst.s_addr;
+ tmp->so_fport = uh->uh_dport;
+ so = tmp;
+ break;
+ }
+ }
+ if (tmp == &udb) {
+ so = NULL;
+ } else {
+ udpstat.udpps_pcbcachemiss++;
+ udp_last_so = so;
+ }
+ }
+
+ if (so == NULL) {
+ /*
+ * If there's no socket for this packet,
+ * create one
+ */
+ if ((so = socreate()) == NULL) goto bad;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ goto bad;
+ }
+
+ /*
+ * Setup fields
+ */
+ /* udp_last_so = so; */
+ so->so_laddr = ip->ip_src;
+ so->so_lport = uh->uh_sport;
+ so->so_faddr = ip->ip_dst; /* XXX */
+ so->so_fport = uh->uh_dport; /* XXX */
+
+ if ((so->so_iptos = udp_tos(so)) == 0)
+ so->so_iptos = ip->ip_tos;
+
+ /*
+ * XXXXX Here, check if it's in udpexec_list,
+ * and if it is, do the fork_exec() etc.
+ */
+ }
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ /*
+ * Now we sendto() the packet.
+ */
+ if (so->so_emu)
+ udp_emu(so, m);
+
+ if(sosendto(so,m) == -1) {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ }
+
+ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ so->so_m=m; /* ICMP backup */
+
+ return;
+bad:
+ m_freem(m);
+ /* if (opts) m_freem(opts); */
+ return;
+}
+
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos)
+{
+ register struct udpiphdr *ui;
+ int error = 0;
+
+ DEBUG_CALL("udp_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
+ DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
+
+ /*
+ * Adjust for header
+ */
+ m->m_data -= sizeof(struct udpiphdr);
+ m->m_len += sizeof(struct udpiphdr);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ ui->ui_next = ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */
+ /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+ ui->ui_src = saddr->sin_addr;
+ ui->ui_dst = daddr->sin_addr;
+ ui->ui_sport = saddr->sin_port;
+ ui->ui_dport = daddr->sin_port;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if (udpcksum) {
+ if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0)
+ ui->ui_sum = 0xffff;
+ }
+ ((struct ip *)ui)->ip_len = m->m_len;
+
+ ((struct ip *)ui)->ip_ttl = ip_defttl;
+ ((struct ip *)ui)->ip_tos = iptos;
+
+ udpstat.udps_opackets++;
+
+ error = ip_output(so, m);
+
+ return (error);
+}
+
+int udp_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *addr)
+
+{
+ struct sockaddr_in saddr, daddr;
+
+ saddr = *addr;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ saddr.sin_addr.s_addr = so->so_faddr.s_addr;
+ if ((so->so_faddr.s_addr & htonl(0x000000ff)) == htonl(0xff))
+ saddr.sin_addr.s_addr = alias_addr.s_addr;
+ }
+ daddr.sin_addr = so->so_laddr;
+ daddr.sin_port = so->so_lport;
+
+ return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(so)
+ struct socket *so;
+{
+ struct sockaddr_in addr;
+
+ if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) {
+ /*
+ * Here, we bind() the socket. Although not really needed
+ * (sendto() on an unbound socket will bind it), it's done
+ * here so that emulation of ytalk etc. don't have to do it
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if(bind(so->s, (struct sockaddr *)&addr, sizeof(addr))<0) {
+ int lasterrno=errno;
+ closesocket(so->s);
+ so->s=-1;
+#ifdef _WIN32
+ WSASetLastError(lasterrno);
+#else
+ errno=lasterrno;
+#endif
+ } else {
+ /* success, insert in queue */
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so,&udb);
+ }
+ }
+ return(so->s);
+}
+
+void
+udp_detach(so)
+ struct socket *so;
+{
+ closesocket(so->s);
+ /* if (so->so_m) m_free(so->so_m); done by sofree */
+
+ sofree(so);
+}
+
+struct tos_t udptos[] = {
+ {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
+ {517, 517, IPTOS_LOWDELAY, EMU_TALK}, /* talk */
+ {518, 518, IPTOS_LOWDELAY, EMU_NTALK}, /* ntalk */
+ {0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME}, /* Cu-Seeme */
+ {0, 0, 0, 0}
+};
+
+u_int8_t
+udp_tos(so)
+ struct socket *so;
+{
+ int i = 0;
+
+ while(udptos[i].tos) {
+ if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
+ (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
+ so->so_emu = udptos[i].emu;
+ return udptos[i].tos;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+#ifdef EMULATE_TALK
+#include "talkd.h"
+#endif
+
+/*
+ * Here, talk/ytalk/ntalk requests must be emulated
+ */
+void
+udp_emu(so, m)
+ struct socket *so;
+ struct mbuf *m;
+{
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+#ifdef EMULATE_TALK
+ CTL_MSG_OLD *omsg;
+ CTL_MSG *nmsg;
+ char buff[sizeof(CTL_MSG)];
+ u_char type;
+
+struct talk_request {
+ struct talk_request *next;
+ struct socket *udp_so;
+ struct socket *tcp_so;
+} *req;
+
+ static struct talk_request *req_tbl = 0;
+
+#endif
+
+struct cu_header {
+ uint16_t d_family; // destination family
+ uint16_t d_port; // destination port
+ uint32_t d_addr; // destination address
+ uint16_t s_family; // source family
+ uint16_t s_port; // source port
+ uint32_t so_addr; // source address
+ uint32_t seqn; // sequence number
+ uint16_t message; // message
+ uint16_t data_type; // data type
+ uint16_t pkt_len; // packet length
+} *cu_head;
+
+ switch(so->so_emu) {
+
+#ifdef EMULATE_TALK
+ case EMU_TALK:
+ case EMU_NTALK:
+ /*
+ * Talk emulation. We always change the ctl_addr to get
+ * some answers from the daemon. When an ANNOUNCE comes,
+ * we send LEAVE_INVITE to the local daemons. Also when a
+ * DELETE comes, we send copies to the local daemons.
+ */
+ if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
+ return;
+
+#define IS_OLD (so->so_emu == EMU_TALK)
+
+#define COPY_MSG(dest, src) { dest->type = src->type; \
+ dest->id_num = src->id_num; \
+ dest->pid = src->pid; \
+ dest->addr = src->addr; \
+ dest->ctl_addr = src->ctl_addr; \
+ memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \
+ memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \
+ memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); }
+
+#define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field)
+/* old_sockaddr to sockaddr_in */
+
+
+ if (IS_OLD) { /* old talk */
+ omsg = mtod(m, CTL_MSG_OLD*);
+ nmsg = (CTL_MSG *) buff;
+ type = omsg->type;
+ OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_addr = our_addr;
+ strncpy(omsg->l_name, getlogin(), NAME_SIZE_OLD);
+ } else { /* new talk */
+ omsg = (CTL_MSG_OLD *) buff;
+ nmsg = mtod(m, CTL_MSG *);
+ type = nmsg->type;
+ OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port;
+ OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr;
+ strncpy(nmsg->l_name, getlogin(), NAME_SIZE_OLD);
+ }
+
+ if (type == LOOK_UP)
+ return; /* for LOOK_UP this is enough */
+
+ if (IS_OLD) { /* make a copy of the message */
+ COPY_MSG(nmsg, omsg);
+ nmsg->vers = 1;
+ nmsg->answer = 0;
+ } else
+ COPY_MSG(omsg, nmsg);
+
+ /*
+ * If if is an ANNOUNCE message, we go through the
+ * request table to see if a tcp port has already
+ * been redirected for this socket. If not, we solisten()
+ * a new socket and add this entry to the table.
+ * The port number of the tcp socket and our IP
+ * are put to the addr field of the message structures.
+ * Then a LEAVE_INVITE is sent to both local daemon
+ * ports, 517 and 518. This is why we have two copies
+ * of the message, one in old talk and one in new talk
+ * format.
+ */
+
+ if (type == ANNOUNCE) {
+ int s;
+ u_short temp_port;
+
+ for(req = req_tbl; req; req = req->next)
+ if (so == req->udp_so)
+ break; /* found it */
+
+ if (!req) { /* no entry for so, create new */
+ req = (struct talk_request *)
+ malloc(sizeof(struct talk_request));
+ req->udp_so = so;
+ req->tcp_so = solisten(0,
+ OTOSIN(omsg, addr)->sin_addr.s_addr,
+ OTOSIN(omsg, addr)->sin_port,
+ SS_FACCEPTONCE);
+ req->next = req_tbl;
+ req_tbl = req;
+ }
+
+ /* replace port number in addr field */
+ addrlen = sizeof(addr);
+ getsockname(req->tcp_so->s,
+ (struct sockaddr *) &addr,
+ &addrlen);
+ OTOSIN(omsg, addr)->sin_port = addr.sin_port;
+ OTOSIN(omsg, addr)->sin_addr = our_addr;
+ OTOSIN(nmsg, addr)->sin_port = addr.sin_port;
+ OTOSIN(nmsg, addr)->sin_addr = our_addr;
+
+ /* send LEAVE_INVITEs */
+ temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_port = 0;
+ OTOSIN(nmsg, ctl_addr)->sin_port = 0;
+ omsg->type = nmsg->type = LEAVE_INVITE;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ addr.sin_addr = our_addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(517);
+ sendto(s, (char *)omsg, sizeof(*omsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ addr.sin_port = htons(518);
+ sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
+ (struct sockaddr *) &addr, sizeof(addr));
+ closesocket(s) ;
+
+ omsg->type = nmsg->type = ANNOUNCE;
+ OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
+ OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
+ }
+
+ /*
+ * If it is a DELETE message, we send a copy to the
+ * local daemons. Then we delete the entry corresponding
+ * to our socket from the request table.
+ */
+
+ if (type == DELETE) {
+ struct talk_request *temp_req, *req_next;
+ int s;
+ u_short temp_port;
+
+ temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_port = 0;
+ OTOSIN(nmsg, ctl_addr)->sin_port = 0;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ addr.sin_addr = our_addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(517);
+ sendto(s, (char *)omsg, sizeof(*omsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ addr.sin_port = htons(518);
+ sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ closesocket(s);
+
+ OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
+ OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
+
+ /* delete table entry */
+ if (so == req_tbl->udp_so) {
+ temp_req = req_tbl;
+ req_tbl = req_tbl->next;
+ free(temp_req);
+ } else {
+ temp_req = req_tbl;
+ for(req = req_tbl->next; req; req = req_next) {
+ req_next = req->next;
+ if (so == req->udp_so) {
+ temp_req->next = req_next;
+ free(req);
+ break;
+ } else {
+ temp_req = req;
+ }
+ }
+ }
+ }
+
+ return;
+#endif
+
+ case EMU_CUSEEME:
+
+ /*
+ * Cu-SeeMe emulation.
+ * Hopefully the packet is more that 16 bytes long. We don't
+ * do any other tests, just replace the address and port
+ * fields.
+ */
+ if (m->m_len >= sizeof (*cu_head)) {
+ if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
+ return;
+ cu_head = mtod(m, struct cu_header *);
+ cu_head->s_port = addr.sin_port;
+ cu_head->so_addr = our_addr.s_addr;
+ }
+
+ return;
+ }
+}
+
+struct socket *
+udp_listen(port, laddr, lport, flags)
+ u_int port;
+ u_int32_t laddr;
+ u_int lport;
+ int flags;
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ int addrlen = sizeof(struct sockaddr_in), opt = 1;
+
+ if ((so = socreate()) == NULL) {
+ free(so);
+ return NULL;
+ }
+ so->s = socket(AF_INET,SOCK_DGRAM,0);
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so,&udb);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = port;
+
+ if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
+ udp_detach(so);
+ return NULL;
+ }
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+/* setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); */
+
+ getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->so_lport = lport;
+ so->so_laddr.s_addr = laddr;
+ if (flags != SS_FACCEPTONCE)
+ so->so_expire = 0;
+
+ so->so_state = SS_ISFCONNECTED;
+
+ return so;
+}
diff --git a/slirp/udp.h b/slirp/udp.h
new file mode 100644
index 0000000..24c11bb
--- /dev/null
+++ b/slirp/udp.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+extern struct socket *udp_last_so;
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ u_int16_t uh_sport; /* source port */
+ u_int16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ u_int16_t uh_sum; /* udp checksum */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_next ui_i.ih_next
+#define ui_prev ui_i.ih_prev
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+struct udpstat {
+ /* input statistics: */
+ u_long udps_ipackets; /* total input packets */
+ u_long udps_hdrops; /* packet shorter than header */
+ u_long udps_badsum; /* checksum error */
+ u_long udps_badlen; /* data length larger than packet */
+ u_long udps_noport; /* no socket on port */
+ u_long udps_noportbcast; /* of above, arrived as broadcast */
+ u_long udps_fullsock; /* not delivered, input socket full */
+ u_long udpps_pcbcachemiss; /* input packets missing pcb cache */
+ /* output statistics: */
+ u_long udps_opackets; /* total output packets */
+};
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+extern struct udpstat udpstat;
+extern struct socket udb;
+struct mbuf;
+
+void udp_init _P((void));
+void udp_input _P((register struct mbuf *, int));
+int udp_output _P((struct socket *, struct mbuf *, struct sockaddr_in *));
+int udp_attach _P((struct socket *));
+void udp_detach _P((struct socket *));
+u_int8_t udp_tos _P((struct socket *));
+void udp_emu _P((struct socket *, struct mbuf *));
+struct socket * udp_listen _P((u_int, u_int32_t, u_int, int));
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos);
+#endif
diff --git a/softmmu_exec.h b/softmmu_exec.h
new file mode 100644
index 0000000..3b789ee
--- /dev/null
+++ b/softmmu_exec.h
@@ -0,0 +1,65 @@
+/* Common softmmu definitions and inline routines. */
+
+#define ldul_user ldl_user
+#define ldul_kernel ldl_kernel
+
+#define ACCESS_TYPE 0
+#define MEMSUFFIX _kernel
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ACCESS_TYPE 1
+#define MEMSUFFIX _user
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+/* these access are slower, they must be as rare as possible */
+#define ACCESS_TYPE 2
+#define MEMSUFFIX _data
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ldub(p) ldub_data(p)
+#define ldsb(p) ldsb_data(p)
+#define lduw(p) lduw_data(p)
+#define ldsw(p) ldsw_data(p)
+#define ldl(p) ldl_data(p)
+#define ldq(p) ldq_data(p)
+
+#define stb(p, v) stb_data(p, v)
+#define stw(p, v) stw_data(p, v)
+#define stl(p, v) stl_data(p, v)
+#define stq(p, v) stq_data(p, v)
diff --git a/softmmu_header.h b/softmmu_header.h
new file mode 100644
index 0000000..d5b3deb
--- /dev/null
+++ b/softmmu_header.h
@@ -0,0 +1,385 @@
+/*
+ * Software MMU support
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#define DATA_STYPE int16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#define DATA_STYPE int8_t
+#else
+#error unsupported data size
+#endif
+
+#if ACCESS_TYPE == 0
+
+#define CPU_MEM_INDEX 0
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == 1
+
+#define CPU_MEM_INDEX 1
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == 2
+
+#ifdef TARGET_I386
+#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
+#elif defined (TARGET_PPC)
+#define CPU_MEM_INDEX (msr_pr)
+#elif defined (TARGET_MIPS)
+#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
+#elif defined (TARGET_SPARC)
+#define CPU_MEM_INDEX ((env->psrs) == 0)
+#elif defined (TARGET_ARM)
+#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
+#elif defined (TARGET_SH4)
+#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0)
+#else
+#error unsupported CPU
+#endif
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == 3
+
+#ifdef TARGET_I386
+#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
+#elif defined (TARGET_PPC)
+#define CPU_MEM_INDEX (msr_pr)
+#elif defined (TARGET_MIPS)
+#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
+#elif defined (TARGET_SPARC)
+#define CPU_MEM_INDEX ((env->psrs) == 0)
+#elif defined (TARGET_ARM)
+#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)
+#elif defined (TARGET_SH4)
+#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0)
+#else
+#error unsupported CPU
+#endif
+#define MMUSUFFIX _cmmu
+
+#else
+#error invalid ACCESS_TYPE
+#endif
+
+#if DATA_SIZE == 8
+#define RES_TYPE uint64_t
+#else
+#define RES_TYPE int
+#endif
+
+#if ACCESS_TYPE == 3
+#define ADDR_READ addr_code
+#else
+#define ADDR_READ addr_read
+#endif
+
+DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int is_user);
+void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE v, int is_user);
+
+#if (DATA_SIZE <= 4) && (TARGET_LONG_BITS == 32) && defined(__i386__) && \
+ (ACCESS_TYPE <= 1) && defined(ASM_SOFTMMU)
+
+#define CPU_TLB_ENTRY_BITS 4
+
+static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int res;
+
+ asm volatile ("movl %1, %%edx\n"
+ "movl %1, %%eax\n"
+ "shrl %3, %%edx\n"
+ "andl %4, %%eax\n"
+ "andl %2, %%edx\n"
+ "leal %5(%%edx, %%ebp), %%edx\n"
+ "cmpl (%%edx), %%eax\n"
+ "movl %1, %%eax\n"
+ "je 1f\n"
+ "pushl %6\n"
+ "call %7\n"
+ "popl %%edx\n"
+ "movl %%eax, %0\n"
+ "jmp 2f\n"
+ "1:\n"
+ "addl 12(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+ "movzbl (%%eax), %0\n"
+#elif DATA_SIZE == 2
+ "movzwl (%%eax), %0\n"
+#elif DATA_SIZE == 4
+ "movl (%%eax), %0\n"
+#else
+#error unsupported size
+#endif
+ "2:\n"
+ : "=r" (res)
+ : "r" (ptr),
+ "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+ "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_read)),
+ "i" (CPU_MEM_INDEX),
+ "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX))
+ : "%eax", "%ecx", "%edx", "memory", "cc");
+ return res;
+}
+
+#if DATA_SIZE <= 2
+static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int res;
+
+ asm volatile ("movl %1, %%edx\n"
+ "movl %1, %%eax\n"
+ "shrl %3, %%edx\n"
+ "andl %4, %%eax\n"
+ "andl %2, %%edx\n"
+ "leal %5(%%edx, %%ebp), %%edx\n"
+ "cmpl (%%edx), %%eax\n"
+ "movl %1, %%eax\n"
+ "je 1f\n"
+ "pushl %6\n"
+ "call %7\n"
+ "popl %%edx\n"
+#if DATA_SIZE == 1
+ "movsbl %%al, %0\n"
+#elif DATA_SIZE == 2
+ "movswl %%ax, %0\n"
+#else
+#error unsupported size
+#endif
+ "jmp 2f\n"
+ "1:\n"
+ "addl 12(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+ "movsbl (%%eax), %0\n"
+#elif DATA_SIZE == 2
+ "movswl (%%eax), %0\n"
+#else
+#error unsupported size
+#endif
+ "2:\n"
+ : "=r" (res)
+ : "r" (ptr),
+ "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+ "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_read)),
+ "i" (CPU_MEM_INDEX),
+ "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX))
+ : "%eax", "%ecx", "%edx", "memory", "cc");
+ return res;
+}
+#endif
+
+static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v)
+{
+ asm volatile ("movl %0, %%edx\n"
+ "movl %0, %%eax\n"
+ "shrl %3, %%edx\n"
+ "andl %4, %%eax\n"
+ "andl %2, %%edx\n"
+ "leal %5(%%edx, %%ebp), %%edx\n"
+ "cmpl (%%edx), %%eax\n"
+ "movl %0, %%eax\n"
+ "je 1f\n"
+#if DATA_SIZE == 1
+ "movzbl %b1, %%edx\n"
+#elif DATA_SIZE == 2
+ "movzwl %w1, %%edx\n"
+#elif DATA_SIZE == 4
+ "movl %1, %%edx\n"
+#else
+#error unsupported size
+#endif
+ "pushl %6\n"
+ "call %7\n"
+ "popl %%eax\n"
+ "jmp 2f\n"
+ "1:\n"
+ "addl 8(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+ "movb %b1, (%%eax)\n"
+#elif DATA_SIZE == 2
+ "movw %w1, (%%eax)\n"
+#elif DATA_SIZE == 4
+ "movl %1, (%%eax)\n"
+#else
+#error unsupported size
+#endif
+ "2:\n"
+ :
+ : "r" (ptr),
+/* NOTE: 'q' would be needed as constraint, but we could not use it
+ with T1 ! */
+ "r" (v),
+ "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+ "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+ "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_write)),
+ "i" (CPU_MEM_INDEX),
+ "m" (*(uint8_t *)&glue(glue(__st, SUFFIX), MMUSUFFIX))
+ : "%eax", "%ecx", "%edx", "memory", "cc");
+}
+
+#else
+
+/* generic load/store macros */
+
+static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int index;
+ RES_TYPE res;
+ target_ulong addr;
+ unsigned long physaddr;
+ int is_user;
+
+ addr = ptr;
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ is_user = CPU_MEM_INDEX;
+ if (__builtin_expect(env->tlb_table[is_user][index].ADDR_READ !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) {
+ res = glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, is_user);
+ } else {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
+ }
+ return res;
+}
+
+#if DATA_SIZE <= 2
+static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int res, index;
+ target_ulong addr;
+ unsigned long physaddr;
+ int is_user;
+
+ addr = ptr;
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ is_user = CPU_MEM_INDEX;
+ if (__builtin_expect(env->tlb_table[is_user][index].ADDR_READ !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) {
+ res = (DATA_STYPE)glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, is_user);
+ } else {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ res = glue(glue(lds, SUFFIX), _raw)((uint8_t *)physaddr);
+ }
+ return res;
+}
+#endif
+
+#if ACCESS_TYPE != 3
+
+/* generic store macro */
+
+static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v)
+{
+ int index;
+ target_ulong addr;
+ unsigned long physaddr;
+ int is_user;
+
+ addr = ptr;
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ is_user = CPU_MEM_INDEX;
+ if (__builtin_expect(env->tlb_table[is_user][index].addr_write !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) {
+ glue(glue(__st, SUFFIX), MMUSUFFIX)(addr, v, is_user);
+ } else {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, v);
+ }
+}
+
+#endif /* ACCESS_TYPE != 3 */
+
+#endif /* !asm */
+
+#if ACCESS_TYPE != 3
+
+#if DATA_SIZE == 8
+static inline float64 glue(ldfq, MEMSUFFIX)(target_ulong ptr)
+{
+ union {
+ float64 d;
+ uint64_t i;
+ } u;
+ u.i = glue(ldq, MEMSUFFIX)(ptr);
+ return u.d;
+}
+
+static inline void glue(stfq, MEMSUFFIX)(target_ulong ptr, float64 v)
+{
+ union {
+ float64 d;
+ uint64_t i;
+ } u;
+ u.d = v;
+ glue(stq, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 8 */
+
+#if DATA_SIZE == 4
+static inline float32 glue(ldfl, MEMSUFFIX)(target_ulong ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = glue(ldl, MEMSUFFIX)(ptr);
+ return u.f;
+}
+
+static inline void glue(stfl, MEMSUFFIX)(target_ulong ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ glue(stl, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 4 */
+
+#endif /* ACCESS_TYPE != 3 */
+
+#undef RES_TYPE
+#undef DATA_TYPE
+#undef DATA_STYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef CPU_MEM_INDEX
+#undef MMUSUFFIX
+#undef ADDR_READ
diff --git a/softmmu_template.h b/softmmu_template.h
new file mode 100644
index 0000000..1c12c42
--- /dev/null
+++ b/softmmu_template.h
@@ -0,0 +1,313 @@
+/*
+ * Software MMU support
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define DATA_SIZE (1 << SHIFT)
+
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#else
+#error unsupported data size
+#endif
+
+#ifdef SOFTMMU_CODE_ACCESS
+#define READ_ACCESS_TYPE 2
+#define ADDR_READ addr_code
+#else
+#define READ_ACCESS_TYPE 0
+#define ADDR_READ addr_read
+#endif
+
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int is_user,
+ void *retaddr);
+static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
+ target_ulong tlb_addr)
+{
+ DATA_TYPE res;
+ int index;
+
+ index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+#if SHIFT <= 2
+ res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+ res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32;
+ res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4);
+#else
+ res = io_mem_read[index][2](io_mem_opaque[index], physaddr);
+ res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32;
+#endif
+#endif /* SHIFT > 2 */
+#ifdef USE_KQEMU
+ env->last_io_time = cpu_get_time_fast();
+#endif
+ return res;
+}
+
+/* handle all cases except unaligned access which span two pages */
+DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int is_user)
+{
+ DATA_TYPE res;
+ int index;
+ target_ulong tlb_addr;
+ target_phys_addr_t physaddr;
+ void *retaddr;
+
+ /* test if there is match for unaligned or IO access */
+ /* XXX: could done more in memory macro in a non portable way */
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[is_user][index].ADDR_READ;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ /* slow unaligned access (it spans two pages or IO) */
+ do_unaligned_access:
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
+#endif
+ res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr,
+ is_user, retaddr);
+ } else {
+ /* unaligned/aligned access in the same page */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ retaddr = GETPC();
+ do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
+ }
+#endif
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
+#endif
+ tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
+ goto redo;
+ }
+ return res;
+}
+
+/* handle all unaligned cases */
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int is_user,
+ void *retaddr)
+{
+ DATA_TYPE res, res1, res2;
+ int index, shift;
+ target_phys_addr_t physaddr;
+ target_ulong tlb_addr, addr1, addr2;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[is_user][index].ADDR_READ;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ /* slow unaligned access (it spans two pages) */
+ addr1 = addr & ~(DATA_SIZE - 1);
+ addr2 = addr1 + DATA_SIZE;
+ res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1,
+ is_user, retaddr);
+ res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2,
+ is_user, retaddr);
+ shift = (addr & (DATA_SIZE - 1)) * 8;
+#ifdef TARGET_WORDS_BIGENDIAN
+ res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
+#else
+ res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
+#endif
+ res = (DATA_TYPE)res;
+ } else {
+ /* unaligned/aligned access in the same page */
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
+ goto redo;
+ }
+ return res;
+}
+
+#ifndef SOFTMMU_CODE_ACCESS
+
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int is_user,
+ void *retaddr);
+
+static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
+ DATA_TYPE val,
+ target_ulong tlb_addr,
+ void *retaddr)
+{
+ int index;
+
+ index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ env->mem_write_vaddr = tlb_addr;
+ env->mem_write_pc = (unsigned long)retaddr;
+#if SHIFT <= 2
+ io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+ io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
+ io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
+#else
+ io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
+ io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
+#endif
+#endif /* SHIFT > 2 */
+#ifdef USE_KQEMU
+ env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int is_user)
+{
+ target_phys_addr_t physaddr;
+ target_ulong tlb_addr;
+ void *retaddr;
+ int index;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[is_user][index].addr_write;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ retaddr = GETPC();
+ glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(addr, 1, is_user, retaddr);
+#endif
+ glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val,
+ is_user, retaddr);
+ } else {
+ /* aligned/unaligned access in the same page */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ retaddr = GETPC();
+ do_unaligned_access(addr, 1, is_user, retaddr);
+ }
+#endif
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ do_unaligned_access(addr, 1, is_user, retaddr);
+#endif
+ tlb_fill(addr, 1, is_user, retaddr);
+ goto redo;
+ }
+}
+
+/* handles all unaligned cases */
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int is_user,
+ void *retaddr)
+{
+ target_phys_addr_t physaddr;
+ target_ulong tlb_addr;
+ int index, i;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[is_user][index].addr_write;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = addr + env->tlb_table[is_user][index].addend;
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ /* XXX: not efficient, but simple */
+ for(i = 0;i < DATA_SIZE; i++) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)),
+ is_user, retaddr);
+#else
+ glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8),
+ is_user, retaddr);
+#endif
+ }
+ } else {
+ /* aligned/unaligned access in the same page */
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ tlb_fill(addr, 1, is_user, retaddr);
+ goto redo;
+ }
+}
+
+#endif /* !defined(SOFTMMU_CODE_ACCESS) */
+
+#undef READ_ACCESS_TYPE
+#undef SHIFT
+#undef DATA_TYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef ADDR_READ
diff --git a/sparc-dis.c b/sparc-dis.c
new file mode 100644
index 0000000..2be874a
--- /dev/null
+++ b/sparc-dis.c
@@ -0,0 +1,3265 @@
+/* Print SPARC instructions.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2002 Free Software Foundation, Inc.
+
+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. */
+#include <stdlib.h>
+#include "dis-asm.h"
+
+/* The SPARC opcode table (and other related data) is defined in
+ the opcodes library in sparc-opc.c. If you change anything here, make
+ sure you fix up that file, and vice versa. */
+
+ /* FIXME-someday: perhaps the ,a's and such should be embedded in the
+ instruction's name rather than the args. This would make gas faster, pinsn
+ slower, but would mess up some macros a bit. xoxorich. */
+
+/* List of instruction sets variations.
+ These values are such that each element is either a superset of a
+ preceding each one or they conflict in which case SPARC_OPCODE_CONFLICT_P
+ returns non-zero.
+ The values are indices into `sparc_opcode_archs' defined in sparc-opc.c.
+ Don't change this without updating sparc-opc.c. */
+
+enum sparc_opcode_arch_val {
+ SPARC_OPCODE_ARCH_V6 = 0,
+ SPARC_OPCODE_ARCH_V7,
+ SPARC_OPCODE_ARCH_V8,
+ SPARC_OPCODE_ARCH_SPARCLET,
+ SPARC_OPCODE_ARCH_SPARCLITE,
+ /* v9 variants must appear last */
+ SPARC_OPCODE_ARCH_V9,
+ SPARC_OPCODE_ARCH_V9A, /* v9 with ultrasparc additions */
+ SPARC_OPCODE_ARCH_V9B, /* v9 with ultrasparc and cheetah additions */
+ SPARC_OPCODE_ARCH_BAD /* error return from sparc_opcode_lookup_arch */
+};
+
+/* The highest architecture in the table. */
+#define SPARC_OPCODE_ARCH_MAX (SPARC_OPCODE_ARCH_BAD - 1)
+
+/* Given an enum sparc_opcode_arch_val, return the bitmask to use in
+ insn encoding/decoding. */
+#define SPARC_OPCODE_ARCH_MASK(arch) (1 << (arch))
+
+/* Given a valid sparc_opcode_arch_val, return non-zero if it's v9. */
+#define SPARC_OPCODE_ARCH_V9_P(arch) ((arch) >= SPARC_OPCODE_ARCH_V9)
+
+/* Table of cpu variants. */
+
+struct sparc_opcode_arch {
+ const char *name;
+ /* Mask of sparc_opcode_arch_val's supported.
+ EG: For v7 this would be
+ (SPARC_OPCODE_ARCH_MASK (..._V6) | SPARC_OPCODE_ARCH_MASK (..._V7)).
+ These are short's because sparc_opcode.architecture is. */
+ short supported;
+};
+
+extern const struct sparc_opcode_arch sparc_opcode_archs[];
+
+/* Given architecture name, look up it's sparc_opcode_arch_val value. */
+extern enum sparc_opcode_arch_val sparc_opcode_lookup_arch
+ PARAMS ((const char *));
+
+/* Return the bitmask of supported architectures for ARCH. */
+#define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported)
+
+/* Non-zero if ARCH1 conflicts with ARCH2.
+ IE: ARCH1 as a supported bit set that ARCH2 doesn't, and vice versa. */
+#define SPARC_OPCODE_CONFLICT_P(ARCH1, ARCH2) \
+(((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
+ != SPARC_OPCODE_SUPPORTED (ARCH1)) \
+ && ((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
+ != SPARC_OPCODE_SUPPORTED (ARCH2)))
+
+/* Structure of an opcode table entry. */
+
+struct sparc_opcode {
+ const char *name;
+ unsigned long match; /* Bits that must be set. */
+ unsigned long lose; /* Bits that must not be set. */
+ const char *args;
+ /* This was called "delayed" in versions before the flags. */
+ char flags;
+ short architecture; /* Bitmask of sparc_opcode_arch_val's. */
+};
+
+#define F_DELAYED 1 /* Delayed branch */
+#define F_ALIAS 2 /* Alias for a "real" instruction */
+#define F_UNBR 4 /* Unconditional branch */
+#define F_CONDBR 8 /* Conditional branch */
+#define F_JSR 16 /* Subroutine call */
+#define F_FLOAT 32 /* Floating point instruction (not a branch) */
+#define F_FBR 64 /* Floating point branch */
+/* FIXME: Add F_ANACHRONISTIC flag for v9. */
+
+/*
+
+All sparc opcodes are 32 bits, except for the `set' instruction (really a
+macro), which is 64 bits. It is handled as a special case.
+
+The match component is a mask saying which bits must match a particular
+opcode in order for an instruction to be an instance of that opcode.
+
+The args component is a string containing one character for each operand of the
+instruction.
+
+Kinds of operands:
+ # Number used by optimizer. It is ignored.
+ 1 rs1 register.
+ 2 rs2 register.
+ d rd register.
+ e frs1 floating point register.
+ v frs1 floating point register (double/even).
+ V frs1 floating point register (quad/multiple of 4).
+ f frs2 floating point register.
+ B frs2 floating point register (double/even).
+ R frs2 floating point register (quad/multiple of 4).
+ g frsd floating point register.
+ H frsd floating point register (double/even).
+ J frsd floating point register (quad/multiple of 4).
+ b crs1 coprocessor register
+ c crs2 coprocessor register
+ D crsd coprocessor register
+ m alternate space register (asr) in rd
+ M alternate space register (asr) in rs1
+ h 22 high bits.
+ X 5 bit unsigned immediate
+ Y 6 bit unsigned immediate
+ 3 SIAM mode (3 bits). (v9b)
+ K MEMBAR mask (7 bits). (v9)
+ j 10 bit Immediate. (v9)
+ I 11 bit Immediate. (v9)
+ i 13 bit Immediate.
+ n 22 bit immediate.
+ k 2+14 bit PC relative immediate. (v9)
+ G 19 bit PC relative immediate. (v9)
+ l 22 bit PC relative immediate.
+ L 30 bit PC relative immediate.
+ a Annul. The annul bit is set.
+ A Alternate address space. Stored as 8 bits.
+ C Coprocessor state register.
+ F floating point state register.
+ p Processor state register.
+ N Branch predict clear ",pn" (v9)
+ T Branch predict set ",pt" (v9)
+ z %icc. (v9)
+ Z %xcc. (v9)
+ q Floating point queue.
+ r Single register that is both rs1 and rd.
+ O Single register that is both rs2 and rd.
+ Q Coprocessor queue.
+ S Special case.
+ t Trap base register.
+ w Window invalid mask register.
+ y Y register.
+ u sparclet coprocessor registers in rd position
+ U sparclet coprocessor registers in rs1 position
+ E %ccr. (v9)
+ s %fprs. (v9)
+ P %pc. (v9)
+ W %tick. (v9)
+ o %asi. (v9)
+ 6 %fcc0. (v9)
+ 7 %fcc1. (v9)
+ 8 %fcc2. (v9)
+ 9 %fcc3. (v9)
+ ! Privileged Register in rd (v9)
+ ? Privileged Register in rs1 (v9)
+ * Prefetch function constant. (v9)
+ x OPF field (v9 impdep).
+ 0 32/64 bit immediate for set or setx (v9) insns
+ _ Ancillary state register in rd (v9a)
+ / Ancillary state register in rs1 (v9a)
+
+The following chars are unused: (note: ,[] are used as punctuation)
+[45]
+
+*/
+
+#define OP2(x) (((x)&0x7) << 22) /* op2 field of format2 insns */
+#define OP3(x) (((x)&0x3f) << 19) /* op3 field of format3 insns */
+#define OP(x) ((unsigned)((x)&0x3) << 30) /* op field of all insns */
+#define OPF(x) (((x)&0x1ff) << 5) /* opf field of float insns */
+#define OPF_LOW5(x) OPF((x)&0x1f) /* v9 */
+#define F3F(x, y, z) (OP(x) | OP3(y) | OPF(z)) /* format3 float insns */
+#define F3I(x) (((x)&0x1) << 13) /* immediate field of format 3 insns */
+#define F2(x, y) (OP(x) | OP2(y)) /* format 2 insns */
+#define F3(x, y, z) (OP(x) | OP3(y) | F3I(z)) /* format3 insns */
+#define F1(x) (OP(x))
+#define DISP30(x) ((x)&0x3fffffff)
+#define ASI(x) (((x)&0xff) << 5) /* asi field of format3 insns */
+#define RS2(x) ((x)&0x1f) /* rs2 field */
+#define SIMM13(x) ((x)&0x1fff) /* simm13 field */
+#define RD(x) (((x)&0x1f) << 25) /* destination register field */
+#define RS1(x) (((x)&0x1f) << 14) /* rs1 field */
+#define ASI_RS2(x) (SIMM13(x))
+#define MEMBAR(x) ((x)&0x7f)
+#define SLCPOP(x) (((x)&0x7f) << 6) /* sparclet cpop */
+
+#define ANNUL (1<<29)
+#define BPRED (1<<19) /* v9 */
+#define IMMED F3I(1)
+#define RD_G0 RD(~0)
+#define RS1_G0 RS1(~0)
+#define RS2_G0 RS2(~0)
+
+extern const struct sparc_opcode sparc_opcodes[];
+extern const int sparc_num_opcodes;
+
+extern int sparc_encode_asi PARAMS ((const char *));
+extern const char *sparc_decode_asi PARAMS ((int));
+extern int sparc_encode_membar PARAMS ((const char *));
+extern const char *sparc_decode_membar PARAMS ((int));
+extern int sparc_encode_prefetch PARAMS ((const char *));
+extern const char *sparc_decode_prefetch PARAMS ((int));
+extern int sparc_encode_sparclet_cpreg PARAMS ((const char *));
+extern const char *sparc_decode_sparclet_cpreg PARAMS ((int));
+
+/* Some defines to make life easy. */
+#define MASK_V6 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V6)
+#define MASK_V7 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V7)
+#define MASK_V8 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8)
+#define MASK_SPARCLET SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET)
+#define MASK_SPARCLITE SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE)
+#define MASK_V9 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9)
+#define MASK_V9A SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A)
+#define MASK_V9B SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B)
+
+/* Bit masks of architectures supporting the insn. */
+
+#define v6 (MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+/* v6 insns not supported on the sparclet */
+#define v6notlet (MASK_V6 | MASK_V7 | MASK_V8 \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+#define v7 (MASK_V7 | MASK_V8 | MASK_SPARCLET \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+/* Although not all insns are implemented in hardware, sparclite is defined
+ to be a superset of v8. Unimplemented insns trap and are then theoretically
+ implemented in software.
+ It's not clear that the same is true for sparclet, although the docs
+ suggest it is. Rather than complicating things, the sparclet assembler
+ recognizes all v8 insns. */
+#define v8 (MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE \
+ | MASK_V9 | MASK_V9A | MASK_V9B)
+#define sparclet (MASK_SPARCLET)
+#define sparclite (MASK_SPARCLITE)
+#define v9 (MASK_V9 | MASK_V9A | MASK_V9B)
+#define v9a (MASK_V9A | MASK_V9B)
+#define v9b (MASK_V9B)
+/* v6 insns not supported by v9 */
+#define v6notv9 (MASK_V6 | MASK_V7 | MASK_V8 \
+ | MASK_SPARCLET | MASK_SPARCLITE)
+/* v9a instructions which would appear to be aliases to v9's impdep's
+ otherwise */
+#define v9notv9a (MASK_V9)
+
+/* Table of opcode architectures.
+ The order is defined in opcode/sparc.h. */
+
+const struct sparc_opcode_arch sparc_opcode_archs[] = {
+ { "v6", MASK_V6 },
+ { "v7", MASK_V6 | MASK_V7 },
+ { "v8", MASK_V6 | MASK_V7 | MASK_V8 },
+ { "sparclet", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET },
+ { "sparclite", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLITE },
+ /* ??? Don't some v8 priviledged insns conflict with v9? */
+ { "v9", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 },
+ /* v9 with ultrasparc additions */
+ { "v9a", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A },
+ /* v9 with cheetah additions */
+ { "v9b", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A | MASK_V9B },
+ { NULL, 0 }
+};
+
+/* Given NAME, return it's architecture entry. */
+
+enum sparc_opcode_arch_val
+sparc_opcode_lookup_arch (name)
+ const char *name;
+{
+ const struct sparc_opcode_arch *p;
+
+ for (p = &sparc_opcode_archs[0]; p->name; ++p)
+ {
+ if (strcmp (name, p->name) == 0)
+ return (enum sparc_opcode_arch_val) (p - &sparc_opcode_archs[0]);
+ }
+
+ return SPARC_OPCODE_ARCH_BAD;
+}
+
+/* Branch condition field. */
+#define COND(x) (((x)&0xf)<<25)
+
+/* v9: Move (MOVcc and FMOVcc) condition field. */
+#define MCOND(x,i_or_f) ((((i_or_f)&1)<<18)|(((x)>>11)&(0xf<<14))) /* v9 */
+
+/* v9: Move register (MOVRcc and FMOVRcc) condition field. */
+#define RCOND(x) (((x)&0x7)<<10) /* v9 */
+
+#define CONDA (COND(0x8))
+#define CONDCC (COND(0xd))
+#define CONDCS (COND(0x5))
+#define CONDE (COND(0x1))
+#define CONDG (COND(0xa))
+#define CONDGE (COND(0xb))
+#define CONDGU (COND(0xc))
+#define CONDL (COND(0x3))
+#define CONDLE (COND(0x2))
+#define CONDLEU (COND(0x4))
+#define CONDN (COND(0x0))
+#define CONDNE (COND(0x9))
+#define CONDNEG (COND(0x6))
+#define CONDPOS (COND(0xe))
+#define CONDVC (COND(0xf))
+#define CONDVS (COND(0x7))
+
+#define CONDNZ CONDNE
+#define CONDZ CONDE
+#define CONDGEU CONDCC
+#define CONDLU CONDCS
+
+#define FCONDA (COND(0x8))
+#define FCONDE (COND(0x9))
+#define FCONDG (COND(0x6))
+#define FCONDGE (COND(0xb))
+#define FCONDL (COND(0x4))
+#define FCONDLE (COND(0xd))
+#define FCONDLG (COND(0x2))
+#define FCONDN (COND(0x0))
+#define FCONDNE (COND(0x1))
+#define FCONDO (COND(0xf))
+#define FCONDU (COND(0x7))
+#define FCONDUE (COND(0xa))
+#define FCONDUG (COND(0x5))
+#define FCONDUGE (COND(0xc))
+#define FCONDUL (COND(0x3))
+#define FCONDULE (COND(0xe))
+
+#define FCONDNZ FCONDNE
+#define FCONDZ FCONDE
+
+#define ICC (0) /* v9 */
+#define XCC (1<<12) /* v9 */
+#define FCC(x) (((x)&0x3)<<11) /* v9 */
+#define FBFCC(x) (((x)&0x3)<<20) /* v9 */
+
+/* The order of the opcodes in the table is significant:
+
+ * The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler will bomb at runtime.
+
+ * The disassembler should not care about the order of the opcodes.
+
+*/
+
+/* Entries for commutative arithmetic operations. */
+/* ??? More entries can make use of this. */
+#define COMMUTEOP(opcode, op3, arch_mask) \
+{ opcode, F3(2, op3, 0), F3(~2, ~op3, ~0)|ASI(~0), "1,2,d", 0, arch_mask }, \
+{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "1,i,d", 0, arch_mask }, \
+{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "i,1,d", 0, arch_mask }
+
+const struct sparc_opcode sparc_opcodes[] = {
+
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 },
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0), "[1+2],g", 0, v6 },
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0)|RS2_G0, "[1],g", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[1+i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[i+1],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|RS1_G0, "[i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ld [rs1+0],d */
+
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RD(~0), "[1+2],F", 0, v6 },
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RS2_G0|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[1+i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[i+1],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~0),"[i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+0],d */
+
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1],D", 0, v6notv9 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0), "[1+2],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0)|RS2_G0, "[1],C", 0, v6notv9 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[1+i],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[i+1],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|RS1_G0, "[i],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|SIMM13(~0), "[1],C", 0, v6notv9 }, /* ld [rs1+0],d */
+
+/* The v9 LDUW is the same as the old 'ld' opcode, it is not the same as the
+ 'ld' pseudo-op in v9. */
+{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", F_ALIAS, v9 }, /* ld [rs1+%g0],d */
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", F_ALIAS, v9 }, /* ld [rs1+0],d */
+
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[1+i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[i+1],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldd [rs1+0],d */
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI(~0), "[1+2],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI_RS2(~0), "[1],H", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[1+i],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[i+1],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|RS1_G0, "[i],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|SIMM13(~0), "[1],H", 0, v6 }, /* ldd [rs1+0],d */
+
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI(~0), "[1+2],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI_RS2(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+0],d */
+
+{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI(~0), "[1+2],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI_RS2(~0), "[1],J", 0, v9 }, /* ldd [rs1+%g0],d */
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[1+i],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[i+1],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|RS1_G0, "[i],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|SIMM13(~0), "[1],J", 0, v9 }, /* ldd [rs1+0],d */
+
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsb [rs1+%g0],d */
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[1+i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[i+1],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsb [rs1+0],d */
+
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsh [rs1+%g0],d */
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[1+i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[i+1],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsh [rs1+0],d */
+
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldstub [rs1+%g0],d */
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[1+i],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[i+1],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldstub [rs1+0],d */
+
+{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI(~0), "[1+2],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldsw [rs1+%g0],d */
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[1+i],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[i+1],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|RS1_G0, "[i],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldsw [rs1+0],d */
+
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldub [rs1+%g0],d */
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[1+i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[i+1],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldub [rs1+0],d */
+
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* lduh [rs1+%g0],d */
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[1+i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[i+1],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* lduh [rs1+0],d */
+
+{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI(~0), "[1+2],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldx [rs1+%g0],d */
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[1+i],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[i+1],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|RS1_G0, "[i],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldx [rs1+0],d */
+
+{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RD(~1), "[1+2],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RS2_G0|RD(~1), "[1],F", 0, v9 }, /* ld [rs1+%g0],d */
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[1+i],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[i+1],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~1), "[i],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~1),"[1],F", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", 0, v6 },
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lda [rs1+%g0],d */
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2]A,g", 0, v9 },
+{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1]A,g", 0, v9 }, /* lda [rs1+%g0],d */
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1]o,g", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0), "[1+2]A,d", 0, v6 },
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldda [rs1+%g0],d */
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[1+i]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[i+1]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0), "[1+2]A,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|RS2_G0, "[1]A,H", 0, v9 }, /* ldda [rs1+%g0],d */
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1]o,H", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0), "[1+2]A,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0)|RS2_G0, "[1]A,J", 0, v9 }, /* ldd [rs1+%g0],d */
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[1+i]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[i+1]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|RS1_G0, "[i]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|SIMM13(~0), "[1]o,J", 0, v9 }, /* ldd [rs1+0],d */
+
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsba [rs1+%g0],d */
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[1+i]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[i+1]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsha [rs1+%g0],d */
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[1+i]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[i+1]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0), "[1+2]A,d", 0, v6 },
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldstuba [rs1+%g0],d */
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[1+i]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[i+1]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0), "[1+2]A,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[1+i]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[i+1]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0), "[1+2]A,d", 0, v6 },
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduba [rs1+%g0],d */
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[1+i]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[i+1]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0), "[1+2]A,d", 0, v6 },
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduha [rs1+%g0],d */
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[1+i]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[i+1]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", F_ALIAS, v9 }, /* lduwa === lda */
+{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", F_ALIAS, v9 }, /* lda [rs1+%g0],d */
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", F_ALIAS, v9 }, /* ld [rs1+0],d */
+
+{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0), "[1+2]A,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[1+i]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[i+1]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI(~0), "g,[1+2]", 0, v6 },
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* st d[rs1+%g0] */
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[1+i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[i+1]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|RS1_G0, "g,[i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[1+i]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[i+1]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "D,[i]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI(~0), "C,[1+2]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI_RS2(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[1+i]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[i+1]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|RS1_G0, "C,[i]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|SIMM13(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+0] */
+
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI(~0), "F,[1+2]", 0, v6 },
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI_RS2(~0), "F,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[1+i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[i+1]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|RS1_G0, "F,[i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|SIMM13(~0), "F,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+
+{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+%g0] */
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+0] */
+
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", 0, v6 },
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* sta d,[rs1+%g0] */
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* st d,[rs1+0] */
+
+{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0), "g,[1+2]A", 0, v9 },
+{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|RS2(~0), "g,[1]A", 0, v9 }, /* sta d,[rs1+%g0] */
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[1+i]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[i+1]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "g,[i]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "g,[1]o", 0, v9 }, /* st d,[rs1+0] */
+
+{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+%g0] */
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+0] */
+
+{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */
+{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */
+
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", 0, v6 },
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stba d,[rs1+%g0] */
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stb d,[rs1+0] */
+
+{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */
+{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */
+
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* std d,[rs1+0] */
+
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "q,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "q,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI(~0), "H,[1+2]", 0, v6 },
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI_RS2(~0), "H,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[1+i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[i+1]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|RS1_G0, "H,[i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|SIMM13(~0), "H,[1]", 0, v6 }, /* std d,[rs1+0] */
+
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "Q,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "Q,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "D,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+
+{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+%g0] */
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+0] */
+
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0), "d,[1+2]A", 0, v6 },
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stda d,[rs1+%g0] */
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[1+i]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[i+1]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* std d,[rs1+0] */
+{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0), "H,[1+2]A", 0, v9 },
+{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|RS2(~0), "H,[1]A", 0, v9 }, /* stda d,[rs1+%g0] */
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[1+i]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[i+1]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "H,[i]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "H,[1]o", 0, v9 }, /* std d,[rs1+0] */
+
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+%g0] */
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+0] */
+
+{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */
+{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */
+
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", 0, v6 },
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stha ,[rs1+%g0] */
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* sth d,[rs1+0] */
+
+{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */
+{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */
+
+{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI(~0), "d,[1+2]", 0, v9 },
+{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI_RS2(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+%g0] */
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[1+i]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[i+1]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RS1_G0, "d,[i]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|SIMM13(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI(~0)|RD(~1), "F,[1+2]", 0, v9 },
+{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI_RS2(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+%g0] */
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[1+i]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[i+1]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RS1_G0|RD(~1), "F,[i]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|SIMM13(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0), "d,[1+2]A", 0, v9 },
+{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0)|RS2(~0), "d,[1]A", 0, v9 }, /* stxa d,[rs1+%g0] */
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[1+i]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[i+1]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "J,[1+2]", 0, v9 },
+{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "J,[1]", 0, v9 }, /* stq [rs1+%g0] */
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[1+i]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[i+1]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "J,[i]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "J,[1]", 0, v9 }, /* stq [rs1+0] */
+
+{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "J,[1+2]A", 0, v9 },
+{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "J,[1]A", 0, v9 }, /* stqa [rs1+%g0] */
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[1+i]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[i+1]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "J,[i]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "J,[1]o", 0, v9 }, /* stqa [rs1+0] */
+
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI(~0), "[1+2],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI_RS2(~0), "[1],d", 0, v7 }, /* swap [rs1+%g0],d */
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[1+i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[i+1],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|RS1_G0, "[i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|SIMM13(~0), "[1],d", 0, v7 }, /* swap [rs1+0],d */
+
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0), "[1+2]A,d", 0, v7 },
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0)|RS2(~0), "[1]A,d", 0, v7 }, /* swapa [rs1+%g0],d */
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[1+i]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[i+1]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* swap [rs1+0],d */
+
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v6 }, /* restore %g0,%g0,%g0 */
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1), "1,i,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1)|RD_G0|RS1_G0|SIMM13(~0), "", 0, v6 }, /* restore %g0,0,%g0 */
+
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* rett rs1+rs2 */
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1,%g0 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* rett rs1+X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1+0 */
+
+{ "save", F3(2, 0x3c, 0), F3(~2, ~0x3c, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "save", F3(2, 0x3c, 1), F3(~2, ~0x3c, ~1), "1,i,d", 0, v6 },
+{ "save", 0x81e00000, ~0x81e00000, "", F_ALIAS, v6 },
+
+{ "ret", F3(2, 0x38, 1)|RS1(0x1f)|SIMM13(8), F3(~2, ~0x38, ~1)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %i7+8,%g0 */
+{ "retl", F3(2, 0x38, 1)|RS1(0x0f)|SIMM13(8), F3(~2, ~0x38, ~1)|RS1(~0x0f)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %o7+8,%g0 */
+
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI(~0), "1+2,d", F_JSR|F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI_RS2(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|SIMM13(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "1+i,d", F_JSR|F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "i+1,d", F_JSR|F_DELAYED, v6 },
+
+{ "done", F3(2, 0x3e, 0)|RD(0), F3(~2, ~0x3e, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "retry", F3(2, 0x3e, 0)|RD(1), F3(~2, ~0x3e, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "saved", F3(2, 0x31, 0)|RD(0), F3(~2, ~0x31, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "restored", F3(2, 0x31, 0)|RD(1), F3(~2, ~0x31, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "sir", F3(2, 0x30, 1)|RD(0xf), F3(~2, ~0x30, ~1)|RD(~0xf)|RS1_G0, "i", 0, v9 },
+
+{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", 0, v8 },
+{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", 0, v8 }, /* flush rs1+%g0 */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", 0, v8 }, /* flush rs1+0 */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", 0, v8 }, /* flush %g0+i */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", 0, v8 },
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", 0, v8 },
+
+/* IFLUSH was renamed to FLUSH in v8. */
+{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", F_ALIAS, v6 }, /* flush rs1+%g0 */
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", F_ALIAS, v6 }, /* flush rs1+0 */
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", F_ALIAS, v6 },
+
+{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI(~0), "1+2", 0, v9 },
+{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI_RS2(~0), "1", 0, v9 }, /* return rs1+%g0 */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|SIMM13(~0), "1", 0, v9 }, /* return rs1+0 */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RS1_G0, "i", 0, v9 }, /* return %g0+i */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "1+i", 0, v9 },
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "i+1", 0, v9 },
+
+{ "flushw", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v9 },
+
+{ "membar", F3(2, 0x28, 1)|RS1(0xf), F3(~2, ~0x28, ~1)|RD_G0|RS1(~0xf)|SIMM13(~127), "K", 0, v9 },
+{ "stbar", F3(2, 0x28, 0)|RS1(0xf), F3(~2, ~0x28, ~0)|RD_G0|RS1(~0xf)|SIMM13(~0), "", 0, v8 },
+
+{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0), "[1+2],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0)|RS2_G0, "[1],*", 0, v9 }, /* prefetch [rs1+%g0],prefetch_fcn */
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[1+i],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[i+1],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|RS1_G0, "[i],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|SIMM13(~0), "[1],*", 0, v9 }, /* prefetch [rs1+0],prefetch_fcn */
+{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0), "[1+2]A,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0)|RS2_G0, "[1]A,*", 0, v9 }, /* prefetcha [rs1+%g0],prefetch_fcn */
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[1+i]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[i+1]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|RS1_G0, "[i]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|SIMM13(~0), "[1]o,*", 0, v9 }, /* prefetcha [rs1+0],d */
+
+{ "sll", F3(2, 0x25, 0), F3(~2, ~0x25, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "sll", F3(2, 0x25, 1), F3(~2, ~0x25, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+{ "sra", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "sra", F3(2, 0x27, 1), F3(~2, ~0x27, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+{ "srl", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "srl", F3(2, 0x26, 1), F3(~2, ~0x26, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+
+{ "sllx", F3(2, 0x25, 0)|(1<<12), F3(~2, ~0x25, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "sllx", F3(2, 0x25, 1)|(1<<12), F3(~2, ~0x25, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+{ "srax", F3(2, 0x27, 0)|(1<<12), F3(~2, ~0x27, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "srax", F3(2, 0x27, 1)|(1<<12), F3(~2, ~0x27, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+{ "srlx", F3(2, 0x26, 0)|(1<<12), F3(~2, ~0x26, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "srlx", F3(2, 0x26, 1)|(1<<12), F3(~2, ~0x26, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+
+{ "mulscc", F3(2, 0x24, 0), F3(~2, ~0x24, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "mulscc", F3(2, 0x24, 1), F3(~2, ~0x24, ~1), "1,i,d", 0, v6 },
+
+{ "divscc", F3(2, 0x1d, 0), F3(~2, ~0x1d, ~0)|ASI(~0), "1,2,d", 0, sparclite },
+{ "divscc", F3(2, 0x1d, 1), F3(~2, ~0x1d, ~1), "1,i,d", 0, sparclite },
+
+{ "scan", F3(2, 0x2c, 0), F3(~2, ~0x2c, ~0)|ASI(~0), "1,2,d", 0, sparclet|sparclite },
+{ "scan", F3(2, 0x2c, 1), F3(~2, ~0x2c, ~1), "1,i,d", 0, sparclet|sparclite },
+
+{ "popc", F3(2, 0x2e, 0), F3(~2, ~0x2e, ~0)|RS1_G0|ASI(~0),"2,d", 0, v9 },
+{ "popc", F3(2, 0x2e, 1), F3(~2, ~0x2e, ~1)|RS1_G0, "i,d", 0, v9 },
+
+{ "clr", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "d", F_ALIAS, v6 }, /* or %g0,%g0,d */
+{ "clr", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0|SIMM13(~0), "d", F_ALIAS, v6 }, /* or %g0,0,d */
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+%g0] */
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+0] */
+
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+%g0] */
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+0] */
+
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+%g0] */
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+0] */
+
+{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+%g0] */
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[1+i]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[i+1]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+0] */
+
+{ "orcc", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "1,i,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "i,1,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "orncc", F3(2, 0x16, 0), F3(~2, ~0x16, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "1,i,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "orn", F3(2, 0x06, 0), F3(~2, ~0x06, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "1,i,d", 0, v6 },
+
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|ASI_RS2(~0), "1", 0, v6 }, /* orcc rs1, %g0, %g0 */
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|RS1_G0|ASI(~0), "2", 0, v6 }, /* orcc %g0, rs2, %g0 */
+{ "tst", F3(2, 0x12, 1), F3(~2, ~0x12, ~1)|RD_G0|SIMM13(~0), "1", 0, v6 }, /* orcc rs1, 0, %g0 */
+
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", 0, v8 }, /* wr r,r,%asrX */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", 0, v8 }, /* wr r,i,%asrX */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", 0, v6 }, /* wr r,r,%y */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", 0, v6 }, /* wr r,i,%y */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */
+{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", 0, v6notv9 }, /* wr r,r,%psr */
+{ "wr", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", 0, v6notv9 }, /* wr r,i,%psr */
+{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */
+{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", 0, v6notv9 }, /* wr r,r,%wim */
+{ "wr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", 0, v6notv9 }, /* wr r,i,%wim */
+{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */
+{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", 0, v6notv9 }, /* wr r,r,%tbr */
+{ "wr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", 0, v6notv9 }, /* wr r,i,%tbr */
+{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */
+
+{ "wr", F3(2, 0x30, 0)|RD(2), F3(~2, ~0x30, ~0)|RD(~2)|ASI(~0), "1,2,E", 0, v9 }, /* wr r,r,%ccr */
+{ "wr", F3(2, 0x30, 1)|RD(2), F3(~2, ~0x30, ~1)|RD(~2), "1,i,E", 0, v9 }, /* wr r,i,%ccr */
+{ "wr", F3(2, 0x30, 0)|RD(3), F3(~2, ~0x30, ~0)|RD(~3)|ASI(~0), "1,2,o", 0, v9 }, /* wr r,r,%asi */
+{ "wr", F3(2, 0x30, 1)|RD(3), F3(~2, ~0x30, ~1)|RD(~3), "1,i,o", 0, v9 }, /* wr r,i,%asi */
+{ "wr", F3(2, 0x30, 0)|RD(6), F3(~2, ~0x30, ~0)|RD(~6)|ASI(~0), "1,2,s", 0, v9 }, /* wr r,r,%fprs */
+{ "wr", F3(2, 0x30, 1)|RD(6), F3(~2, ~0x30, ~1)|RD(~6), "1,i,s", 0, v9 }, /* wr r,i,%fprs */
+
+{ "wr", F3(2, 0x30, 0)|RD(16), F3(~2, ~0x30, ~0)|RD(~16)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pcr */
+{ "wr", F3(2, 0x30, 1)|RD(16), F3(~2, ~0x30, ~1)|RD(~16), "1,i,_", 0, v9a }, /* wr r,i,%pcr */
+{ "wr", F3(2, 0x30, 0)|RD(17), F3(~2, ~0x30, ~0)|RD(~17)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pic */
+{ "wr", F3(2, 0x30, 1)|RD(17), F3(~2, ~0x30, ~1)|RD(~17), "1,i,_", 0, v9a }, /* wr r,i,%pic */
+{ "wr", F3(2, 0x30, 0)|RD(18), F3(~2, ~0x30, ~0)|RD(~18)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%dcr */
+{ "wr", F3(2, 0x30, 1)|RD(18), F3(~2, ~0x30, ~1)|RD(~18), "1,i,_", 0, v9a }, /* wr r,i,%dcr */
+{ "wr", F3(2, 0x30, 0)|RD(19), F3(~2, ~0x30, ~0)|RD(~19)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%gsr */
+{ "wr", F3(2, 0x30, 1)|RD(19), F3(~2, ~0x30, ~1)|RD(~19), "1,i,_", 0, v9a }, /* wr r,i,%gsr */
+{ "wr", F3(2, 0x30, 0)|RD(20), F3(~2, ~0x30, ~0)|RD(~20)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%set_softint */
+{ "wr", F3(2, 0x30, 1)|RD(20), F3(~2, ~0x30, ~1)|RD(~20), "1,i,_", 0, v9a }, /* wr r,i,%set_softint */
+{ "wr", F3(2, 0x30, 0)|RD(21), F3(~2, ~0x30, ~0)|RD(~21)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%clear_softint */
+{ "wr", F3(2, 0x30, 1)|RD(21), F3(~2, ~0x30, ~1)|RD(~21), "1,i,_", 0, v9a }, /* wr r,i,%clear_softint */
+{ "wr", F3(2, 0x30, 0)|RD(22), F3(~2, ~0x30, ~0)|RD(~22)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%softint */
+{ "wr", F3(2, 0x30, 1)|RD(22), F3(~2, ~0x30, ~1)|RD(~22), "1,i,_", 0, v9a }, /* wr r,i,%softint */
+{ "wr", F3(2, 0x30, 0)|RD(23), F3(~2, ~0x30, ~0)|RD(~23)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%tick_cmpr */
+{ "wr", F3(2, 0x30, 1)|RD(23), F3(~2, ~0x30, ~1)|RD(~23), "1,i,_", 0, v9a }, /* wr r,i,%tick_cmpr */
+{ "wr", F3(2, 0x30, 0)|RD(24), F3(~2, ~0x30, ~0)|RD(~24)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick */
+{ "wr", F3(2, 0x30, 1)|RD(24), F3(~2, ~0x30, ~1)|RD(~24), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick */
+{ "wr", F3(2, 0x30, 0)|RD(25), F3(~2, ~0x30, ~0)|RD(~25)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick_cmpr */
+{ "wr", F3(2, 0x30, 1)|RD(25), F3(~2, ~0x30, ~1)|RD(~25), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick_cmpr */
+
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", 0, v8 }, /* rd %asrX,r */
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", 0, v6 }, /* rd %y,r */
+{ "rd", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", 0, v6notv9 }, /* rd %psr,r */
+{ "rd", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", 0, v6notv9 }, /* rd %wim,r */
+{ "rd", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", 0, v6notv9 }, /* rd %tbr,r */
+
+{ "rd", F3(2, 0x28, 0)|RS1(2), F3(~2, ~0x28, ~0)|RS1(~2)|SIMM13(~0), "E,d", 0, v9 }, /* rd %ccr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(3), F3(~2, ~0x28, ~0)|RS1(~3)|SIMM13(~0), "o,d", 0, v9 }, /* rd %asi,r */
+{ "rd", F3(2, 0x28, 0)|RS1(4), F3(~2, ~0x28, ~0)|RS1(~4)|SIMM13(~0), "W,d", 0, v9 }, /* rd %tick,r */
+{ "rd", F3(2, 0x28, 0)|RS1(5), F3(~2, ~0x28, ~0)|RS1(~5)|SIMM13(~0), "P,d", 0, v9 }, /* rd %pc,r */
+{ "rd", F3(2, 0x28, 0)|RS1(6), F3(~2, ~0x28, ~0)|RS1(~6)|SIMM13(~0), "s,d", 0, v9 }, /* rd %fprs,r */
+
+{ "rd", F3(2, 0x28, 0)|RS1(16), F3(~2, ~0x28, ~0)|RS1(~16)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pcr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(17), F3(~2, ~0x28, ~0)|RS1(~17)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pic,r */
+{ "rd", F3(2, 0x28, 0)|RS1(18), F3(~2, ~0x28, ~0)|RS1(~18)|SIMM13(~0), "/,d", 0, v9a }, /* rd %dcr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(19), F3(~2, ~0x28, ~0)|RS1(~19)|SIMM13(~0), "/,d", 0, v9a }, /* rd %gsr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(22), F3(~2, ~0x28, ~0)|RS1(~22)|SIMM13(~0), "/,d", 0, v9a }, /* rd %softint,r */
+{ "rd", F3(2, 0x28, 0)|RS1(23), F3(~2, ~0x28, ~0)|RS1(~23)|SIMM13(~0), "/,d", 0, v9a }, /* rd %tick_cmpr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(24), F3(~2, ~0x28, ~0)|RS1(~24)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick,r */
+{ "rd", F3(2, 0x28, 0)|RS1(25), F3(~2, ~0x28, ~0)|RS1(~25)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick_cmpr,r */
+
+{ "rdpr", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|SIMM13(~0), "?,d", 0, v9 }, /* rdpr %priv,r */
+{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0), "1,2,!", 0, v9 }, /* wrpr r1,r2,%priv */
+{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|SIMM13(~0), "1,!", 0, v9 }, /* wrpr r1,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "1,i,!", 0, v9 }, /* wrpr r1,i,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "i,1,!", F_ALIAS, v9 }, /* wrpr i,r1,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RS1(~0), "i,!", 0, v9 }, /* wrpr i,%priv */
+
+/* ??? This group seems wrong. A three operand move? */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", F_ALIAS, v8 }, /* wr r,r,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", F_ALIAS, v8 }, /* wr r,i,%asrX */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", F_ALIAS, v6 }, /* wr r,r,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", F_ALIAS, v6 }, /* wr r,i,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", F_ALIAS, v6notv9 }, /* wr r,r,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", F_ALIAS, v6notv9 }, /* wr r,i,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", F_ALIAS, v6notv9 }, /* wr r,r,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", F_ALIAS, v6notv9 }, /* wr r,i,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", F_ALIAS, v6notv9 }, /* wr r,r,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", F_ALIAS, v6notv9 }, /* wr r,i,%tbr */
+
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", F_ALIAS, v8 }, /* rd %asr1,r */
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", F_ALIAS, v6 }, /* rd %y,r */
+{ "mov", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", F_ALIAS, v6notv9 }, /* rd %psr,r */
+{ "mov", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", F_ALIAS, v6notv9 }, /* rd %wim,r */
+{ "mov", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", F_ALIAS, v6notv9 }, /* rd %tbr,r */
+
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "i,m", F_ALIAS, v8 }, /* wr %g0,i,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|SIMM13(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,0,%asrX */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "i,y", F_ALIAS, v6 }, /* wr %g0,i,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0|SIMM13(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,0,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "i,p", F_ALIAS, v6notv9 }, /* wr %g0,i,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0|SIMM13(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,0,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "i,w", F_ALIAS, v6notv9 }, /* wr %g0,i,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0|SIMM13(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,0,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "i,t", F_ALIAS, v6notv9 }, /* wr %g0,i,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0|SIMM13(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,0,%tbr */
+
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RS1_G0|ASI(~0), "2,d", 0, v6 }, /* or %g0,rs2,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0, "i,d", 0, v6 }, /* or %g0,i,d */
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI_RS2(~0), "1,d", 0, v6 }, /* or rs1,%g0,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|SIMM13(~0), "1,d", 0, v6 }, /* or rs1,0,d */
+
+{ "or", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "1,i,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,1,d", 0, v6 },
+
+{ "bset", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* or rd,rs2,rd */
+{ "bset", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,r", F_ALIAS, v6 }, /* or rd,i,rd */
+
+/* This is not a commutative instruction. */
+{ "andn", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "1,i,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "andncc", F3(2, 0x15, 0), F3(~2, ~0x15, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "1,i,d", 0, v6 },
+
+{ "bclr", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* andn rd,rs2,rd */
+{ "bclr", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,r", F_ALIAS, v6 }, /* andn rd,i,rd */
+
+{ "cmp", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|RD_G0|ASI(~0), "1,2", 0, v6 }, /* subcc rs1,rs2,%g0 */
+{ "cmp", F3(2, 0x14, 1), F3(~2, ~0x14, ~1)|RD_G0, "1,i", 0, v6 }, /* subcc rs1,i,%g0 */
+
+{ "sub", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "sub", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "1,i,d", 0, v6 },
+
+{ "subcc", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "subcc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "1,i,d", 0, v6 },
+
+{ "subx", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "subx", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v6notv9 },
+{ "subc", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "subc", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v9 },
+
+{ "subxcc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "subxcc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v6notv9 },
+{ "subccc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "subccc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v9 },
+
+{ "and", F3(2, 0x01, 0), F3(~2, ~0x01, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "1,i,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "i,1,d", 0, v6 },
+
+{ "andcc", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "1,i,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "i,1,d", 0, v6 },
+
+{ "dec", F3(2, 0x04, 1)|SIMM13(0x1), F3(~2, ~0x04, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* sub rd,1,rd */
+{ "dec", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "i,r", F_ALIAS, v8 }, /* sub rd,imm,rd */
+{ "deccc", F3(2, 0x14, 1)|SIMM13(0x1), F3(~2, ~0x14, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* subcc rd,1,rd */
+{ "deccc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "i,r", F_ALIAS, v8 }, /* subcc rd,imm,rd */
+{ "inc", F3(2, 0x00, 1)|SIMM13(0x1), F3(~2, ~0x00, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* add rd,1,rd */
+{ "inc", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,r", F_ALIAS, v8 }, /* add rd,imm,rd */
+{ "inccc", F3(2, 0x10, 1)|SIMM13(0x1), F3(~2, ~0x10, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* addcc rd,1,rd */
+{ "inccc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,r", F_ALIAS, v8 }, /* addcc rd,imm,rd */
+
+{ "btst", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|RD_G0|ASI(~0), "1,2", F_ALIAS, v6 }, /* andcc rs1,rs2,%g0 */
+{ "btst", F3(2, 0x11, 1), F3(~2, ~0x11, ~1)|RD_G0, "i,1", F_ALIAS, v6 }, /* andcc rs1,i,%g0 */
+
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "2,d", F_ALIAS, v6 }, /* sub %g0,rs2,rd */
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "O", F_ALIAS, v6 }, /* sub %g0,rd,rd */
+
+{ "add", F3(2, 0x00, 0), F3(~2, ~0x00, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "1,i,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,1,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 0), F3(~2, ~0x10, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "1,i,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,1,d", 0, v6 },
+
+{ "addx", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v6notv9 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v6notv9 },
+{ "addc", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v9 },
+{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v9 },
+
+{ "addxcc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v6notv9 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v6notv9 },
+{ "addccc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v9 },
+{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v9 },
+
+{ "smul", F3(2, 0x0b, 0), F3(~2, ~0x0b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "1,i,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "i,1,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 0), F3(~2, ~0x1b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "1,i,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "i,1,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 0), F3(~2, ~0x0a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "1,i,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "i,1,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 0), F3(~2, ~0x1a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "1,i,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "i,1,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 0), F3(~2, ~0x0f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "1,i,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "i,1,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 0), F3(~2, ~0x1f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "1,i,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "i,1,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 0), F3(~2, ~0x0e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "1,i,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "i,1,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 0), F3(~2, ~0x1e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "1,i,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "i,1,d", 0, v8 },
+
+{ "mulx", F3(2, 0x09, 0), F3(~2, ~0x09, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "mulx", F3(2, 0x09, 1), F3(~2, ~0x09, ~1), "1,i,d", 0, v9 },
+{ "sdivx", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "sdivx", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, v9 },
+{ "udivx", F3(2, 0x0d, 0), F3(~2, ~0x0d, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "udivx", F3(2, 0x0d, 1), F3(~2, ~0x0d, ~1), "1,i,d", 0, v9 },
+
+{ "call", F1(0x1), F1(~0x1), "L", F_JSR|F_DELAYED, v6 },
+{ "call", F1(0x1), F1(~0x1), "L,#", F_JSR|F_DELAYED, v6 },
+
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%o7 */
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%o7 */
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+i,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1", F_JSR|F_DELAYED, v6 }, /* jmpl i+rs1,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1,#", F_JSR|F_DELAYED, v6 },
+
+
+/* Conditional instructions.
+
+ Because this part of the table was such a mess earlier, I have
+ macrofied it so that all the branches and traps are generated from
+ a single-line description of each condition value. John Gilmore. */
+
+/* Define branches -- one annulled, one without, etc. */
+#define br(opcode, mask, lose, flags) \
+ { opcode, (mask)|ANNUL, (lose), ",a l", (flags), v6 }, \
+ { opcode, (mask) , (lose)|ANNUL, "l", (flags), v6 }
+
+#define brx(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), "Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), ",T Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a,T Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20), ANNUL|BPRED|(lose), ",N Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|ANNUL, BPRED|(lose), ",a,N Z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), "z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), ",T z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a,T z,G", (flags), v9 }, \
+ { opcode, (mask), ANNUL|BPRED|(lose)|(2<<20), ",N z,G", (flags), v9 }, \
+ { opcode, (mask)|ANNUL, BPRED|(lose)|(2<<20), ",a,N z,G", (flags), v9 }
+
+/* Define four traps: reg+reg, reg + immediate, immediate alone, reg alone. */
+#define tr(opcode, mask, lose, flags) \
+ { opcode, (mask)|(2<<11)|IMMED, (lose)|RS1_G0, "Z,i", (flags), v9 }, /* %g0 + imm */ \
+ { opcode, (mask)|(2<<11)|IMMED, (lose), "Z,1+i", (flags), v9 }, /* rs1 + imm */ \
+ { opcode, (mask)|(2<<11), IMMED|(lose), "Z,1+2", (flags), v9 }, /* rs1 + rs2 */ \
+ { opcode, (mask)|(2<<11), IMMED|(lose)|RS2_G0, "Z,1", (flags), v9 }, /* rs1 + %g0 */ \
+ { opcode, (mask)|IMMED, (lose)|RS1_G0, "z,i", (flags)|F_ALIAS, v9 }, /* %g0 + imm */ \
+ { opcode, (mask)|IMMED, (lose), "z,1+i", (flags)|F_ALIAS, v9 }, /* rs1 + imm */ \
+ { opcode, (mask), IMMED|(lose), "z,1+2", (flags)|F_ALIAS, v9 }, /* rs1 + rs2 */ \
+ { opcode, (mask), IMMED|(lose)|RS2_G0, "z,1", (flags)|F_ALIAS, v9 }, /* rs1 + %g0 */ \
+ { opcode, (mask)|IMMED, (lose)|RS1_G0, "i", (flags), v6 }, /* %g0 + imm */ \
+ { opcode, (mask)|IMMED, (lose), "1+i", (flags), v6 }, /* rs1 + imm */ \
+ { opcode, (mask), IMMED|(lose), "1+2", (flags), v6 }, /* rs1 + rs2 */ \
+ { opcode, (mask), IMMED|(lose)|RS2_G0, "1", (flags), v6 } /* rs1 + %g0 */
+
+/* v9: We must put `brx' before `br', to ensure that we never match something
+ v9: against an expression unless it is an expression. Otherwise, we end
+ v9: up with undefined symbol tables entries, because they get added, but
+ v9: are not deleted if the pattern fails to match. */
+
+/* Define both branches and traps based on condition mask */
+#define cond(bop, top, mask, flags) \
+ brx(bop, F2(0, 1)|(mask), F2(~0, ~1)|((~mask)&COND(~0)), F_DELAYED|(flags)), /* v9 */ \
+ br(bop, F2(0, 2)|(mask), F2(~0, ~2)|((~mask)&COND(~0)), F_DELAYED|(flags)), \
+ tr(top, F3(2, 0x3a, 0)|(mask), F3(~2, ~0x3a, 0)|((~mask)&COND(~0)), ((flags) & ~(F_UNBR|F_CONDBR)))
+
+/* Define all the conditions, all the branches, all the traps. */
+
+/* Standard branch, trap mnemonics */
+cond ("b", "ta", CONDA, F_UNBR),
+/* Alternative form (just for assembly, not for disassembly) */
+cond ("ba", "t", CONDA, F_UNBR|F_ALIAS),
+
+cond ("bcc", "tcc", CONDCC, F_CONDBR),
+cond ("bcs", "tcs", CONDCS, F_CONDBR),
+cond ("be", "te", CONDE, F_CONDBR),
+cond ("beq", "teq", CONDE, F_CONDBR|F_ALIAS),
+cond ("bg", "tg", CONDG, F_CONDBR),
+cond ("bgt", "tgt", CONDG, F_CONDBR|F_ALIAS),
+cond ("bge", "tge", CONDGE, F_CONDBR),
+cond ("bgeu", "tgeu", CONDGEU, F_CONDBR|F_ALIAS), /* for cc */
+cond ("bgu", "tgu", CONDGU, F_CONDBR),
+cond ("bl", "tl", CONDL, F_CONDBR),
+cond ("blt", "tlt", CONDL, F_CONDBR|F_ALIAS),
+cond ("ble", "tle", CONDLE, F_CONDBR),
+cond ("bleu", "tleu", CONDLEU, F_CONDBR),
+cond ("blu", "tlu", CONDLU, F_CONDBR|F_ALIAS), /* for cs */
+cond ("bn", "tn", CONDN, F_CONDBR),
+cond ("bne", "tne", CONDNE, F_CONDBR),
+cond ("bneg", "tneg", CONDNEG, F_CONDBR),
+cond ("bnz", "tnz", CONDNZ, F_CONDBR|F_ALIAS), /* for ne */
+cond ("bpos", "tpos", CONDPOS, F_CONDBR),
+cond ("bvc", "tvc", CONDVC, F_CONDBR),
+cond ("bvs", "tvs", CONDVS, F_CONDBR),
+cond ("bz", "tz", CONDZ, F_CONDBR|F_ALIAS), /* for e */
+
+#undef cond
+#undef br
+#undef brr /* v9 */
+#undef tr
+
+#define brr(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask)|BPRED, ANNUL|(lose), "1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose), ",T 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose), ",a 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose), ",a,T 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask), ANNUL|BPRED|(lose), ",N 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|ANNUL, BPRED|(lose), ",a,N 1,k", F_DELAYED|(flags), v9 }
+
+#define condr(bop, mask, flags) /* v9 */ \
+ brr(bop, F2(0, 3)|COND(mask), F2(~0, ~3)|COND(~(mask)), (flags)) /* v9 */
+
+/* v9 */ condr("brnz", 0x5, F_CONDBR),
+/* v9 */ condr("brz", 0x1, F_CONDBR),
+/* v9 */ condr("brgez", 0x7, F_CONDBR),
+/* v9 */ condr("brlz", 0x3, F_CONDBR),
+/* v9 */ condr("brlez", 0x2, F_CONDBR),
+/* v9 */ condr("brgz", 0x6, F_CONDBR),
+
+#undef condr /* v9 */
+#undef brr /* v9 */
+
+#define movr(opcode, mask, flags) /* v9 */ \
+ { opcode, F3(2, 0x2f, 0)|RCOND(mask), F3(~2, ~0x2f, ~0)|RCOND(~(mask)), "1,2,d", (flags), v9 }, \
+ { opcode, F3(2, 0x2f, 1)|RCOND(mask), F3(~2, ~0x2f, ~1)|RCOND(~(mask)), "1,j,d", (flags), v9 }
+
+#define fmrrs(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,f,g", (flags) | F_FLOAT, v9 }
+#define fmrrd(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,B,H", (flags) | F_FLOAT, v9 }
+#define fmrrq(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,R,J", (flags) | F_FLOAT, v9 }
+
+#define fmovrs(mop, mask, flags) /* v9 */ \
+ fmrrs(mop, F3(2, 0x35, 0)|OPF_LOW5(5)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~5)|RCOND(~(mask)), (flags)) /* v9 */
+#define fmovrd(mop, mask, flags) /* v9 */ \
+ fmrrd(mop, F3(2, 0x35, 0)|OPF_LOW5(6)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~6)|RCOND(~(mask)), (flags)) /* v9 */
+#define fmovrq(mop, mask, flags) /* v9 */ \
+ fmrrq(mop, F3(2, 0x35, 0)|OPF_LOW5(7)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~7)|RCOND(~(mask)), (flags)) /* v9 */
+
+/* v9 */ movr("movrne", 0x5, 0),
+/* v9 */ movr("movre", 0x1, 0),
+/* v9 */ movr("movrgez", 0x7, 0),
+/* v9 */ movr("movrlz", 0x3, 0),
+/* v9 */ movr("movrlez", 0x2, 0),
+/* v9 */ movr("movrgz", 0x6, 0),
+/* v9 */ movr("movrnz", 0x5, F_ALIAS),
+/* v9 */ movr("movrz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrs("fmovrsne", 0x5, 0),
+/* v9 */ fmovrs("fmovrse", 0x1, 0),
+/* v9 */ fmovrs("fmovrsgez", 0x7, 0),
+/* v9 */ fmovrs("fmovrslz", 0x3, 0),
+/* v9 */ fmovrs("fmovrslez", 0x2, 0),
+/* v9 */ fmovrs("fmovrsgz", 0x6, 0),
+/* v9 */ fmovrs("fmovrsnz", 0x5, F_ALIAS),
+/* v9 */ fmovrs("fmovrsz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrd("fmovrdne", 0x5, 0),
+/* v9 */ fmovrd("fmovrde", 0x1, 0),
+/* v9 */ fmovrd("fmovrdgez", 0x7, 0),
+/* v9 */ fmovrd("fmovrdlz", 0x3, 0),
+/* v9 */ fmovrd("fmovrdlez", 0x2, 0),
+/* v9 */ fmovrd("fmovrdgz", 0x6, 0),
+/* v9 */ fmovrd("fmovrdnz", 0x5, F_ALIAS),
+/* v9 */ fmovrd("fmovrdz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrq("fmovrqne", 0x5, 0),
+/* v9 */ fmovrq("fmovrqe", 0x1, 0),
+/* v9 */ fmovrq("fmovrqgez", 0x7, 0),
+/* v9 */ fmovrq("fmovrqlz", 0x3, 0),
+/* v9 */ fmovrq("fmovrqlez", 0x2, 0),
+/* v9 */ fmovrq("fmovrqgz", 0x6, 0),
+/* v9 */ fmovrq("fmovrqnz", 0x5, F_ALIAS),
+/* v9 */ fmovrq("fmovrqz", 0x1, F_ALIAS),
+
+#undef movr /* v9 */
+#undef fmovr /* v9 */
+#undef fmrr /* v9 */
+
+#define movicc(opcode, cond, flags) /* v9 */ \
+ { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|XCC|(1<<11), "z,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|XCC|(1<<11), "z,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|(1<<11), "Z,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|(1<<11), "Z,I,d", flags, v9 }
+
+#define movfcc(opcode, fcond, flags) /* v9 */ \
+ { opcode, F3(2, 0x2c, 0)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~0), "6,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~1), "6,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~0), "7,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~1), "7,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~0), "8,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~1), "8,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~0), "9,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~1), "9,I,d", flags, v9 }
+
+#define movcc(opcode, cond, fcond, flags) /* v9 */ \
+ movfcc (opcode, fcond, flags), /* v9 */ \
+ movicc (opcode, cond, flags) /* v9 */
+
+/* v9 */ movcc ("mova", CONDA, FCONDA, 0),
+/* v9 */ movicc ("movcc", CONDCC, 0),
+/* v9 */ movicc ("movgeu", CONDGEU, F_ALIAS),
+/* v9 */ movicc ("movcs", CONDCS, 0),
+/* v9 */ movicc ("movlu", CONDLU, F_ALIAS),
+/* v9 */ movcc ("move", CONDE, FCONDE, 0),
+/* v9 */ movcc ("movg", CONDG, FCONDG, 0),
+/* v9 */ movcc ("movge", CONDGE, FCONDGE, 0),
+/* v9 */ movicc ("movgu", CONDGU, 0),
+/* v9 */ movcc ("movl", CONDL, FCONDL, 0),
+/* v9 */ movcc ("movle", CONDLE, FCONDLE, 0),
+/* v9 */ movicc ("movleu", CONDLEU, 0),
+/* v9 */ movfcc ("movlg", FCONDLG, 0),
+/* v9 */ movcc ("movn", CONDN, FCONDN, 0),
+/* v9 */ movcc ("movne", CONDNE, FCONDNE, 0),
+/* v9 */ movicc ("movneg", CONDNEG, 0),
+/* v9 */ movcc ("movnz", CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ movfcc ("movo", FCONDO, 0),
+/* v9 */ movicc ("movpos", CONDPOS, 0),
+/* v9 */ movfcc ("movu", FCONDU, 0),
+/* v9 */ movfcc ("movue", FCONDUE, 0),
+/* v9 */ movfcc ("movug", FCONDUG, 0),
+/* v9 */ movfcc ("movuge", FCONDUGE, 0),
+/* v9 */ movfcc ("movul", FCONDUL, 0),
+/* v9 */ movfcc ("movule", FCONDULE, 0),
+/* v9 */ movicc ("movvc", CONDVC, 0),
+/* v9 */ movicc ("movvs", CONDVS, 0),
+/* v9 */ movcc ("movz", CONDZ, FCONDZ, F_ALIAS),
+
+#undef movicc /* v9 */
+#undef movfcc /* v9 */
+#undef movcc /* v9 */
+
+#define FM_SF 1 /* v9 - values for fpsize */
+#define FM_DF 2 /* v9 */
+#define FM_QF 3 /* v9 */
+
+#define fmovicc(opcode, fpsize, cond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z,f,g", flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z,f,g", flags, v9 }
+
+#define fmovfcc(opcode, fpsize, fcond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6,f,g", flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7,f,g", flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8,f,g", flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9,f,g", flags, v9 }
+
+/* FIXME: use fmovicc/fmovfcc? */ /* v9 */
+#define fmovcc(opcode, fpsize, cond, fcond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z,f,g", flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6,f,g", flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z,f,g", flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7,f,g", flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8,f,g", flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9,f,g", flags | F_FLOAT, v9 }
+
+/* v9 */ fmovcc ("fmovda", FM_DF, CONDA, FCONDA, 0),
+/* v9 */ fmovcc ("fmovqa", FM_QF, CONDA, FCONDA, 0),
+/* v9 */ fmovcc ("fmovsa", FM_SF, CONDA, FCONDA, 0),
+/* v9 */ fmovicc ("fmovdcc", FM_DF, CONDCC, 0),
+/* v9 */ fmovicc ("fmovqcc", FM_QF, CONDCC, 0),
+/* v9 */ fmovicc ("fmovscc", FM_SF, CONDCC, 0),
+/* v9 */ fmovicc ("fmovdcs", FM_DF, CONDCS, 0),
+/* v9 */ fmovicc ("fmovqcs", FM_QF, CONDCS, 0),
+/* v9 */ fmovicc ("fmovscs", FM_SF, CONDCS, 0),
+/* v9 */ fmovcc ("fmovde", FM_DF, CONDE, FCONDE, 0),
+/* v9 */ fmovcc ("fmovqe", FM_QF, CONDE, FCONDE, 0),
+/* v9 */ fmovcc ("fmovse", FM_SF, CONDE, FCONDE, 0),
+/* v9 */ fmovcc ("fmovdg", FM_DF, CONDG, FCONDG, 0),
+/* v9 */ fmovcc ("fmovqg", FM_QF, CONDG, FCONDG, 0),
+/* v9 */ fmovcc ("fmovsg", FM_SF, CONDG, FCONDG, 0),
+/* v9 */ fmovcc ("fmovdge", FM_DF, CONDGE, FCONDGE, 0),
+/* v9 */ fmovcc ("fmovqge", FM_QF, CONDGE, FCONDGE, 0),
+/* v9 */ fmovcc ("fmovsge", FM_SF, CONDGE, FCONDGE, 0),
+/* v9 */ fmovicc ("fmovdgeu", FM_DF, CONDGEU, F_ALIAS),
+/* v9 */ fmovicc ("fmovqgeu", FM_QF, CONDGEU, F_ALIAS),
+/* v9 */ fmovicc ("fmovsgeu", FM_SF, CONDGEU, F_ALIAS),
+/* v9 */ fmovicc ("fmovdgu", FM_DF, CONDGU, 0),
+/* v9 */ fmovicc ("fmovqgu", FM_QF, CONDGU, 0),
+/* v9 */ fmovicc ("fmovsgu", FM_SF, CONDGU, 0),
+/* v9 */ fmovcc ("fmovdl", FM_DF, CONDL, FCONDL, 0),
+/* v9 */ fmovcc ("fmovql", FM_QF, CONDL, FCONDL, 0),
+/* v9 */ fmovcc ("fmovsl", FM_SF, CONDL, FCONDL, 0),
+/* v9 */ fmovcc ("fmovdle", FM_DF, CONDLE, FCONDLE, 0),
+/* v9 */ fmovcc ("fmovqle", FM_QF, CONDLE, FCONDLE, 0),
+/* v9 */ fmovcc ("fmovsle", FM_SF, CONDLE, FCONDLE, 0),
+/* v9 */ fmovicc ("fmovdleu", FM_DF, CONDLEU, 0),
+/* v9 */ fmovicc ("fmovqleu", FM_QF, CONDLEU, 0),
+/* v9 */ fmovicc ("fmovsleu", FM_SF, CONDLEU, 0),
+/* v9 */ fmovfcc ("fmovdlg", FM_DF, FCONDLG, 0),
+/* v9 */ fmovfcc ("fmovqlg", FM_QF, FCONDLG, 0),
+/* v9 */ fmovfcc ("fmovslg", FM_SF, FCONDLG, 0),
+/* v9 */ fmovicc ("fmovdlu", FM_DF, CONDLU, F_ALIAS),
+/* v9 */ fmovicc ("fmovqlu", FM_QF, CONDLU, F_ALIAS),
+/* v9 */ fmovicc ("fmovslu", FM_SF, CONDLU, F_ALIAS),
+/* v9 */ fmovcc ("fmovdn", FM_DF, CONDN, FCONDN, 0),
+/* v9 */ fmovcc ("fmovqn", FM_QF, CONDN, FCONDN, 0),
+/* v9 */ fmovcc ("fmovsn", FM_SF, CONDN, FCONDN, 0),
+/* v9 */ fmovcc ("fmovdne", FM_DF, CONDNE, FCONDNE, 0),
+/* v9 */ fmovcc ("fmovqne", FM_QF, CONDNE, FCONDNE, 0),
+/* v9 */ fmovcc ("fmovsne", FM_SF, CONDNE, FCONDNE, 0),
+/* v9 */ fmovicc ("fmovdneg", FM_DF, CONDNEG, 0),
+/* v9 */ fmovicc ("fmovqneg", FM_QF, CONDNEG, 0),
+/* v9 */ fmovicc ("fmovsneg", FM_SF, CONDNEG, 0),
+/* v9 */ fmovcc ("fmovdnz", FM_DF, CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ fmovcc ("fmovqnz", FM_QF, CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ fmovcc ("fmovsnz", FM_SF, CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ fmovfcc ("fmovdo", FM_DF, FCONDO, 0),
+/* v9 */ fmovfcc ("fmovqo", FM_QF, FCONDO, 0),
+/* v9 */ fmovfcc ("fmovso", FM_SF, FCONDO, 0),
+/* v9 */ fmovicc ("fmovdpos", FM_DF, CONDPOS, 0),
+/* v9 */ fmovicc ("fmovqpos", FM_QF, CONDPOS, 0),
+/* v9 */ fmovicc ("fmovspos", FM_SF, CONDPOS, 0),
+/* v9 */ fmovfcc ("fmovdu", FM_DF, FCONDU, 0),
+/* v9 */ fmovfcc ("fmovqu", FM_QF, FCONDU, 0),
+/* v9 */ fmovfcc ("fmovsu", FM_SF, FCONDU, 0),
+/* v9 */ fmovfcc ("fmovdue", FM_DF, FCONDUE, 0),
+/* v9 */ fmovfcc ("fmovque", FM_QF, FCONDUE, 0),
+/* v9 */ fmovfcc ("fmovsue", FM_SF, FCONDUE, 0),
+/* v9 */ fmovfcc ("fmovdug", FM_DF, FCONDUG, 0),
+/* v9 */ fmovfcc ("fmovqug", FM_QF, FCONDUG, 0),
+/* v9 */ fmovfcc ("fmovsug", FM_SF, FCONDUG, 0),
+/* v9 */ fmovfcc ("fmovduge", FM_DF, FCONDUGE, 0),
+/* v9 */ fmovfcc ("fmovquge", FM_QF, FCONDUGE, 0),
+/* v9 */ fmovfcc ("fmovsuge", FM_SF, FCONDUGE, 0),
+/* v9 */ fmovfcc ("fmovdul", FM_DF, FCONDUL, 0),
+/* v9 */ fmovfcc ("fmovqul", FM_QF, FCONDUL, 0),
+/* v9 */ fmovfcc ("fmovsul", FM_SF, FCONDUL, 0),
+/* v9 */ fmovfcc ("fmovdule", FM_DF, FCONDULE, 0),
+/* v9 */ fmovfcc ("fmovqule", FM_QF, FCONDULE, 0),
+/* v9 */ fmovfcc ("fmovsule", FM_SF, FCONDULE, 0),
+/* v9 */ fmovicc ("fmovdvc", FM_DF, CONDVC, 0),
+/* v9 */ fmovicc ("fmovqvc", FM_QF, CONDVC, 0),
+/* v9 */ fmovicc ("fmovsvc", FM_SF, CONDVC, 0),
+/* v9 */ fmovicc ("fmovdvs", FM_DF, CONDVS, 0),
+/* v9 */ fmovicc ("fmovqvs", FM_QF, CONDVS, 0),
+/* v9 */ fmovicc ("fmovsvs", FM_SF, CONDVS, 0),
+/* v9 */ fmovcc ("fmovdz", FM_DF, CONDZ, FCONDZ, F_ALIAS),
+/* v9 */ fmovcc ("fmovqz", FM_QF, CONDZ, FCONDZ, F_ALIAS),
+/* v9 */ fmovcc ("fmovsz", FM_SF, CONDZ, FCONDZ, F_ALIAS),
+
+#undef fmovicc /* v9 */
+#undef fmovfcc /* v9 */
+#undef fmovcc /* v9 */
+#undef FM_DF /* v9 */
+#undef FM_QF /* v9 */
+#undef FM_SF /* v9 */
+
+/* Coprocessor branches. */
+#define CBR(opcode, mask, lose, flags, arch) \
+ { opcode, (mask), ANNUL|(lose), "l", flags|F_DELAYED, arch }, \
+ { opcode, (mask)|ANNUL, (lose), ",a l", flags|F_DELAYED, arch }
+
+/* Floating point branches. */
+#define FBR(opcode, mask, lose, flags) \
+ { opcode, (mask), ANNUL|(lose), "l", flags|F_DELAYED|F_FBR, v6 }, \
+ { opcode, (mask)|ANNUL, (lose), ",a l", flags|F_DELAYED|F_FBR, v6 }
+
+/* V9 extended floating point branches. */
+#define FBRX(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), "6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), ",T 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a,T 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask), ANNUL|BPRED|FBFCC(~0)|(lose), ",N 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|ANNUL, BPRED|FBFCC(~0)|(lose), ",a,N 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), "7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), ",T 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a,T 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask), ANNUL|BPRED|FBFCC(~1)|(lose), ",N 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|ANNUL, BPRED|FBFCC(~1)|(lose), ",a,N 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), "8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), ",T 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a,T 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask), ANNUL|BPRED|FBFCC(~2)|(lose), ",N 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|ANNUL, BPRED|FBFCC(~2)|(lose), ",a,N 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), "9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), ",T 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a,T 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask), ANNUL|BPRED|FBFCC(~3)|(lose), ",N 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|ANNUL, BPRED|FBFCC(~3)|(lose), ",a,N 9,G", flags|F_DELAYED|F_FBR, v9 }
+
+/* v9: We must put `FBRX' before `FBR', to ensure that we never match
+ v9: something against an expression unless it is an expression. Otherwise,
+ v9: we end up with undefined symbol tables entries, because they get added,
+ v9: but are not deleted if the pattern fails to match. */
+
+#define CONDFC(fop, cop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \
+ CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6notlet)
+
+#define CONDFCL(fop, cop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \
+ CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6)
+
+#define CONDF(fop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags)
+
+CONDFC ("fb", "cb", 0x8, F_UNBR),
+CONDFCL ("fba", "cba", 0x8, F_UNBR|F_ALIAS),
+CONDFC ("fbe", "cb0", 0x9, F_CONDBR),
+CONDF ("fbz", 0x9, F_CONDBR|F_ALIAS),
+CONDFC ("fbg", "cb2", 0x6, F_CONDBR),
+CONDFC ("fbge", "cb02", 0xb, F_CONDBR),
+CONDFC ("fbl", "cb1", 0x4, F_CONDBR),
+CONDFC ("fble", "cb01", 0xd, F_CONDBR),
+CONDFC ("fblg", "cb12", 0x2, F_CONDBR),
+CONDFCL ("fbn", "cbn", 0x0, F_UNBR),
+CONDFC ("fbne", "cb123", 0x1, F_CONDBR),
+CONDF ("fbnz", 0x1, F_CONDBR|F_ALIAS),
+CONDFC ("fbo", "cb012", 0xf, F_CONDBR),
+CONDFC ("fbu", "cb3", 0x7, F_CONDBR),
+CONDFC ("fbue", "cb03", 0xa, F_CONDBR),
+CONDFC ("fbug", "cb23", 0x5, F_CONDBR),
+CONDFC ("fbuge", "cb023", 0xc, F_CONDBR),
+CONDFC ("fbul", "cb13", 0x3, F_CONDBR),
+CONDFC ("fbule", "cb013", 0xe, F_CONDBR),
+
+#undef CONDFC
+#undef CONDFCL
+#undef CONDF
+#undef CBR
+#undef FBR
+#undef FBRX /* v9 */
+
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%g0 */
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+i,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* jmpl i+rs1,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* jmpl %g0+i,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+0,%g0 */
+
+{ "nop", F2(0, 4), 0xfeffffff, "", 0, v6 }, /* sethi 0, %g0 */
+
+{ "set", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v6 },
+{ "setuw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 },
+{ "setsw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 },
+{ "setx", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,1,d", F_ALIAS, v9 },
+
+{ "sethi", F2(0x0, 0x4), F2(~0x0, ~0x4), "h,d", 0, v6 },
+
+{ "taddcc", F3(2, 0x20, 0), F3(~2, ~0x20, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "1,i,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "i,1,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 0), F3(~2, ~0x22, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "1,i,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "i,1,d", 0, v6 },
+
+{ "tsubcc", F3(2, 0x21, 0), F3(~2, ~0x21, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcc", F3(2, 0x21, 1), F3(~2, ~0x21, ~1), "1,i,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 0), F3(~2, ~0x23, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 1), F3(~2, ~0x23, ~1), "1,i,d", 0, v6 },
+
+{ "unimp", F2(0x0, 0x0), 0xffc00000, "n", 0, v6notv9 },
+{ "illtrap", F2(0, 0), F2(~0, ~0)|RD_G0, "n", 0, v9 },
+
+/* This *is* a commutative instruction. */
+{ "xnor", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "1,i,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "i,1,d", 0, v6 },
+/* This *is* a commutative instruction. */
+{ "xnorcc", F3(2, 0x17, 0), F3(~2, ~0x17, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "1,i,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "i,1,d", 0, v6 },
+{ "xor", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "1,i,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,1,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 0), F3(~2, ~0x13, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "1,i,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "i,1,d", 0, v6 },
+
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,d", F_ALIAS, v6 }, /* xnor rs1,%0,rd */
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "r", F_ALIAS, v6 }, /* xnor rd,%0,rd */
+
+{ "btog", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* xor rd,rs2,rd */
+{ "btog", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,r", F_ALIAS, v6 }, /* xor rd,i,rd */
+
+/* FPop1 and FPop2 are not instructions. Don't accept them. */
+
+{ "fdtoi", F3F(2, 0x34, 0x0d2), F3F(~2, ~0x34, ~0x0d2)|RS1_G0, "B,g", F_FLOAT, v6 },
+{ "fstoi", F3F(2, 0x34, 0x0d1), F3F(~2, ~0x34, ~0x0d1)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fqtoi", F3F(2, 0x34, 0x0d3), F3F(~2, ~0x34, ~0x0d3)|RS1_G0, "R,g", F_FLOAT, v8 },
+
+{ "fdtox", F3F(2, 0x34, 0x082), F3F(~2, ~0x34, ~0x082)|RS1_G0, "B,g", F_FLOAT, v9 },
+{ "fstox", F3F(2, 0x34, 0x081), F3F(~2, ~0x34, ~0x081)|RS1_G0, "f,g", F_FLOAT, v9 },
+{ "fqtox", F3F(2, 0x34, 0x083), F3F(~2, ~0x34, ~0x083)|RS1_G0, "R,g", F_FLOAT, v9 },
+
+{ "fitod", F3F(2, 0x34, 0x0c8), F3F(~2, ~0x34, ~0x0c8)|RS1_G0, "f,H", F_FLOAT, v6 },
+{ "fitos", F3F(2, 0x34, 0x0c4), F3F(~2, ~0x34, ~0x0c4)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fitoq", F3F(2, 0x34, 0x0cc), F3F(~2, ~0x34, ~0x0cc)|RS1_G0, "f,J", F_FLOAT, v8 },
+
+{ "fxtod", F3F(2, 0x34, 0x088), F3F(~2, ~0x34, ~0x088)|RS1_G0, "f,H", F_FLOAT, v9 },
+{ "fxtos", F3F(2, 0x34, 0x084), F3F(~2, ~0x34, ~0x084)|RS1_G0, "f,g", F_FLOAT, v9 },
+{ "fxtoq", F3F(2, 0x34, 0x08c), F3F(~2, ~0x34, ~0x08c)|RS1_G0, "f,J", F_FLOAT, v9 },
+
+{ "fdtoq", F3F(2, 0x34, 0x0ce), F3F(~2, ~0x34, ~0x0ce)|RS1_G0, "B,J", F_FLOAT, v8 },
+{ "fdtos", F3F(2, 0x34, 0x0c6), F3F(~2, ~0x34, ~0x0c6)|RS1_G0, "B,g", F_FLOAT, v6 },
+{ "fqtod", F3F(2, 0x34, 0x0cb), F3F(~2, ~0x34, ~0x0cb)|RS1_G0, "R,H", F_FLOAT, v8 },
+{ "fqtos", F3F(2, 0x34, 0x0c7), F3F(~2, ~0x34, ~0x0c7)|RS1_G0, "R,g", F_FLOAT, v8 },
+{ "fstod", F3F(2, 0x34, 0x0c9), F3F(~2, ~0x34, ~0x0c9)|RS1_G0, "f,H", F_FLOAT, v6 },
+{ "fstoq", F3F(2, 0x34, 0x0cd), F3F(~2, ~0x34, ~0x0cd)|RS1_G0, "f,J", F_FLOAT, v8 },
+
+{ "fdivd", F3F(2, 0x34, 0x04e), F3F(~2, ~0x34, ~0x04e), "v,B,H", F_FLOAT, v6 },
+{ "fdivq", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT, v8 },
+{ "fdivx", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fdivs", F3F(2, 0x34, 0x04d), F3F(~2, ~0x34, ~0x04d), "e,f,g", F_FLOAT, v6 },
+{ "fmuld", F3F(2, 0x34, 0x04a), F3F(~2, ~0x34, ~0x04a), "v,B,H", F_FLOAT, v6 },
+{ "fmulq", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT, v8 },
+{ "fmulx", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fmuls", F3F(2, 0x34, 0x049), F3F(~2, ~0x34, ~0x049), "e,f,g", F_FLOAT, v6 },
+
+{ "fdmulq", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT, v8 },
+{ "fdmulx", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsmuld", F3F(2, 0x34, 0x069), F3F(~2, ~0x34, ~0x069), "e,f,H", F_FLOAT, v8 },
+
+{ "fsqrtd", F3F(2, 0x34, 0x02a), F3F(~2, ~0x34, ~0x02a)|RS1_G0, "B,H", F_FLOAT, v7 },
+{ "fsqrtq", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT, v8 },
+{ "fsqrtx", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsqrts", F3F(2, 0x34, 0x029), F3F(~2, ~0x34, ~0x029)|RS1_G0, "f,g", F_FLOAT, v7 },
+
+{ "fabsd", F3F(2, 0x34, 0x00a), F3F(~2, ~0x34, ~0x00a)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fabsq", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fabsx", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fabss", F3F(2, 0x34, 0x009), F3F(~2, ~0x34, ~0x009)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fmovd", F3F(2, 0x34, 0x002), F3F(~2, ~0x34, ~0x002)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fmovq", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fmovx", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fmovs", F3F(2, 0x34, 0x001), F3F(~2, ~0x34, ~0x001)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fnegd", F3F(2, 0x34, 0x006), F3F(~2, ~0x34, ~0x006)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fnegq", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fnegx", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fnegs", F3F(2, 0x34, 0x005), F3F(~2, ~0x34, ~0x005)|RS1_G0, "f,g", F_FLOAT, v6 },
+
+{ "faddd", F3F(2, 0x34, 0x042), F3F(~2, ~0x34, ~0x042), "v,B,H", F_FLOAT, v6 },
+{ "faddq", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT, v8 },
+{ "faddx", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fadds", F3F(2, 0x34, 0x041), F3F(~2, ~0x34, ~0x041), "e,f,g", F_FLOAT, v6 },
+{ "fsubd", F3F(2, 0x34, 0x046), F3F(~2, ~0x34, ~0x046), "v,B,H", F_FLOAT, v6 },
+{ "fsubq", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT, v8 },
+{ "fsubx", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsubs", F3F(2, 0x34, 0x045), F3F(~2, ~0x34, ~0x045), "e,f,g", F_FLOAT, v6 },
+
+#define CMPFCC(x) (((x)&0x3)<<25)
+
+{ "fcmpd", F3F(2, 0x35, 0x052), F3F(~2, ~0x35, ~0x052)|RD_G0, "v,B", F_FLOAT, v6 },
+{ "fcmpd", CMPFCC(0)|F3F(2, 0x35, 0x052), CMPFCC(~0)|F3F(~2, ~0x35, ~0x052), "6,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(1)|F3F(2, 0x35, 0x052), CMPFCC(~1)|F3F(~2, ~0x35, ~0x052), "7,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(2)|F3F(2, 0x35, 0x052), CMPFCC(~2)|F3F(~2, ~0x35, ~0x052), "8,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(3)|F3F(2, 0x35, 0x052), CMPFCC(~3)|F3F(~2, ~0x35, ~0x052), "9,v,B", F_FLOAT, v9 },
+{ "fcmped", F3F(2, 0x35, 0x056), F3F(~2, ~0x35, ~0x056)|RD_G0, "v,B", F_FLOAT, v6 },
+{ "fcmped", CMPFCC(0)|F3F(2, 0x35, 0x056), CMPFCC(~0)|F3F(~2, ~0x35, ~0x056), "6,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(1)|F3F(2, 0x35, 0x056), CMPFCC(~1)|F3F(~2, ~0x35, ~0x056), "7,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(2)|F3F(2, 0x35, 0x056), CMPFCC(~2)|F3F(~2, ~0x35, ~0x056), "8,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(3)|F3F(2, 0x35, 0x056), CMPFCC(~3)|F3F(~2, ~0x35, ~0x056), "9,v,B", F_FLOAT, v9 },
+{ "fcmpq", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT, v8 },
+{ "fcmpq", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT, v9 },
+{ "fcmpeq", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT, v8 },
+{ "fcmpeq", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT, v9 },
+{ "fcmpx", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 },
+{ "fcmpx", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 },
+{ "fcmpex", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmps", F3F(2, 0x35, 0x051), F3F(~2, ~0x35, ~0x051)|RD_G0, "e,f", F_FLOAT, v6 },
+{ "fcmps", CMPFCC(0)|F3F(2, 0x35, 0x051), CMPFCC(~0)|F3F(~2, ~0x35, ~0x051), "6,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(1)|F3F(2, 0x35, 0x051), CMPFCC(~1)|F3F(~2, ~0x35, ~0x051), "7,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(2)|F3F(2, 0x35, 0x051), CMPFCC(~2)|F3F(~2, ~0x35, ~0x051), "8,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(3)|F3F(2, 0x35, 0x051), CMPFCC(~3)|F3F(~2, ~0x35, ~0x051), "9,e,f", F_FLOAT, v9 },
+{ "fcmpes", F3F(2, 0x35, 0x055), F3F(~2, ~0x35, ~0x055)|RD_G0, "e,f", F_FLOAT, v6 },
+{ "fcmpes", CMPFCC(0)|F3F(2, 0x35, 0x055), CMPFCC(~0)|F3F(~2, ~0x35, ~0x055), "6,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(1)|F3F(2, 0x35, 0x055), CMPFCC(~1)|F3F(~2, ~0x35, ~0x055), "7,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(2)|F3F(2, 0x35, 0x055), CMPFCC(~2)|F3F(~2, ~0x35, ~0x055), "8,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(3)|F3F(2, 0x35, 0x055), CMPFCC(~3)|F3F(~2, ~0x35, ~0x055), "9,e,f", F_FLOAT, v9 },
+
+/* These Extended FPop (FIFO) instructions are new in the Fujitsu
+ MB86934, replacing the CPop instructions from v6 and later
+ processors. */
+
+#define EFPOP1_2(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op)|RS1_G0, args, 0, sparclite }
+#define EFPOP1_3(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op), args, 0, sparclite }
+#define EFPOP2_2(name, op, args) { name, F3F(2, 0x37, op), F3F(~2, ~0x37, ~op)|RD_G0, args, 0, sparclite }
+
+EFPOP1_2 ("efitod", 0x0c8, "f,H"),
+EFPOP1_2 ("efitos", 0x0c4, "f,g"),
+EFPOP1_2 ("efdtoi", 0x0d2, "B,g"),
+EFPOP1_2 ("efstoi", 0x0d1, "f,g"),
+EFPOP1_2 ("efstod", 0x0c9, "f,H"),
+EFPOP1_2 ("efdtos", 0x0c6, "B,g"),
+EFPOP1_2 ("efmovs", 0x001, "f,g"),
+EFPOP1_2 ("efnegs", 0x005, "f,g"),
+EFPOP1_2 ("efabss", 0x009, "f,g"),
+EFPOP1_2 ("efsqrtd", 0x02a, "B,H"),
+EFPOP1_2 ("efsqrts", 0x029, "f,g"),
+EFPOP1_3 ("efaddd", 0x042, "v,B,H"),
+EFPOP1_3 ("efadds", 0x041, "e,f,g"),
+EFPOP1_3 ("efsubd", 0x046, "v,B,H"),
+EFPOP1_3 ("efsubs", 0x045, "e,f,g"),
+EFPOP1_3 ("efdivd", 0x04e, "v,B,H"),
+EFPOP1_3 ("efdivs", 0x04d, "e,f,g"),
+EFPOP1_3 ("efmuld", 0x04a, "v,B,H"),
+EFPOP1_3 ("efmuls", 0x049, "e,f,g"),
+EFPOP1_3 ("efsmuld", 0x069, "e,f,H"),
+EFPOP2_2 ("efcmpd", 0x052, "v,B"),
+EFPOP2_2 ("efcmped", 0x056, "v,B"),
+EFPOP2_2 ("efcmps", 0x051, "e,f"),
+EFPOP2_2 ("efcmpes", 0x055, "e,f"),
+
+#undef EFPOP1_2
+#undef EFPOP1_3
+#undef EFPOP2_2
+
+/* These are marked F_ALIAS, so that they won't conflict with sparclite insns
+ present. Otherwise, the F_ALIAS flag is ignored. */
+{ "cpop1", F3(2, 0x36, 0), F3(~2, ~0x36, ~1), "[1+2],d", F_ALIAS, v6notv9 },
+{ "cpop2", F3(2, 0x37, 0), F3(~2, ~0x37, ~1), "[1+2],d", F_ALIAS, v6notv9 },
+
+/* sparclet specific insns */
+
+COMMUTEOP ("umac", 0x3e, sparclet),
+COMMUTEOP ("smac", 0x3f, sparclet),
+COMMUTEOP ("umacd", 0x2e, sparclet),
+COMMUTEOP ("smacd", 0x2f, sparclet),
+COMMUTEOP ("umuld", 0x09, sparclet),
+COMMUTEOP ("smuld", 0x0d, sparclet),
+
+{ "shuffle", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, sparclet },
+{ "shuffle", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, sparclet },
+
+/* The manual isn't completely accurate on these insns. The `rs2' field is
+ treated as being 6 bits to account for 6 bit immediates to cpush. It is
+ assumed that it is intended that bit 5 is 0 when rs2 contains a reg. */
+#define BIT5 (1<<5)
+{ "crdcxt", F3(2, 0x36, 0)|SLCPOP(4), F3(~2, ~0x36, ~0)|SLCPOP(~4)|BIT5|RS2(~0), "U,d", 0, sparclet },
+{ "cwrcxt", F3(2, 0x36, 0)|SLCPOP(3), F3(~2, ~0x36, ~0)|SLCPOP(~3)|BIT5|RS2(~0), "1,u", 0, sparclet },
+{ "cpush", F3(2, 0x36, 0)|SLCPOP(0), F3(~2, ~0x36, ~0)|SLCPOP(~0)|BIT5|RD(~0), "1,2", 0, sparclet },
+{ "cpush", F3(2, 0x36, 1)|SLCPOP(0), F3(~2, ~0x36, ~1)|SLCPOP(~0)|RD(~0), "1,Y", 0, sparclet },
+{ "cpusha", F3(2, 0x36, 0)|SLCPOP(1), F3(~2, ~0x36, ~0)|SLCPOP(~1)|BIT5|RD(~0), "1,2", 0, sparclet },
+{ "cpusha", F3(2, 0x36, 1)|SLCPOP(1), F3(~2, ~0x36, ~1)|SLCPOP(~1)|RD(~0), "1,Y", 0, sparclet },
+{ "cpull", F3(2, 0x36, 0)|SLCPOP(2), F3(~2, ~0x36, ~0)|SLCPOP(~2)|BIT5|RS1(~0)|RS2(~0), "d", 0, sparclet },
+#undef BIT5
+
+/* sparclet coprocessor branch insns */
+#define SLCBCC2(opcode, mask, lose) \
+ { opcode, (mask), ANNUL|(lose), "l", F_DELAYED|F_CONDBR, sparclet }, \
+ { opcode, (mask)|ANNUL, (lose), ",a l", F_DELAYED|F_CONDBR, sparclet }
+#define SLCBCC(opcode, mask) \
+ SLCBCC2(opcode, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)))
+
+/* cbn,cba can't be defined here because they're defined elsewhere and GAS
+ requires all mnemonics of the same name to be consecutive. */
+/*SLCBCC("cbn", 0), - already defined */
+SLCBCC("cbe", 1),
+SLCBCC("cbf", 2),
+SLCBCC("cbef", 3),
+SLCBCC("cbr", 4),
+SLCBCC("cber", 5),
+SLCBCC("cbfr", 6),
+SLCBCC("cbefr", 7),
+/*SLCBCC("cba", 8), - already defined */
+SLCBCC("cbne", 9),
+SLCBCC("cbnf", 10),
+SLCBCC("cbnef", 11),
+SLCBCC("cbnr", 12),
+SLCBCC("cbner", 13),
+SLCBCC("cbnfr", 14),
+SLCBCC("cbnefr", 15),
+
+#undef SLCBCC2
+#undef SLCBCC
+
+{ "casa", F3(3, 0x3c, 0), F3(~3, ~0x3c, ~0), "[1]A,2,d", 0, v9 },
+{ "casa", F3(3, 0x3c, 1), F3(~3, ~0x3c, ~1), "[1]o,2,d", 0, v9 },
+{ "casxa", F3(3, 0x3e, 0), F3(~3, ~0x3e, ~0), "[1]A,2,d", 0, v9 },
+{ "casxa", F3(3, 0x3e, 1), F3(~3, ~0x3e, ~1), "[1]o,2,d", 0, v9 },
+
+/* v9 synthetic insns */
+{ "iprefetch", F2(0, 1)|(2<<20)|BPRED, F2(~0, ~1)|(1<<20)|ANNUL|COND(~0), "G", 0, v9 }, /* bn,a,pt %xcc,label */
+{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* sra rs1,%g0,rd */
+{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* sra rd,%g0,rd */
+{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* srl rs1,%g0,rd */
+{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* srl rd,%g0,rd */
+{ "cas", F3(3, 0x3c, 0)|ASI(0x80), F3(~3, ~0x3c, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P,rs2,rd */
+{ "casl", F3(3, 0x3c, 0)|ASI(0x88), F3(~3, ~0x3c, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P_L,rs2,rd */
+{ "casx", F3(3, 0x3e, 0)|ASI(0x80), F3(~3, ~0x3e, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P,rs2,rd */
+{ "casxl", F3(3, 0x3e, 0)|ASI(0x88), F3(~3, ~0x3e, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P_L,rs2,rd */
+
+/* Ultrasparc extensions */
+{ "shutdown", F3F(2, 0x36, 0x080), F3F(~2, ~0x36, ~0x080)|RD_G0|RS1_G0|RS2_G0, "", 0, v9a },
+
+/* FIXME: Do we want to mark these as F_FLOAT, or something similar? */
+{ "fpadd16", F3F(2, 0x36, 0x050), F3F(~2, ~0x36, ~0x050), "v,B,H", 0, v9a },
+{ "fpadd16s", F3F(2, 0x36, 0x051), F3F(~2, ~0x36, ~0x051), "e,f,g", 0, v9a },
+{ "fpadd32", F3F(2, 0x36, 0x052), F3F(~2, ~0x36, ~0x052), "v,B,H", 0, v9a },
+{ "fpadd32s", F3F(2, 0x36, 0x053), F3F(~2, ~0x36, ~0x053), "e,f,g", 0, v9a },
+{ "fpsub16", F3F(2, 0x36, 0x054), F3F(~2, ~0x36, ~0x054), "v,B,H", 0, v9a },
+{ "fpsub16s", F3F(2, 0x36, 0x055), F3F(~2, ~0x36, ~0x055), "e,f,g", 0, v9a },
+{ "fpsub32", F3F(2, 0x36, 0x056), F3F(~2, ~0x36, ~0x056), "v,B,H", 0, v9a },
+{ "fpsub32s", F3F(2, 0x36, 0x057), F3F(~2, ~0x36, ~0x057), "e,f,g", 0, v9a },
+
+{ "fpack32", F3F(2, 0x36, 0x03a), F3F(~2, ~0x36, ~0x03a), "v,B,H", 0, v9a },
+{ "fpack16", F3F(2, 0x36, 0x03b), F3F(~2, ~0x36, ~0x03b)|RS1_G0, "B,g", 0, v9a },
+{ "fpackfix", F3F(2, 0x36, 0x03d), F3F(~2, ~0x36, ~0x03d)|RS1_G0, "B,g", 0, v9a },
+{ "fexpand", F3F(2, 0x36, 0x04d), F3F(~2, ~0x36, ~0x04d)|RS1_G0, "f,H", 0, v9a },
+{ "fpmerge", F3F(2, 0x36, 0x04b), F3F(~2, ~0x36, ~0x04b), "e,f,H", 0, v9a },
+
+/* Note that the mixing of 32/64 bit regs is intentional. */
+{ "fmul8x16", F3F(2, 0x36, 0x031), F3F(~2, ~0x36, ~0x031), "e,B,H", 0, v9a },
+{ "fmul8x16au", F3F(2, 0x36, 0x033), F3F(~2, ~0x36, ~0x033), "e,f,H", 0, v9a },
+{ "fmul8x16al", F3F(2, 0x36, 0x035), F3F(~2, ~0x36, ~0x035), "e,f,H", 0, v9a },
+{ "fmul8sux16", F3F(2, 0x36, 0x036), F3F(~2, ~0x36, ~0x036), "v,B,H", 0, v9a },
+{ "fmul8ulx16", F3F(2, 0x36, 0x037), F3F(~2, ~0x36, ~0x037), "v,B,H", 0, v9a },
+{ "fmuld8sux16", F3F(2, 0x36, 0x038), F3F(~2, ~0x36, ~0x038), "e,f,H", 0, v9a },
+{ "fmuld8ulx16", F3F(2, 0x36, 0x039), F3F(~2, ~0x36, ~0x039), "e,f,H", 0, v9a },
+
+{ "alignaddr", F3F(2, 0x36, 0x018), F3F(~2, ~0x36, ~0x018), "1,2,d", 0, v9a },
+{ "alignaddrl", F3F(2, 0x36, 0x01a), F3F(~2, ~0x36, ~0x01a), "1,2,d", 0, v9a },
+{ "faligndata", F3F(2, 0x36, 0x048), F3F(~2, ~0x36, ~0x048), "v,B,H", 0, v9a },
+
+{ "fzero", F3F(2, 0x36, 0x060), F3F(~2, ~0x36, ~0x060), "H", 0, v9a },
+{ "fzeros", F3F(2, 0x36, 0x061), F3F(~2, ~0x36, ~0x061), "g", 0, v9a },
+{ "fone", F3F(2, 0x36, 0x07e), F3F(~2, ~0x36, ~0x07e), "H", 0, v9a },
+{ "fones", F3F(2, 0x36, 0x07f), F3F(~2, ~0x36, ~0x07f), "g", 0, v9a },
+{ "fsrc1", F3F(2, 0x36, 0x074), F3F(~2, ~0x36, ~0x074), "v,H", 0, v9a },
+{ "fsrc1s", F3F(2, 0x36, 0x075), F3F(~2, ~0x36, ~0x075), "e,g", 0, v9a },
+{ "fsrc2", F3F(2, 0x36, 0x078), F3F(~2, ~0x36, ~0x078), "B,H", 0, v9a },
+{ "fsrc2s", F3F(2, 0x36, 0x079), F3F(~2, ~0x36, ~0x079), "f,g", 0, v9a },
+{ "fnot1", F3F(2, 0x36, 0x06a), F3F(~2, ~0x36, ~0x06a), "v,H", 0, v9a },
+{ "fnot1s", F3F(2, 0x36, 0x06b), F3F(~2, ~0x36, ~0x06b), "e,g", 0, v9a },
+{ "fnot2", F3F(2, 0x36, 0x066), F3F(~2, ~0x36, ~0x066), "B,H", 0, v9a },
+{ "fnot2s", F3F(2, 0x36, 0x067), F3F(~2, ~0x36, ~0x067), "f,g", 0, v9a },
+{ "for", F3F(2, 0x36, 0x07c), F3F(~2, ~0x36, ~0x07c), "v,B,H", 0, v9a },
+{ "fors", F3F(2, 0x36, 0x07d), F3F(~2, ~0x36, ~0x07d), "e,f,g", 0, v9a },
+{ "fnor", F3F(2, 0x36, 0x062), F3F(~2, ~0x36, ~0x062), "v,B,H", 0, v9a },
+{ "fnors", F3F(2, 0x36, 0x063), F3F(~2, ~0x36, ~0x063), "e,f,g", 0, v9a },
+{ "fand", F3F(2, 0x36, 0x070), F3F(~2, ~0x36, ~0x070), "v,B,H", 0, v9a },
+{ "fands", F3F(2, 0x36, 0x071), F3F(~2, ~0x36, ~0x071), "e,f,g", 0, v9a },
+{ "fnand", F3F(2, 0x36, 0x06e), F3F(~2, ~0x36, ~0x06e), "v,B,H", 0, v9a },
+{ "fnands", F3F(2, 0x36, 0x06f), F3F(~2, ~0x36, ~0x06f), "e,f,g", 0, v9a },
+{ "fxor", F3F(2, 0x36, 0x06c), F3F(~2, ~0x36, ~0x06c), "v,B,H", 0, v9a },
+{ "fxors", F3F(2, 0x36, 0x06d), F3F(~2, ~0x36, ~0x06d), "e,f,g", 0, v9a },
+{ "fxnor", F3F(2, 0x36, 0x072), F3F(~2, ~0x36, ~0x072), "v,B,H", 0, v9a },
+{ "fxnors", F3F(2, 0x36, 0x073), F3F(~2, ~0x36, ~0x073), "e,f,g", 0, v9a },
+{ "fornot1", F3F(2, 0x36, 0x07a), F3F(~2, ~0x36, ~0x07a), "v,B,H", 0, v9a },
+{ "fornot1s", F3F(2, 0x36, 0x07b), F3F(~2, ~0x36, ~0x07b), "e,f,g", 0, v9a },
+{ "fornot2", F3F(2, 0x36, 0x076), F3F(~2, ~0x36, ~0x076), "v,B,H", 0, v9a },
+{ "fornot2s", F3F(2, 0x36, 0x077), F3F(~2, ~0x36, ~0x077), "e,f,g", 0, v9a },
+{ "fandnot1", F3F(2, 0x36, 0x068), F3F(~2, ~0x36, ~0x068), "v,B,H", 0, v9a },
+{ "fandnot1s", F3F(2, 0x36, 0x069), F3F(~2, ~0x36, ~0x069), "e,f,g", 0, v9a },
+{ "fandnot2", F3F(2, 0x36, 0x064), F3F(~2, ~0x36, ~0x064), "v,B,H", 0, v9a },
+{ "fandnot2s", F3F(2, 0x36, 0x065), F3F(~2, ~0x36, ~0x065), "e,f,g", 0, v9a },
+
+{ "fcmpgt16", F3F(2, 0x36, 0x028), F3F(~2, ~0x36, ~0x028), "v,B,d", 0, v9a },
+{ "fcmpgt32", F3F(2, 0x36, 0x02c), F3F(~2, ~0x36, ~0x02c), "v,B,d", 0, v9a },
+{ "fcmple16", F3F(2, 0x36, 0x020), F3F(~2, ~0x36, ~0x020), "v,B,d", 0, v9a },
+{ "fcmple32", F3F(2, 0x36, 0x024), F3F(~2, ~0x36, ~0x024), "v,B,d", 0, v9a },
+{ "fcmpne16", F3F(2, 0x36, 0x022), F3F(~2, ~0x36, ~0x022), "v,B,d", 0, v9a },
+{ "fcmpne32", F3F(2, 0x36, 0x026), F3F(~2, ~0x36, ~0x026), "v,B,d", 0, v9a },
+{ "fcmpeq16", F3F(2, 0x36, 0x02a), F3F(~2, ~0x36, ~0x02a), "v,B,d", 0, v9a },
+{ "fcmpeq32", F3F(2, 0x36, 0x02e), F3F(~2, ~0x36, ~0x02e), "v,B,d", 0, v9a },
+
+{ "edge8", F3F(2, 0x36, 0x000), F3F(~2, ~0x36, ~0x000), "1,2,d", 0, v9a },
+{ "edge8l", F3F(2, 0x36, 0x002), F3F(~2, ~0x36, ~0x002), "1,2,d", 0, v9a },
+{ "edge16", F3F(2, 0x36, 0x004), F3F(~2, ~0x36, ~0x004), "1,2,d", 0, v9a },
+{ "edge16l", F3F(2, 0x36, 0x006), F3F(~2, ~0x36, ~0x006), "1,2,d", 0, v9a },
+{ "edge32", F3F(2, 0x36, 0x008), F3F(~2, ~0x36, ~0x008), "1,2,d", 0, v9a },
+{ "edge32l", F3F(2, 0x36, 0x00a), F3F(~2, ~0x36, ~0x00a), "1,2,d", 0, v9a },
+
+{ "pdist", F3F(2, 0x36, 0x03e), F3F(~2, ~0x36, ~0x03e), "v,B,H", 0, v9a },
+
+{ "array8", F3F(2, 0x36, 0x010), F3F(~2, ~0x36, ~0x010), "1,2,d", 0, v9a },
+{ "array16", F3F(2, 0x36, 0x012), F3F(~2, ~0x36, ~0x012), "1,2,d", 0, v9a },
+{ "array32", F3F(2, 0x36, 0x014), F3F(~2, ~0x36, ~0x014), "1,2,d", 0, v9a },
+
+/* Cheetah instructions */
+{ "edge8n", F3F(2, 0x36, 0x001), F3F(~2, ~0x36, ~0x001), "1,2,d", 0, v9b },
+{ "edge8ln", F3F(2, 0x36, 0x003), F3F(~2, ~0x36, ~0x003), "1,2,d", 0, v9b },
+{ "edge16n", F3F(2, 0x36, 0x005), F3F(~2, ~0x36, ~0x005), "1,2,d", 0, v9b },
+{ "edge16ln", F3F(2, 0x36, 0x007), F3F(~2, ~0x36, ~0x007), "1,2,d", 0, v9b },
+{ "edge32n", F3F(2, 0x36, 0x009), F3F(~2, ~0x36, ~0x009), "1,2,d", 0, v9b },
+{ "edge32ln", F3F(2, 0x36, 0x00b), F3F(~2, ~0x36, ~0x00b), "1,2,d", 0, v9b },
+
+{ "bmask", F3F(2, 0x36, 0x019), F3F(~2, ~0x36, ~0x019), "1,2,d", 0, v9b },
+{ "bshuffle", F3F(2, 0x36, 0x04c), F3F(~2, ~0x36, ~0x04c), "v,B,H", 0, v9b },
+
+{ "siam", F3F(2, 0x36, 0x081), F3F(~2, ~0x36, ~0x081)|RD_G0|RS1_G0|RS2(~7), "3", 0, v9b },
+
+/* More v9 specific insns, these need to come last so they do not clash
+ with v9a instructions such as "edge8" which looks like impdep1. */
+
+#define IMPDEP(name, code) \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0)|ASI(~0), "1,2,d", 0, v9notv9a }, \
+{ name, F3(2, code, 1), F3(~2, ~code, ~1), "1,i,d", 0, v9notv9a }, \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,1,2,d", 0, v9notv9a }, \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,e,f,g", 0, v9notv9a }
+
+IMPDEP ("impdep1", 0x36),
+IMPDEP ("impdep2", 0x37),
+
+#undef IMPDEP
+
+};
+
+const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0]));
+
+/* Utilities for argument parsing. */
+
+typedef struct
+{
+ int value;
+ const char *name;
+} arg;
+
+/* Look up NAME in TABLE. */
+
+static int lookup_name PARAMS ((const arg *, const char *));
+static const char *lookup_value PARAMS ((const arg *, int));
+
+static int
+lookup_name (table, name)
+ const arg *table;
+ const char *name;
+{
+ const arg *p;
+
+ for (p = table; p->name; ++p)
+ if (strcmp (name, p->name) == 0)
+ return p->value;
+
+ return -1;
+}
+
+/* Look up VALUE in TABLE. */
+
+static const char *
+lookup_value (table, value)
+ const arg *table;
+ int value;
+{
+ const arg *p;
+
+ for (p = table; p->name; ++p)
+ if (value == p->value)
+ return p->name;
+
+ return (char *) 0;
+}
+
+/* Handle ASI's. */
+
+static const arg asi_table_v8[] =
+{
+ { 0x00, "#ASI_M_RES00" },
+ { 0x01, "#ASI_M_UNA01" },
+ { 0x02, "#ASI_M_MXCC" },
+ { 0x03, "#ASI_M_FLUSH_PROBE" },
+ { 0x04, "#ASI_M_MMUREGS" },
+ { 0x05, "#ASI_M_TLBDIAG" },
+ { 0x06, "#ASI_M_DIAGS" },
+ { 0x07, "#ASI_M_IODIAG" },
+ { 0x08, "#ASI_M_USERTXT" },
+ { 0x09, "#ASI_M_KERNELTXT" },
+ { 0x0A, "#ASI_M_USERDATA" },
+ { 0x0B, "#ASI_M_KERNELDATA" },
+ { 0x0C, "#ASI_M_TXTC_TAG" },
+ { 0x0D, "#ASI_M_TXTC_DATA" },
+ { 0x0E, "#ASI_M_DATAC_TAG" },
+ { 0x0F, "#ASI_M_DATAC_DATA" },
+ { 0x10, "#ASI_M_FLUSH_PAGE" },
+ { 0x11, "#ASI_M_FLUSH_SEG" },
+ { 0x12, "#ASI_M_FLUSH_REGION" },
+ { 0x13, "#ASI_M_FLUSH_CTX" },
+ { 0x14, "#ASI_M_FLUSH_USER" },
+ { 0x17, "#ASI_M_BCOPY" },
+ { 0x18, "#ASI_M_IFLUSH_PAGE" },
+ { 0x19, "#ASI_M_IFLUSH_SEG" },
+ { 0x1A, "#ASI_M_IFLUSH_REGION" },
+ { 0x1B, "#ASI_M_IFLUSH_CTX" },
+ { 0x1C, "#ASI_M_IFLUSH_USER" },
+ { 0x1F, "#ASI_M_BFILL" },
+ { 0x20, "#ASI_M_BYPASS" },
+ { 0x29, "#ASI_M_FBMEM" },
+ { 0x2A, "#ASI_M_VMEUS" },
+ { 0x2B, "#ASI_M_VMEPS" },
+ { 0x2C, "#ASI_M_VMEUT" },
+ { 0x2D, "#ASI_M_VMEPT" },
+ { 0x2E, "#ASI_M_SBUS" },
+ { 0x2F, "#ASI_M_CTL" },
+ { 0x31, "#ASI_M_FLUSH_IWHOLE" },
+ { 0x36, "#ASI_M_IC_FLCLEAR" },
+ { 0x37, "#ASI_M_DC_FLCLEAR" },
+ { 0x39, "#ASI_M_DCDR" },
+ { 0x40, "#ASI_M_VIKING_TMP1" },
+ { 0x41, "#ASI_M_VIKING_TMP2" },
+ { 0x4c, "#ASI_M_ACTION" },
+ { 0, 0 }
+};
+
+static const arg asi_table_v9[] =
+{
+ /* These are in the v9 architecture manual. */
+ /* The shorter versions appear first, they're here because Sun's as has them.
+ Sun's as uses #ASI_P_L instead of #ASI_PL (which appears in the
+ UltraSPARC architecture manual). */
+ { 0x04, "#ASI_N" },
+ { 0x0c, "#ASI_N_L" },
+ { 0x10, "#ASI_AIUP" },
+ { 0x11, "#ASI_AIUS" },
+ { 0x18, "#ASI_AIUP_L" },
+ { 0x19, "#ASI_AIUS_L" },
+ { 0x80, "#ASI_P" },
+ { 0x81, "#ASI_S" },
+ { 0x82, "#ASI_PNF" },
+ { 0x83, "#ASI_SNF" },
+ { 0x88, "#ASI_P_L" },
+ { 0x89, "#ASI_S_L" },
+ { 0x8a, "#ASI_PNF_L" },
+ { 0x8b, "#ASI_SNF_L" },
+ { 0x04, "#ASI_NUCLEUS" },
+ { 0x0c, "#ASI_NUCLEUS_LITTLE" },
+ { 0x10, "#ASI_AS_IF_USER_PRIMARY" },
+ { 0x11, "#ASI_AS_IF_USER_SECONDARY" },
+ { 0x18, "#ASI_AS_IF_USER_PRIMARY_LITTLE" },
+ { 0x19, "#ASI_AS_IF_USER_SECONDARY_LITTLE" },
+ { 0x80, "#ASI_PRIMARY" },
+ { 0x81, "#ASI_SECONDARY" },
+ { 0x82, "#ASI_PRIMARY_NOFAULT" },
+ { 0x83, "#ASI_SECONDARY_NOFAULT" },
+ { 0x88, "#ASI_PRIMARY_LITTLE" },
+ { 0x89, "#ASI_SECONDARY_LITTLE" },
+ { 0x8a, "#ASI_PRIMARY_NOFAULT_LITTLE" },
+ { 0x8b, "#ASI_SECONDARY_NOFAULT_LITTLE" },
+ /* These are UltraSPARC extensions. */
+ /* FIXME: There are dozens of them. Not sure we want them all.
+ Most are for kernel building but some are for vis type stuff. */
+ { 0, 0 }
+};
+
+/* Return the name for ASI value VALUE or NULL if not found. */
+
+static const char *
+sparc_decode_asi_v9 (int value)
+{
+ return lookup_value (asi_table_v9, value);
+}
+
+static const char *
+sparc_decode_asi_v8 (int value)
+{
+ return lookup_value (asi_table_v8, value);
+}
+
+/* Handle membar masks. */
+
+static arg membar_table[] =
+{
+ { 0x40, "#Sync" },
+ { 0x20, "#MemIssue" },
+ { 0x10, "#Lookaside" },
+ { 0x08, "#StoreStore" },
+ { 0x04, "#LoadStore" },
+ { 0x02, "#StoreLoad" },
+ { 0x01, "#LoadLoad" },
+ { 0, 0 }
+};
+
+/* Return the value for membar arg NAME, or -1 if not found. */
+
+int
+sparc_encode_membar (name)
+ const char *name;
+{
+ return lookup_name (membar_table, name);
+}
+
+/* Return the name for membar value VALUE or NULL if not found. */
+
+const char *
+sparc_decode_membar (value)
+ int value;
+{
+ return lookup_value (membar_table, value);
+}
+
+/* Handle prefetch args. */
+
+static arg prefetch_table[] =
+{
+ { 0, "#n_reads" },
+ { 1, "#one_read" },
+ { 2, "#n_writes" },
+ { 3, "#one_write" },
+ { 4, "#page" },
+ { 16, "#invalidate" },
+ { 0, 0 }
+};
+
+/* Return the value for prefetch arg NAME, or -1 if not found. */
+
+int
+sparc_encode_prefetch (name)
+ const char *name;
+{
+ return lookup_name (prefetch_table, name);
+}
+
+/* Return the name for prefetch value VALUE or NULL if not found. */
+
+const char *
+sparc_decode_prefetch (value)
+ int value;
+{
+ return lookup_value (prefetch_table, value);
+}
+
+/* Handle sparclet coprocessor registers. */
+
+static arg sparclet_cpreg_table[] =
+{
+ { 0, "%ccsr" },
+ { 1, "%ccfr" },
+ { 2, "%cccrcr" },
+ { 3, "%ccpr" },
+ { 4, "%ccsr2" },
+ { 5, "%cccrr" },
+ { 6, "%ccrstr" },
+ { 0, 0 }
+};
+
+/* Return the value for sparclet cpreg arg NAME, or -1 if not found. */
+
+int
+sparc_encode_sparclet_cpreg (name)
+ const char *name;
+{
+ return lookup_name (sparclet_cpreg_table, name);
+}
+
+/* Return the name for sparclet cpreg value VALUE or NULL if not found. */
+
+const char *
+sparc_decode_sparclet_cpreg (value)
+ int value;
+{
+ return lookup_value (sparclet_cpreg_table, value);
+}
+
+#undef MASK_V9
+
+/* Bitmask of v9 architectures. */
+#define MASK_V9 ((1 << SPARC_OPCODE_ARCH_V9) \
+ | (1 << SPARC_OPCODE_ARCH_V9A) \
+ | (1 << SPARC_OPCODE_ARCH_V9B))
+/* 1 if INSN is for v9 only. */
+#define V9_ONLY_P(insn) (! ((insn)->architecture & ~MASK_V9))
+/* 1 if INSN is for v9. */
+#define V9_P(insn) (((insn)->architecture & MASK_V9) != 0)
+
+/* The sorted opcode table. */
+static const struct sparc_opcode **sorted_opcodes;
+
+/* For faster lookup, after insns are sorted they are hashed. */
+/* ??? I think there is room for even more improvement. */
+
+#define HASH_SIZE 256
+/* It is important that we only look at insn code bits as that is how the
+ opcode table is hashed. OPCODE_BITS is a table of valid bits for each
+ of the main types (0,1,2,3). */
+static int opcode_bits[4] = { 0x01c00000, 0x0, 0x01f80000, 0x01f80000 };
+#define HASH_INSN(INSN) \
+ ((((INSN) >> 24) & 0xc0) | (((INSN) & opcode_bits[((INSN) >> 30) & 3]) >> 19))
+struct opcode_hash {
+ struct opcode_hash *next;
+ const struct sparc_opcode *opcode;
+};
+static struct opcode_hash *opcode_hash_table[HASH_SIZE];
+
+static void build_hash_table
+ PARAMS ((const struct sparc_opcode **, struct opcode_hash **, int));
+static int is_delayed_branch PARAMS ((unsigned long));
+static int compare_opcodes PARAMS ((const void *, const void *));
+static int compute_arch_mask PARAMS ((unsigned long));
+
+/* Sign-extend a value which is N bits long. */
+#define SEX(value, bits) \
+ ((((int)(value)) << ((8 * sizeof (int)) - bits)) \
+ >> ((8 * sizeof (int)) - bits) )
+
+static char *reg_names[] =
+{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
+ "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
+ "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
+ "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7",
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+ "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
+ "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
+ "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
+ "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63",
+/* psr, wim, tbr, fpsr, cpsr are v8 only. */
+ "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr"
+};
+
+#define freg_names (&reg_names[4 * 8])
+
+/* These are ordered according to there register number in
+ rdpr and wrpr insns. */
+static char *v9_priv_reg_names[] =
+{
+ "tpc", "tnpc", "tstate", "tt", "tick", "tba", "pstate", "tl",
+ "pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin",
+ "wstate", "fq"
+ /* "ver" - special cased */
+};
+
+/* These are ordered according to there register number in
+ rd and wr insns (-16). */
+static char *v9a_asr_reg_names[] =
+{
+ "pcr", "pic", "dcr", "gsr", "set_softint", "clear_softint",
+ "softint", "tick_cmpr", "sys_tick", "sys_tick_cmpr"
+};
+
+/* Macros used to extract instruction fields. Not all fields have
+ macros defined here, only those which are actually used. */
+
+#define X_RD(i) (((i) >> 25) & 0x1f)
+#define X_RS1(i) (((i) >> 14) & 0x1f)
+#define X_LDST_I(i) (((i) >> 13) & 1)
+#define X_ASI(i) (((i) >> 5) & 0xff)
+#define X_RS2(i) (((i) >> 0) & 0x1f)
+#define X_IMM(i,n) (((i) >> 0) & ((1 << (n)) - 1))
+#define X_SIMM(i,n) SEX (X_IMM ((i), (n)), (n))
+#define X_DISP22(i) (((i) >> 0) & 0x3fffff)
+#define X_IMM22(i) X_DISP22 (i)
+#define X_DISP30(i) (((i) >> 0) & 0x3fffffff)
+
+/* These are for v9. */
+#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff))
+#define X_DISP19(i) (((i) >> 0) & 0x7ffff)
+#define X_MEMBAR(i) ((i) & 0x7f)
+
+/* Here is the union which was used to extract instruction fields
+ before the shift and mask macros were written.
+
+ union sparc_insn
+ {
+ unsigned long int code;
+ struct
+ {
+ unsigned int anop:2;
+ #define op ldst.anop
+ unsigned int anrd:5;
+ #define rd ldst.anrd
+ unsigned int op3:6;
+ unsigned int anrs1:5;
+ #define rs1 ldst.anrs1
+ unsigned int i:1;
+ unsigned int anasi:8;
+ #define asi ldst.anasi
+ unsigned int anrs2:5;
+ #define rs2 ldst.anrs2
+ #define shcnt rs2
+ } ldst;
+ struct
+ {
+ unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1;
+ unsigned int IMM13:13;
+ #define imm13 IMM13.IMM13
+ } IMM13;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int a:1;
+ unsigned int cond:4;
+ unsigned int op2:3;
+ unsigned int DISP22:22;
+ #define disp22 branch.DISP22
+ #define imm22 disp22
+ } branch;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int a:1;
+ unsigned int z:1;
+ unsigned int rcond:3;
+ unsigned int op2:3;
+ unsigned int DISP16HI:2;
+ unsigned int p:1;
+ unsigned int _rs1:5;
+ unsigned int DISP16LO:14;
+ } branch16;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int adisp30:30;
+ #define disp30 call.adisp30
+ } call;
+ };
+
+ */
+
+/* Nonzero if INSN is the opcode for a delayed branch. */
+static int
+is_delayed_branch (insn)
+ unsigned long insn;
+{
+ struct opcode_hash *op;
+
+ for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
+ {
+ const struct sparc_opcode *opcode = op->opcode;
+ if ((opcode->match & insn) == opcode->match
+ && (opcode->lose & insn) == 0)
+ return (opcode->flags & F_DELAYED);
+ }
+ return 0;
+}
+
+/* extern void qsort (); */
+
+/* Records current mask of SPARC_OPCODE_ARCH_FOO values, used to pass value
+ to compare_opcodes. */
+static unsigned int current_arch_mask;
+
+/* Print one instruction from MEMADDR on INFO->STREAM.
+
+ We suffix the instruction with a comment that gives the absolute
+ address involved, as well as its symbolic form, if the instruction
+ is preceded by a findable `sethi' and it either adds an immediate
+ displacement to that register, or it is an `add' or `or' instruction
+ on that register. */
+
+int
+print_insn_sparc (memaddr, info)
+ bfd_vma memaddr;
+ disassemble_info *info;
+{
+ FILE *stream = info->stream;
+ bfd_byte buffer[4];
+ unsigned long insn;
+ register struct opcode_hash *op;
+ /* Nonzero of opcode table has been initialized. */
+ static int opcodes_initialized = 0;
+ /* bfd mach number of last call. */
+ static unsigned long current_mach = 0;
+ bfd_vma (*getword) PARAMS ((const unsigned char *));
+
+ if (!opcodes_initialized
+ || info->mach != current_mach)
+ {
+ int i;
+
+ current_arch_mask = compute_arch_mask (info->mach);
+
+ if (!opcodes_initialized)
+ sorted_opcodes = (const struct sparc_opcode **)
+ malloc (sparc_num_opcodes * sizeof (struct sparc_opcode *));
+ /* Reset the sorted table so we can resort it. */
+ for (i = 0; i < sparc_num_opcodes; ++i)
+ sorted_opcodes[i] = &sparc_opcodes[i];
+ qsort ((char *) sorted_opcodes, sparc_num_opcodes,
+ sizeof (sorted_opcodes[0]), compare_opcodes);
+
+ build_hash_table (sorted_opcodes, opcode_hash_table, sparc_num_opcodes);
+ current_mach = info->mach;
+ opcodes_initialized = 1;
+ }
+
+ {
+ int status =
+ (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ }
+
+ /* On SPARClite variants such as DANlite (sparc86x), instructions
+ are always big-endian even when the machine is in little-endian mode. */
+ if (info->endian == BFD_ENDIAN_BIG || info->mach == bfd_mach_sparc_sparclite)
+ getword = bfd_getb32;
+ else
+ getword = bfd_getl32;
+
+ insn = getword (buffer);
+
+ info->insn_info_valid = 1; /* We do return this info */
+ info->insn_type = dis_nonbranch; /* Assume non branch insn */
+ info->branch_delay_insns = 0; /* Assume no delay */
+ info->target = 0; /* Assume no target known */
+
+ for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
+ {
+ const struct sparc_opcode *opcode = op->opcode;
+
+ /* If the insn isn't supported by the current architecture, skip it. */
+ if (! (opcode->architecture & current_arch_mask))
+ continue;
+
+ if ((opcode->match & insn) == opcode->match
+ && (opcode->lose & insn) == 0)
+ {
+ /* Nonzero means that we have found an instruction which has
+ the effect of adding or or'ing the imm13 field to rs1. */
+ int imm_added_to_rs1 = 0;
+ int imm_ored_to_rs1 = 0;
+
+ /* Nonzero means that we have found a plus sign in the args
+ field of the opcode table. */
+ int found_plus = 0;
+
+ /* Nonzero means we have an annulled branch. */
+ int is_annulled = 0;
+
+ /* Do we have an `add' or `or' instruction combining an
+ immediate with rs1? */
+ if (opcode->match == 0x80102000) /* or */
+ imm_ored_to_rs1 = 1;
+ if (opcode->match == 0x80002000) /* add */
+ imm_added_to_rs1 = 1;
+
+ if (X_RS1 (insn) != X_RD (insn)
+ && strchr (opcode->args, 'r') != 0)
+ /* Can't do simple format if source and dest are different. */
+ continue;
+ if (X_RS2 (insn) != X_RD (insn)
+ && strchr (opcode->args, 'O') != 0)
+ /* Can't do simple format if source and dest are different. */
+ continue;
+
+ (*info->fprintf_func) (stream, opcode->name);
+
+ {
+ register const char *s;
+
+ if (opcode->args[0] != ',')
+ (*info->fprintf_func) (stream, " ");
+ for (s = opcode->args; *s != '\0'; ++s)
+ {
+ while (*s == ',')
+ {
+ (*info->fprintf_func) (stream, ",");
+ ++s;
+ switch (*s) {
+ case 'a':
+ (*info->fprintf_func) (stream, "a");
+ is_annulled = 1;
+ ++s;
+ continue;
+ case 'N':
+ (*info->fprintf_func) (stream, "pn");
+ ++s;
+ continue;
+
+ case 'T':
+ (*info->fprintf_func) (stream, "pt");
+ ++s;
+ continue;
+
+ default:
+ break;
+ } /* switch on arg */
+ } /* while there are comma started args */
+
+ (*info->fprintf_func) (stream, " ");
+
+ switch (*s)
+ {
+ case '+':
+ found_plus = 1;
+
+ /* note fall-through */
+ default:
+ (*info->fprintf_func) (stream, "%c", *s);
+ break;
+
+ case '#':
+ (*info->fprintf_func) (stream, "0");
+ break;
+
+#define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n])
+ case '1':
+ case 'r':
+ reg (X_RS1 (insn));
+ break;
+
+ case '2':
+ case 'O':
+ reg (X_RS2 (insn));
+ break;
+
+ case 'd':
+ reg (X_RD (insn));
+ break;
+#undef reg
+
+#define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n])
+#define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)])
+ case 'e':
+ freg (X_RS1 (insn));
+ break;
+ case 'v': /* double/even */
+ case 'V': /* quad/multiple of 4 */
+ fregx (X_RS1 (insn));
+ break;
+
+ case 'f':
+ freg (X_RS2 (insn));
+ break;
+ case 'B': /* double/even */
+ case 'R': /* quad/multiple of 4 */
+ fregx (X_RS2 (insn));
+ break;
+
+ case 'g':
+ freg (X_RD (insn));
+ break;
+ case 'H': /* double/even */
+ case 'J': /* quad/multiple of 4 */
+ fregx (X_RD (insn));
+ break;
+#undef freg
+#undef fregx
+
+#define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n))
+ case 'b':
+ creg (X_RS1 (insn));
+ break;
+
+ case 'c':
+ creg (X_RS2 (insn));
+ break;
+
+ case 'D':
+ creg (X_RD (insn));
+ break;
+#undef creg
+
+ case 'h':
+ (*info->fprintf_func) (stream, "%%hi(%#x)",
+ ((unsigned) 0xFFFFFFFF
+ & ((int) X_IMM22 (insn) << 10)));
+ break;
+
+ case 'i': /* 13 bit immediate */
+ case 'I': /* 11 bit immediate */
+ case 'j': /* 10 bit immediate */
+ {
+ int imm;
+
+ if (*s == 'i')
+ imm = X_SIMM (insn, 13);
+ else if (*s == 'I')
+ imm = X_SIMM (insn, 11);
+ else
+ imm = X_SIMM (insn, 10);
+
+ /* Check to see whether we have a 1+i, and take
+ note of that fact.
+
+ Note: because of the way we sort the table,
+ we will be matching 1+i rather than i+1,
+ so it is OK to assume that i is after +,
+ not before it. */
+ if (found_plus)
+ imm_added_to_rs1 = 1;
+
+ if (imm <= 9)
+ (*info->fprintf_func) (stream, "%d", imm);
+ else
+ (*info->fprintf_func) (stream, "%#x", imm);
+ }
+ break;
+
+ case 'X': /* 5 bit unsigned immediate */
+ case 'Y': /* 6 bit unsigned immediate */
+ {
+ int imm = X_IMM (insn, *s == 'X' ? 5 : 6);
+
+ if (imm <= 9)
+ (info->fprintf_func) (stream, "%d", imm);
+ else
+ (info->fprintf_func) (stream, "%#x", (unsigned) imm);
+ }
+ break;
+
+ case '3':
+ (info->fprintf_func) (stream, "%d", X_IMM (insn, 3));
+ break;
+
+ case 'K':
+ {
+ int mask = X_MEMBAR (insn);
+ int bit = 0x40, printed_one = 0;
+ const char *name;
+
+ if (mask == 0)
+ (info->fprintf_func) (stream, "0");
+ else
+ while (bit)
+ {
+ if (mask & bit)
+ {
+ if (printed_one)
+ (info->fprintf_func) (stream, "|");
+ name = sparc_decode_membar (bit);
+ (info->fprintf_func) (stream, "%s", name);
+ printed_one = 1;
+ }
+ bit >>= 1;
+ }
+ break;
+ }
+
+ case 'k':
+ info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'G':
+ info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ (*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0');
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (stream, "%%icc");
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (stream, "%%xcc");
+ break;
+
+ case 'E':
+ (*info->fprintf_func) (stream, "%%ccr");
+ break;
+
+ case 's':
+ (*info->fprintf_func) (stream, "%%fprs");
+ break;
+
+ case 'o':
+ (*info->fprintf_func) (stream, "%%asi");
+ break;
+
+ case 'W':
+ (*info->fprintf_func) (stream, "%%tick");
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (stream, "%%pc");
+ break;
+
+ case '?':
+ if (X_RS1 (insn) == 31)
+ (*info->fprintf_func) (stream, "%%ver");
+ else if ((unsigned) X_RS1 (insn) < 16)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_priv_reg_names[X_RS1 (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '!':
+ if ((unsigned) X_RD (insn) < 15)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_priv_reg_names[X_RD (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '/':
+ if (X_RS1 (insn) < 16 || X_RS1 (insn) > 25)
+ (*info->fprintf_func) (stream, "%%reserved");
+ else
+ (*info->fprintf_func) (stream, "%%%s",
+ v9a_asr_reg_names[X_RS1 (insn)-16]);
+ break;
+
+ case '_':
+ if (X_RD (insn) < 16 || X_RD (insn) > 25)
+ (*info->fprintf_func) (stream, "%%reserved");
+ else
+ (*info->fprintf_func) (stream, "%%%s",
+ v9a_asr_reg_names[X_RD (insn)-16]);
+ break;
+
+ case '*':
+ {
+ const char *name = sparc_decode_prefetch (X_RD (insn));
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "%d", X_RD (insn));
+ break;
+ }
+
+ case 'M':
+ (*info->fprintf_func) (stream, "%%asr%d", X_RS1 (insn));
+ break;
+
+ case 'm':
+ (*info->fprintf_func) (stream, "%%asr%d", X_RD (insn));
+ break;
+
+ case 'L':
+ info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'n':
+ (*info->fprintf_func)
+ (stream, "%#x", SEX (X_DISP22 (insn), 22));
+ break;
+
+ case 'l':
+ info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'A':
+ {
+ const char *name;
+
+ if ((info->mach == bfd_mach_sparc_v8plusa) ||
+ ((info->mach >= bfd_mach_sparc_v9) &&
+ (info->mach <= bfd_mach_sparc_v9b)))
+ name = sparc_decode_asi_v9 (X_ASI (insn));
+ else
+ name = sparc_decode_asi_v8 (X_ASI (insn));
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "(%d)", X_ASI (insn));
+ break;
+ }
+
+ case 'C':
+ (*info->fprintf_func) (stream, "%%csr");
+ break;
+
+ case 'F':
+ (*info->fprintf_func) (stream, "%%fsr");
+ break;
+
+ case 'p':
+ (*info->fprintf_func) (stream, "%%psr");
+ break;
+
+ case 'q':
+ (*info->fprintf_func) (stream, "%%fq");
+ break;
+
+ case 'Q':
+ (*info->fprintf_func) (stream, "%%cq");
+ break;
+
+ case 't':
+ (*info->fprintf_func) (stream, "%%tbr");
+ break;
+
+ case 'w':
+ (*info->fprintf_func) (stream, "%%wim");
+ break;
+
+ case 'x':
+ (*info->fprintf_func) (stream, "%d",
+ ((X_LDST_I (insn) << 8)
+ + X_ASI (insn)));
+ break;
+
+ case 'y':
+ (*info->fprintf_func) (stream, "%%y");
+ break;
+
+ case 'u':
+ case 'U':
+ {
+ int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn);
+ const char *name = sparc_decode_sparclet_cpreg (val);
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "%%cpreg(%d)", val);
+ break;
+ }
+ }
+ }
+ }
+
+ /* If we are adding or or'ing something to rs1, then
+ check to see whether the previous instruction was
+ a sethi to the same register as in the sethi.
+ If so, attempt to print the result of the add or
+ or (in this context add and or do the same thing)
+ and its symbolic value. */
+ if (imm_ored_to_rs1 || imm_added_to_rs1)
+ {
+ unsigned long prev_insn;
+ int errcode;
+
+ errcode =
+ (*info->read_memory_func)
+ (memaddr - 4, buffer, sizeof (buffer), info);
+ prev_insn = getword (buffer);
+
+ if (errcode == 0)
+ {
+ /* If it is a delayed branch, we need to look at the
+ instruction before the delayed branch. This handles
+ sequences such as
+
+ sethi %o1, %hi(_foo), %o1
+ call _printf
+ or %o1, %lo(_foo), %o1
+ */
+
+ if (is_delayed_branch (prev_insn))
+ {
+ errcode = (*info->read_memory_func)
+ (memaddr - 8, buffer, sizeof (buffer), info);
+ prev_insn = getword (buffer);
+ }
+ }
+
+ /* If there was a problem reading memory, then assume
+ the previous instruction was not sethi. */
+ if (errcode == 0)
+ {
+ /* Is it sethi to the same register? */
+ if ((prev_insn & 0xc1c00000) == 0x01000000
+ && X_RD (prev_insn) == X_RS1 (insn))
+ {
+ (*info->fprintf_func) (stream, "\t! ");
+ info->target =
+ ((unsigned) 0xFFFFFFFF
+ & ((int) X_IMM22 (prev_insn) << 10));
+ if (imm_added_to_rs1)
+ info->target += X_SIMM (insn, 13);
+ else
+ info->target |= X_SIMM (insn, 13);
+ (*info->print_address_func) (info->target, info);
+ info->insn_type = dis_dref;
+ info->data_size = 4; /* FIXME!!! */
+ }
+ }
+ }
+
+ if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR))
+ {
+ /* FIXME -- check is_annulled flag */
+ if (opcode->flags & F_UNBR)
+ info->insn_type = dis_branch;
+ if (opcode->flags & F_CONDBR)
+ info->insn_type = dis_condbranch;
+ if (opcode->flags & F_JSR)
+ info->insn_type = dis_jsr;
+ if (opcode->flags & F_DELAYED)
+ info->branch_delay_insns = 1;
+ }
+
+ return sizeof (buffer);
+ }
+ }
+
+ info->insn_type = dis_noninsn; /* Mark as non-valid instruction */
+ (*info->fprintf_func) (stream, _("unknown"));
+ return sizeof (buffer);
+}
+
+/* Given BFD mach number, return a mask of SPARC_OPCODE_ARCH_FOO values. */
+
+static int
+compute_arch_mask (mach)
+ unsigned long mach;
+{
+ switch (mach)
+ {
+ case 0 :
+ case bfd_mach_sparc :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8);
+ case bfd_mach_sparc_sparclet :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET);
+ case bfd_mach_sparc_sparclite :
+ case bfd_mach_sparc_sparclite_le :
+ /* sparclites insns are recognized by default (because that's how
+ they've always been treated, for better or worse). Kludge this by
+ indicating generic v8 is also selected. */
+ return (SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE)
+ | SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8));
+ case bfd_mach_sparc_v8plus :
+ case bfd_mach_sparc_v9 :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9);
+ case bfd_mach_sparc_v8plusa :
+ case bfd_mach_sparc_v9a :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A);
+ case bfd_mach_sparc_v8plusb :
+ case bfd_mach_sparc_v9b :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B);
+ }
+ abort ();
+}
+
+/* Compare opcodes A and B. */
+
+static int
+compare_opcodes (const void *a, const void *b)
+{
+ struct sparc_opcode *op0 = * (struct sparc_opcode **) a;
+ struct sparc_opcode *op1 = * (struct sparc_opcode **) b;
+ unsigned long int match0 = op0->match, match1 = op1->match;
+ unsigned long int lose0 = op0->lose, lose1 = op1->lose;
+ register unsigned int i;
+
+ /* If one (and only one) insn isn't supported by the current architecture,
+ prefer the one that is. If neither are supported, but they're both for
+ the same architecture, continue processing. Otherwise (both unsupported
+ and for different architectures), prefer lower numbered arch's (fudged
+ by comparing the bitmasks). */
+ if (op0->architecture & current_arch_mask)
+ {
+ if (! (op1->architecture & current_arch_mask))
+ return -1;
+ }
+ else
+ {
+ if (op1->architecture & current_arch_mask)
+ return 1;
+ else if (op0->architecture != op1->architecture)
+ return op0->architecture - op1->architecture;
+ }
+
+ /* If a bit is set in both match and lose, there is something
+ wrong with the opcode table. */
+ if (match0 & lose0)
+ {
+ fprintf
+ (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"),
+ op0->name, match0, lose0);
+ op0->lose &= ~op0->match;
+ lose0 = op0->lose;
+ }
+
+ if (match1 & lose1)
+ {
+ fprintf
+ (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"),
+ op1->name, match1, lose1);
+ op1->lose &= ~op1->match;
+ lose1 = op1->lose;
+ }
+
+ /* Because the bits that are variable in one opcode are constant in
+ another, it is important to order the opcodes in the right order. */
+ for (i = 0; i < 32; ++i)
+ {
+ unsigned long int x = 1 << i;
+ int x0 = (match0 & x) != 0;
+ int x1 = (match1 & x) != 0;
+
+ if (x0 != x1)
+ return x1 - x0;
+ }
+
+ for (i = 0; i < 32; ++i)
+ {
+ unsigned long int x = 1 << i;
+ int x0 = (lose0 & x) != 0;
+ int x1 = (lose1 & x) != 0;
+
+ if (x0 != x1)
+ return x1 - x0;
+ }
+
+ /* They are functionally equal. So as long as the opcode table is
+ valid, we can put whichever one first we want, on aesthetic grounds. */
+
+ /* Our first aesthetic ground is that aliases defer to real insns. */
+ {
+ int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS);
+ if (alias_diff != 0)
+ /* Put the one that isn't an alias first. */
+ return alias_diff;
+ }
+
+ /* Except for aliases, two "identical" instructions had
+ better have the same opcode. This is a sanity check on the table. */
+ i = strcmp (op0->name, op1->name);
+ if (i)
+ {
+ if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */
+ return i;
+ else
+ fprintf (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n"),
+ op0->name, op1->name);
+ }
+
+ /* Fewer arguments are preferred. */
+ {
+ int length_diff = strlen (op0->args) - strlen (op1->args);
+ if (length_diff != 0)
+ /* Put the one with fewer arguments first. */
+ return length_diff;
+ }
+
+ /* Put 1+i before i+1. */
+ {
+ char *p0 = (char *) strchr (op0->args, '+');
+ char *p1 = (char *) strchr (op1->args, '+');
+
+ if (p0 && p1)
+ {
+ /* There is a plus in both operands. Note that a plus
+ sign cannot be the first character in args,
+ so the following [-1]'s are valid. */
+ if (p0[-1] == 'i' && p1[1] == 'i')
+ /* op0 is i+1 and op1 is 1+i, so op1 goes first. */
+ return 1;
+ if (p0[1] == 'i' && p1[-1] == 'i')
+ /* op0 is 1+i and op1 is i+1, so op0 goes first. */
+ return -1;
+ }
+ }
+
+ /* Put 1,i before i,1. */
+ {
+ int i0 = strncmp (op0->args, "i,1", 3) == 0;
+ int i1 = strncmp (op1->args, "i,1", 3) == 0;
+
+ if (i0 ^ i1)
+ return i0 - i1;
+ }
+
+ /* They are, as far as we can tell, identical.
+ Since qsort may have rearranged the table partially, there is
+ no way to tell which one was first in the opcode table as
+ written, so just say there are equal. */
+ /* ??? This is no longer true now that we sort a vector of pointers,
+ not the table itself. */
+ return 0;
+}
+
+/* Build a hash table from the opcode table.
+ OPCODE_TABLE is a sorted list of pointers into the opcode table. */
+
+static void
+build_hash_table (opcode_table, hash_table, num_opcodes)
+ const struct sparc_opcode **opcode_table;
+ struct opcode_hash **hash_table;
+ int num_opcodes;
+{
+ register int i;
+ int hash_count[HASH_SIZE];
+ static struct opcode_hash *hash_buf = NULL;
+
+ /* Start at the end of the table and work backwards so that each
+ chain is sorted. */
+
+ memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0]));
+ memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0]));
+ if (hash_buf != NULL)
+ free (hash_buf);
+ hash_buf = (struct opcode_hash *) malloc (sizeof (struct opcode_hash) * num_opcodes);
+ for (i = num_opcodes - 1; i >= 0; --i)
+ {
+ register int hash = HASH_INSN (opcode_table[i]->match);
+ register struct opcode_hash *h = &hash_buf[i];
+ h->next = hash_table[hash];
+ h->opcode = opcode_table[i];
+ hash_table[hash] = h;
+ ++hash_count[hash];
+ }
+
+#if 0 /* for debugging */
+ {
+ int min_count = num_opcodes, max_count = 0;
+ int total;
+
+ for (i = 0; i < HASH_SIZE; ++i)
+ {
+ if (hash_count[i] < min_count)
+ min_count = hash_count[i];
+ if (hash_count[i] > max_count)
+ max_count = hash_count[i];
+ total += hash_count[i];
+ }
+
+ printf ("Opcode hash table stats: min %d, max %d, ave %f\n",
+ min_count, max_count, (double) total / HASH_SIZE);
+ }
+#endif
+}
diff --git a/sparc.ld b/sparc.ld
new file mode 100644
index 0000000..6333928
--- /dev/null
+++ b/sparc.ld
@@ -0,0 +1,128 @@
+OUTPUT_FORMAT("elf32-sparc", "elf32-sparc",
+ "elf32-sparc")
+OUTPUT_ARCH(sparc)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/tap-win32.c b/tap-win32.c
new file mode 100644
index 0000000..af5e25e
--- /dev/null
+++ b/tap-win32.c
@@ -0,0 +1,688 @@
+/*
+ * TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ * on Windows. Originally derived from the CIPE-Win32
+ * project by Damion K. Wilson, with extensive modifications by
+ * James Yonan.
+ *
+ * All source code which derives from the CIPE-Win32 project is
+ * Copyright (C) Damion K. Wilson, 2003, and is released under the
+ * GPL version 2 (see below).
+ *
+ * All other source code is Copyright (C) James Yonan, 2003-2004,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "vl.h"
+#include <stdio.h>
+#include <windows.h>
+
+/* NOTE: PCIBus is redefined in winddk.h */
+#define PCIBus _PCIBus
+#include <ddk/ntapi.h>
+#include <ddk/winddk.h>
+#include <ddk/ntddk.h>
+#undef PCIBus
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define TAPSUFFIX ".tap"
+
+
+//======================
+// Compile time configuration
+//======================
+
+//#define DEBUG_TAP_WIN32 1
+
+#define TUN_ASYNCHRONOUS_WRITES 1
+
+#define TUN_BUFFER_SIZE 1560
+#define TUN_MAX_BUFFER_COUNT 32
+
+/*
+ * The data member "buffer" must be the first element in the tun_buffer
+ * structure. See the function, tap_win32_free_buffer.
+ */
+typedef struct tun_buffer_s {
+ unsigned char buffer [TUN_BUFFER_SIZE];
+ unsigned long read_size;
+ struct tun_buffer_s* next;
+} tun_buffer_t;
+
+typedef struct tap_win32_overlapped {
+ HANDLE handle;
+ HANDLE read_event;
+ HANDLE write_event;
+ HANDLE output_queue_semaphore;
+ HANDLE free_list_semaphore;
+ CRITICAL_SECTION output_queue_cs;
+ CRITICAL_SECTION free_list_cs;
+ OVERLAPPED read_overlapped;
+ OVERLAPPED write_overlapped;
+ tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
+ tun_buffer_t* free_list;
+ tun_buffer_t* output_queue_front;
+ tun_buffer_t* output_queue_back;
+} tap_win32_overlapped_t;
+
+static tap_win32_overlapped_t tap_overlapped;
+
+static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
+{
+ tun_buffer_t* buffer = NULL;
+ WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer = overlapped->free_list;
+// assert(buffer != NULL);
+ overlapped->free_list = buffer->next;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ buffer->next = NULL;
+ return buffer;
+}
+
+static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer->next = overlapped->free_list;
+ overlapped->free_list = buffer;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
+}
+
+static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block)
+{
+ tun_buffer_t* buffer = NULL;
+ DWORD result, timeout = block ? INFINITE : 0L;
+
+ // Non-blocking call
+ result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout);
+
+ switch (result)
+ {
+ // The semaphore object was signaled.
+ case WAIT_OBJECT_0:
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ buffer = overlapped->output_queue_front;
+ overlapped->output_queue_front = buffer->next;
+
+ if(overlapped->output_queue_front == NULL) {
+ overlapped->output_queue_back = NULL;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+ break;
+
+ // Semaphore was nonsignaled, so a time-out occurred.
+ case WAIT_TIMEOUT:
+ // Cannot open another window.
+ break;
+ }
+
+ return buffer;
+}
+
+static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped)
+{
+ return get_buffer_from_output_queue(overlapped, 0);
+}
+
+static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
+ overlapped->output_queue_front = overlapped->output_queue_back = buffer;
+ } else {
+ buffer->next = NULL;
+ overlapped->output_queue_back->next = buffer;
+ overlapped->output_queue_back = buffer;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+
+ ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
+}
+
+
+static int is_tap_win32_dev(const char *guid)
+{
+ HKEY netcard_key;
+ LONG status;
+ DWORD len;
+ int i = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ ADAPTER_KEY,
+ 0,
+ KEY_READ,
+ &netcard_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ for (;;) {
+ char enum_name[256];
+ char unit_string[256];
+ HKEY unit_key;
+ char component_id_string[] = "ComponentId";
+ char component_id[256];
+ char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+ char net_cfg_instance_id[256];
+ DWORD data_type;
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ netcard_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+ ADAPTER_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ unit_string,
+ 0,
+ KEY_READ,
+ &unit_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ } else {
+ len = sizeof (component_id);
+ status = RegQueryValueEx(
+ unit_key,
+ component_id_string,
+ NULL,
+ &data_type,
+ component_id,
+ &len);
+
+ if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
+ len = sizeof (net_cfg_instance_id);
+ status = RegQueryValueEx(
+ unit_key,
+ net_cfg_instance_id_string,
+ NULL,
+ &data_type,
+ net_cfg_instance_id,
+ &len);
+
+ if (status == ERROR_SUCCESS && data_type == REG_SZ) {
+ if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
+ !strcmp (net_cfg_instance_id, guid)) {
+ RegCloseKey (unit_key);
+ RegCloseKey (netcard_key);
+ return TRUE;
+ }
+ }
+ }
+ RegCloseKey (unit_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (netcard_key);
+ return FALSE;
+}
+
+static int get_device_guid(
+ char *name,
+ int name_size,
+ char *actual_name,
+ int actual_name_size)
+{
+ LONG status;
+ HKEY control_net_key;
+ DWORD len;
+ int i = 0;
+ int stop = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ NETWORK_CONNECTIONS_KEY,
+ 0,
+ KEY_READ,
+ &control_net_key);
+
+ if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ while (!stop)
+ {
+ char enum_name[256];
+ char connection_string[256];
+ HKEY connection_key;
+ char name_data[256];
+ DWORD name_type;
+ const char name_string[] = "Name";
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ control_net_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ snprintf(connection_string,
+ sizeof(connection_string),
+ "%s\\%s\\Connection",
+ NETWORK_CONNECTIONS_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ connection_string,
+ 0,
+ KEY_READ,
+ &connection_key);
+
+ if (status == ERROR_SUCCESS) {
+ len = sizeof (name_data);
+ status = RegQueryValueEx(
+ connection_key,
+ name_string,
+ NULL,
+ &name_type,
+ name_data,
+ &len);
+
+ if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+ return -1;
+ }
+ else {
+ if (is_tap_win32_dev(enum_name)) {
+ snprintf(name, name_size, "%s", enum_name);
+ if (actual_name) {
+ if (strcmp(actual_name, "") != 0) {
+ if (strcmp(name_data, actual_name) != 0) {
+ RegCloseKey (connection_key);
+ ++i;
+ continue;
+ }
+ }
+ else {
+ snprintf(actual_name, actual_name_size, "%s", name_data);
+ }
+ }
+ stop = 1;
+ }
+ }
+
+ RegCloseKey (connection_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (control_net_key);
+
+ if (stop == 0)
+ return -1;
+
+ return 0;
+}
+
+static int tap_win32_set_status(HANDLE handle, int status)
+{
+ unsigned long len = 0;
+
+ return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+ &status, sizeof (status),
+ &status, sizeof (status), &len, NULL);
+}
+
+static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
+{
+ overlapped->handle = handle;
+
+ overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ overlapped->read_overlapped.Offset = 0;
+ overlapped->read_overlapped.OffsetHigh = 0;
+ overlapped->read_overlapped.hEvent = overlapped->read_event;
+
+ overlapped->write_overlapped.Offset = 0;
+ overlapped->write_overlapped.OffsetHigh = 0;
+ overlapped->write_overlapped.hEvent = overlapped->write_event;
+
+ InitializeCriticalSection(&overlapped->output_queue_cs);
+ InitializeCriticalSection(&overlapped->free_list_cs);
+
+ overlapped->output_queue_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ 0, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->output_queue_semaphore) {
+ fprintf(stderr, "error creating output queue semaphore!\n");
+ }
+
+ overlapped->free_list_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ TUN_MAX_BUFFER_COUNT, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->free_list_semaphore) {
+ fprintf(stderr, "error creating free list semaphore!\n");
+ }
+
+ overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
+
+ {
+ unsigned index;
+ for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
+ tun_buffer_t* element = &overlapped->buffers[index];
+ element->next = overlapped->free_list;
+ overlapped->free_list = element;
+ }
+ }
+}
+
+static int tap_win32_write(tap_win32_overlapped_t *overlapped,
+ const void *buffer, unsigned long size)
+{
+ unsigned long write_size;
+ BOOL result;
+ DWORD error;
+
+ result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
+ &write_size, FALSE);
+
+ if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+
+ result = WriteFile(overlapped->handle, buffer, size,
+ &write_size, &overlapped->write_overlapped);
+
+ if (!result) {
+ switch (error = GetLastError())
+ {
+ case ERROR_IO_PENDING:
+#ifndef TUN_ASYNCHRONOUS_WRITES
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+#endif
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
+{
+ tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
+ unsigned long read_size;
+ BOOL result;
+ DWORD dwError;
+ tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
+
+
+ for (;;) {
+ result = ReadFile(overlapped->handle,
+ buffer->buffer,
+ sizeof(buffer->buffer),
+ &read_size,
+ &overlapped->read_overlapped);
+ if (!result) {
+ dwError = GetLastError();
+ if (dwError == ERROR_IO_PENDING) {
+ WaitForSingleObject(overlapped->read_event, INFINITE);
+ result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
+ &read_size, FALSE);
+ if (!result) {
+#if DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ dwError = GetLastError();
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ } else {
+#if DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ }
+
+ if(read_size > 0) {
+ buffer->read_size = read_size;
+ put_buffer_on_output_queue(overlapped, buffer);
+ buffer = get_buffer_from_free_list(overlapped);
+ }
+ }
+
+ return 0;
+}
+
+static int tap_win32_read(tap_win32_overlapped_t *overlapped,
+ uint8_t **pbuf, int max_size)
+{
+ int size = 0;
+
+ tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
+
+ if(buffer != NULL) {
+ *pbuf = buffer->buffer;
+ size = (int)buffer->read_size;
+ if(size > max_size) {
+ size = max_size;
+ }
+ }
+
+ return size;
+}
+
+static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
+ char* pbuf)
+{
+ tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
+ put_buffer_on_free_list(overlapped, buffer);
+}
+
+static int tap_win32_open(tap_win32_overlapped_t **phandle,
+ const char *prefered_name)
+{
+ char device_path[256];
+ char device_guid[0x100];
+ int rc;
+ HANDLE handle;
+ BOOL bret;
+ char name_buffer[0x100] = {0, };
+ struct {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long debug;
+ } version;
+ LONG version_len;
+ DWORD idThread;
+ HANDLE hThread;
+
+ if (prefered_name != NULL)
+ snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+
+ rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
+ if (rc)
+ return -1;
+
+ snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ handle = CreateFile (
+ device_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0 );
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+ &version, sizeof (version),
+ &version, sizeof (version), &version_len, NULL);
+
+ if (bret == FALSE) {
+ CloseHandle(handle);
+ return -1;
+ }
+
+ if (!tap_win32_set_status(handle, TRUE)) {
+ return -1;
+ }
+
+ tap_win32_overlapped_init(&tap_overlapped, handle);
+
+ *phandle = &tap_overlapped;
+
+ hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
+ (LPVOID)&tap_overlapped, 0, &idThread);
+ SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
+
+ return 0;
+}
+
+/********************************************/
+
+ typedef struct TAPState {
+ VLANClientState *vc;
+ tap_win32_overlapped_t *handle;
+ HANDLE tap_event;
+ } TAPState;
+
+static TAPState *tap_win32_state = NULL;
+
+static void tap_receive(void *opaque, const uint8_t *buf, int size)
+{
+ TAPState *s = opaque;
+
+ tap_win32_write(s->handle, buf, size);
+}
+
+/* XXX: horrible, suppress this by using proper thread signaling */
+void tap_win32_poll(void)
+{
+ TAPState *s = tap_win32_state;
+ uint8_t *buf;
+ int max_size = 4096;
+ int size;
+
+ if (!s)
+ return;
+
+ size = tap_win32_read(s->handle, &buf, max_size);
+ if (size > 0) {
+ qemu_send_packet(s->vc, buf, size);
+ tap_win32_free_buffer(s->handle, buf);
+ SetEvent(s->tap_event);
+ }
+}
+
+int tap_win32_init(VLANState *vlan, const char *ifname)
+{
+ TAPState *s;
+
+ s = qemu_mallocz(sizeof(TAPState));
+ if (!s)
+ return -1;
+ if (tap_win32_open(&s->handle, ifname) < 0) {
+ printf("tap: Could not open '%s'\n", ifname);
+ return -1;
+ }
+
+ s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "tap: ifname=%s", ifname);
+ tap_win32_state = s;
+
+ s->tap_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!s->tap_event) {
+ fprintf(stderr, "tap-win32: Failed CreateEvent\n");
+ }
+ qemu_add_wait_object(s->tap_event, NULL, NULL);
+ return 0;
+}
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
new file mode 100644
index 0000000..75a1f13
--- /dev/null
+++ b/target-arm/cpu.h
@@ -0,0 +1,226 @@
+/*
+ * ARM virtual CPU header
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_ARM_H
+#define CPU_ARM_H
+
+#define TARGET_LONG_BITS 32
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#define EXCP_UDEF 1 /* undefined instruction */
+#define EXCP_SWI 2 /* software interrupt */
+#define EXCP_PREFETCH_ABORT 3
+#define EXCP_DATA_ABORT 4
+#define EXCP_IRQ 5
+#define EXCP_FIQ 6
+#define EXCP_BKPT 7
+
+/* We currently assume float and double are IEEE single and double
+ precision respectively.
+ Doing runtime conversions is tricky because VFP registers may contain
+ integer values (eg. as the result of a FTOSI instruction).
+ s<2n> maps to the least significant half of d<n>
+ s<2n+1> maps to the most significant half of d<n>
+ */
+
+typedef struct CPUARMState {
+ /* Regs for current mode. */
+ uint32_t regs[16];
+ /* Frequently accessed CPSR bits are stored separately for efficiently.
+ This contains all the other bits. Use cpsr_{read,write} to accless
+ the whole CPSR. */
+ uint32_t uncached_cpsr;
+ uint32_t spsr;
+
+ /* Banked registers. */
+ uint32_t banked_spsr[6];
+ uint32_t banked_r13[6];
+ uint32_t banked_r14[6];
+
+ /* These hold r8-r12. */
+ uint32_t usr_regs[5];
+ uint32_t fiq_regs[5];
+
+ /* cpsr flag cache for faster execution */
+ uint32_t CF; /* 0 or 1 */
+ uint32_t VF; /* V is the bit 31. All other bits are undefined */
+ uint32_t NZF; /* N is bit 31. Z is computed from NZF */
+ uint32_t QF; /* 0 or 1 */
+
+ int thumb; /* 0 = arm mode, 1 = thumb mode */
+
+ /* System control coprocessor (cp15) */
+ struct {
+ uint32_t c0_cpuid;
+ uint32_t c1_sys; /* System control register. */
+ uint32_t c1_coproc; /* Coprocessor access register. */
+ uint32_t c2; /* MMU translation table base. */
+ uint32_t c3; /* MMU domain access control register. */
+ uint32_t c5_insn; /* Fault status registers. */
+ uint32_t c5_data;
+ uint32_t c6_insn; /* Fault address registers. */
+ uint32_t c6_data;
+ uint32_t c9_insn; /* Cache lockdown registers. */
+ uint32_t c9_data;
+ uint32_t c13_fcse; /* FCSE PID. */
+ uint32_t c13_context; /* Context ID. */
+ } cp15;
+
+ /* Internal CPU feature flags. */
+ uint32_t features;
+
+ /* exception/interrupt handling */
+ jmp_buf jmp_env;
+ int exception_index;
+ int interrupt_request;
+ int user_mode_only;
+ int halted;
+
+ /* VFP coprocessor state. */
+ struct {
+ float64 regs[16];
+
+ uint32_t xregs[16];
+ /* We store these fpcsr fields separately for convenience. */
+ int vec_len;
+ int vec_stride;
+
+ /* Temporary variables if we don't have spare fp regs. */
+ float32 tmp0s, tmp1s;
+ float64 tmp0d, tmp1d;
+
+ float_status fp_status;
+ } vfp;
+
+#if defined(CONFIG_USER_ONLY)
+ /* For usermode syscall translation. */
+ int eabi;
+#endif
+
+ CPU_COMMON
+
+} CPUARMState;
+
+CPUARMState *cpu_arm_init(void);
+int cpu_arm_exec(CPUARMState *s);
+void cpu_arm_close(CPUARMState *s);
+void do_interrupt(CPUARMState *);
+void switch_mode(CPUARMState *, int);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+struct siginfo;
+int cpu_arm_signal_handler(int host_signum, struct siginfo *info,
+ void *puc);
+
+#define CPSR_M (0x1f)
+#define CPSR_T (1 << 5)
+#define CPSR_F (1 << 6)
+#define CPSR_I (1 << 7)
+#define CPSR_A (1 << 8)
+#define CPSR_E (1 << 9)
+#define CPSR_IT_2_7 (0xfc00)
+/* Bits 20-23 reserved. */
+#define CPSR_J (1 << 24)
+#define CPSR_IT_0_1 (3 << 25)
+#define CPSR_Q (1 << 27)
+#define CPSR_NZCV (0xf << 28)
+
+#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV)
+/* Return the current CPSR value. */
+static inline uint32_t cpsr_read(CPUARMState *env)
+{
+ int ZF;
+ ZF = (env->NZF == 0);
+ return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
+ (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
+ | (env->thumb << 5);
+}
+
+/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
+static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
+{
+ /* NOTE: N = 1 and Z = 1 cannot be stored currently */
+ if (mask & CPSR_NZCV) {
+ env->NZF = (val & 0xc0000000) ^ 0x40000000;
+ env->CF = (val >> 29) & 1;
+ env->VF = (val << 3) & 0x80000000;
+ }
+ if (mask & CPSR_Q)
+ env->QF = ((val & CPSR_Q) != 0);
+ if (mask & CPSR_T)
+ env->thumb = ((val & CPSR_T) != 0);
+
+ if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
+ switch_mode(env, val & CPSR_M);
+ }
+ mask &= ~CACHED_CPSR_BITS;
+ env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
+}
+
+enum arm_cpu_mode {
+ ARM_CPU_MODE_USR = 0x10,
+ ARM_CPU_MODE_FIQ = 0x11,
+ ARM_CPU_MODE_IRQ = 0x12,
+ ARM_CPU_MODE_SVC = 0x13,
+ ARM_CPU_MODE_ABT = 0x17,
+ ARM_CPU_MODE_UND = 0x1b,
+ ARM_CPU_MODE_SYS = 0x1f
+};
+
+/* VFP system registers. */
+#define ARM_VFP_FPSID 0
+#define ARM_VFP_FPSCR 1
+#define ARM_VFP_FPEXC 8
+#define ARM_VFP_FPINST 9
+#define ARM_VFP_FPINST2 10
+
+
+enum arm_features {
+ ARM_FEATURE_VFP,
+ ARM_FEATURE_AUXCR /* ARM1026 Auxiliary control register. */
+};
+
+static inline int arm_feature(CPUARMState *env, int feature)
+{
+ return (env->features & (1u << feature)) != 0;
+}
+
+void cpu_arm_set_model(CPUARMState *env, uint32_t id);
+
+#define ARM_CPUID_ARM1026 0x4106a262
+#define ARM_CPUID_ARM926 0x41069265
+
+#if defined(CONFIG_USER_ONLY)
+#define TARGET_PAGE_BITS 12
+#else
+/* The ARM MMU allows 1k pages. */
+/* ??? Linux doesn't actually use these, and they're deprecated in recent
+ architecture revisions. Maybe an a configure option to disable them. */
+#define TARGET_PAGE_BITS 10
+#endif
+#include "cpu-all.h"
+
+#endif
diff --git a/target-arm/exec.h b/target-arm/exec.h
new file mode 100644
index 0000000..2d2b99a
--- /dev/null
+++ b/target-arm/exec.h
@@ -0,0 +1,75 @@
+/*
+ * ARM execution defines
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "dyngen-exec.h"
+
+register struct CPUARMState *env asm(AREG0);
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+register uint32_t T2 asm(AREG3);
+
+/* TODO: Put these in FP regs on targets that have such things. */
+/* It is ok for FT0s and FT0d to overlap. Likewise FT1s and FT1d. */
+#define FT0s env->vfp.tmp0s
+#define FT1s env->vfp.tmp1s
+#define FT0d env->vfp.tmp0d
+#define FT1d env->vfp.tmp1d
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu);
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+/* In op_helper.c */
+
+void cpu_lock(void);
+void cpu_unlock(void);
+void helper_set_cp15(CPUState *, uint32_t, uint32_t);
+uint32_t helper_get_cp15(CPUState *, uint32_t);
+
+void cpu_loop_exit(void);
+
+void raise_exception(int);
+
+void do_vfp_abss(void);
+void do_vfp_absd(void);
+void do_vfp_negs(void);
+void do_vfp_negd(void);
+void do_vfp_sqrts(void);
+void do_vfp_sqrtd(void);
+void do_vfp_cmps(void);
+void do_vfp_cmpd(void);
+void do_vfp_cmpes(void);
+void do_vfp_cmped(void);
+void do_vfp_set_fpscr(void);
+void do_vfp_get_fpscr(void);
+
diff --git a/target-arm/helper.c b/target-arm/helper.c
new file mode 100644
index 0000000..2ed46a2
--- /dev/null
+++ b/target-arm/helper.c
@@ -0,0 +1,624 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+void cpu_reset(CPUARMState *env)
+{
+#if defined (CONFIG_USER_ONLY)
+ env->uncached_cpsr = ARM_CPU_MODE_USR;
+ env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30;
+#else
+ /* SVC mode with interrupts disabled. */
+ env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
+ env->vfp.xregs[ARM_VFP_FPEXC] = 0;
+#endif
+ env->regs[15] = 0;
+}
+
+CPUARMState *cpu_arm_init(void)
+{
+ CPUARMState *env;
+
+ env = qemu_mallocz(sizeof(CPUARMState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_reset(env);
+ tlb_flush(env, 1);
+ return env;
+}
+
+static inline void set_feature(CPUARMState *env, int feature)
+{
+ env->features |= 1u << feature;
+}
+
+void cpu_arm_set_model(CPUARMState *env, uint32_t id)
+{
+ env->cp15.c0_cpuid = id;
+ switch (id) {
+ case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_VFP);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
+ break;
+ case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
+ break;
+ default:
+ cpu_abort(env, "Bad CPU ID: %x\n", id);
+ break;
+ }
+}
+
+void cpu_arm_close(CPUARMState *env)
+{
+ free(env);
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ if (rw == 2) {
+ env->exception_index = EXCP_PREFETCH_ABORT;
+ env->cp15.c6_insn = address;
+ } else {
+ env->exception_index = EXCP_DATA_ABORT;
+ env->cp15.c6_data = address;
+ }
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+
+/* These should probably raise undefined insn exceptions. */
+void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
+{
+ cpu_abort(env, "cp15 insn %08x\n", insn);
+}
+
+uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
+{
+ cpu_abort(env, "cp15 insn %08x\n", insn);
+ return 0;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+ if (mode != ARM_CPU_MODE_USR)
+ cpu_abort(env, "Tried to switch out of user mode\n");
+}
+
+#else
+
+/* Map CPU modes onto saved register banks. */
+static inline int bank_number (int mode)
+{
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_SYS:
+ return 0;
+ case ARM_CPU_MODE_SVC:
+ return 1;
+ case ARM_CPU_MODE_ABT:
+ return 2;
+ case ARM_CPU_MODE_UND:
+ return 3;
+ case ARM_CPU_MODE_IRQ:
+ return 4;
+ case ARM_CPU_MODE_FIQ:
+ return 5;
+ }
+ cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
+ return -1;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+ int old_mode;
+ int i;
+
+ old_mode = env->uncached_cpsr & CPSR_M;
+ if (mode == old_mode)
+ return;
+
+ if (old_mode == ARM_CPU_MODE_FIQ) {
+ memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+ } else if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+ }
+
+ i = bank_number(old_mode);
+ env->banked_r13[i] = env->regs[13];
+ env->banked_r14[i] = env->regs[14];
+ env->banked_spsr[i] = env->spsr;
+
+ i = bank_number(mode);
+ env->regs[13] = env->banked_r13[i];
+ env->regs[14] = env->banked_r14[i];
+ env->spsr = env->banked_spsr[i];
+}
+
+/* Handle a CPU exception. */
+void do_interrupt(CPUARMState *env)
+{
+ uint32_t addr;
+ uint32_t mask;
+ int new_mode;
+ uint32_t offset;
+
+ /* TODO: Vectored interrupt controller. */
+ switch (env->exception_index) {
+ case EXCP_UDEF:
+ new_mode = ARM_CPU_MODE_UND;
+ addr = 0x04;
+ mask = CPSR_I;
+ if (env->thumb)
+ offset = 2;
+ else
+ offset = 4;
+ break;
+ case EXCP_SWI:
+ new_mode = ARM_CPU_MODE_SVC;
+ addr = 0x08;
+ mask = CPSR_I;
+ /* The PC already points to the next instructon. */
+ offset = 0;
+ break;
+ case EXCP_PREFETCH_ABORT:
+ case EXCP_BKPT:
+ new_mode = ARM_CPU_MODE_ABT;
+ addr = 0x0c;
+ mask = CPSR_A | CPSR_I;
+ offset = 4;
+ break;
+ case EXCP_DATA_ABORT:
+ new_mode = ARM_CPU_MODE_ABT;
+ addr = 0x10;
+ mask = CPSR_A | CPSR_I;
+ offset = 8;
+ break;
+ case EXCP_IRQ:
+ new_mode = ARM_CPU_MODE_IRQ;
+ addr = 0x18;
+ /* Disable IRQ and imprecise data aborts. */
+ mask = CPSR_A | CPSR_I;
+ offset = 4;
+ break;
+ case EXCP_FIQ:
+ new_mode = ARM_CPU_MODE_FIQ;
+ addr = 0x1c;
+ /* Disable FIQ, IRQ and imprecise data aborts. */
+ mask = CPSR_A | CPSR_I | CPSR_F;
+ offset = 4;
+ break;
+ default:
+ cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+ return; /* Never happens. Keep compiler happy. */
+ }
+ /* High vectors. */
+ if (env->cp15.c1_sys & (1 << 13)) {
+ addr += 0xffff0000;
+ }
+ switch_mode (env, new_mode);
+ env->spsr = cpsr_read(env);
+ /* Switch to the new mode, and switch to Arm mode. */
+ /* ??? Thumb interrupt handlers not implemented. */
+ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
+ env->uncached_cpsr |= mask;
+ env->thumb = 0;
+ env->regs[14] = env->regs[15] + offset;
+ env->regs[15] = addr;
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+/* Check section/page access permissions.
+ Returns the page protection flags, or zero if the access is not
+ permitted. */
+static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
+ int is_user)
+{
+ if (domain == 3)
+ return PAGE_READ | PAGE_WRITE;
+
+ switch (ap) {
+ case 0:
+ if (access_type != 1)
+ return 0;
+ switch ((env->cp15.c1_sys >> 8) & 3) {
+ case 1:
+ return is_user ? 0 : PAGE_READ;
+ case 2:
+ return PAGE_READ;
+ default:
+ return 0;
+ }
+ case 1:
+ return is_user ? 0 : PAGE_READ | PAGE_WRITE;
+ case 2:
+ if (is_user)
+ return (access_type == 1) ? 0 : PAGE_READ;
+ else
+ return PAGE_READ | PAGE_WRITE;
+ case 3:
+ return PAGE_READ | PAGE_WRITE;
+ default:
+ abort();
+ }
+}
+
+static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
+ int is_user, uint32_t *phys_ptr, int *prot)
+{
+ int code;
+ uint32_t table;
+ uint32_t desc;
+ int type;
+ int ap;
+ int domain;
+ uint32_t phys_addr;
+
+ /* Fast Context Switch Extension. */
+ if (address < 0x02000000)
+ address += env->cp15.c13_fcse;
+
+ if ((env->cp15.c1_sys & 1) == 0) {
+ /* MMU diusabled. */
+ *phys_ptr = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else {
+ /* Pagetable walk. */
+ /* Lookup l1 descriptor. */
+ table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc);
+ desc = ldl_phys(table);
+ type = (desc & 3);
+ domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
+ if (type == 0) {
+ /* Secton translation fault. */
+ code = 5;
+ goto do_fault;
+ }
+ if (domain == 0 || domain == 2) {
+ if (type == 2)
+ code = 9; /* Section domain fault. */
+ else
+ code = 11; /* Page domain fault. */
+ goto do_fault;
+ }
+ if (type == 2) {
+ /* 1Mb section. */
+ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
+ ap = (desc >> 10) & 3;
+ code = 13;
+ } else {
+ /* Lookup l2 entry. */
+ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
+ desc = ldl_phys(table);
+ switch (desc & 3) {
+ case 0: /* Page translation fault. */
+ code = 7;
+ goto do_fault;
+ case 1: /* 64k page. */
+ phys_addr = (desc & 0xffff0000) | (address & 0xffff);
+ ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+ break;
+ case 2: /* 4k page. */
+ phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+ ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+ break;
+ case 3: /* 1k page. */
+ if (type == 1) {
+ /* Page translation fault. */
+ code = 7;
+ goto do_fault;
+ }
+ phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
+ ap = (desc >> 4) & 3;
+ break;
+ default:
+ /* Never happens, but compiler isn't smart enough to tell. */
+ abort();
+ }
+ code = 15;
+ }
+ *prot = check_ap(env, ap, domain, access_type, is_user);
+ if (!*prot) {
+ /* Access permission fault. */
+ goto do_fault;
+ }
+ *phys_ptr = phys_addr;
+ }
+ return 0;
+do_fault:
+ return code | (domain << 4);
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
+ int access_type, int is_user, int is_softmmu)
+{
+ uint32_t phys_addr;
+ int prot;
+ int ret;
+
+ ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
+ if (ret == 0) {
+ /* Map a single [sub]page. */
+ phys_addr &= ~(uint32_t)0x3ff;
+ address &= ~(uint32_t)0x3ff;
+ return tlb_set_page (env, address, phys_addr, prot, is_user,
+ is_softmmu);
+ }
+
+ if (access_type == 2) {
+ env->cp15.c5_insn = ret;
+ env->cp15.c6_insn = address;
+ env->exception_index = EXCP_PREFETCH_ABORT;
+ } else {
+ env->cp15.c5_data = ret;
+ env->cp15.c6_data = address;
+ env->exception_index = EXCP_DATA_ABORT;
+ }
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ uint32_t phys_addr;
+ int prot;
+ int ret;
+
+ ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);
+
+ if (ret != 0)
+ return -1;
+
+ return phys_addr;
+}
+
+void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
+{
+ uint32_t op2;
+
+ op2 = (insn >> 5) & 7;
+ switch ((insn >> 16) & 0xf) {
+ case 0: /* ID codes. */
+ goto bad_reg;
+ case 1: /* System configuration. */
+ switch (op2) {
+ case 0:
+ env->cp15.c1_sys = val;
+ /* ??? Lots of these bits are not implemented. */
+ /* This may enable/disable the MMU, so do a TLB flush. */
+ tlb_flush(env, 1);
+ break;
+ case 2:
+ env->cp15.c1_coproc = val;
+ /* ??? Is this safe when called from within a TB? */
+ tb_flush(env);
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 2: /* MMU Page table control. */
+ env->cp15.c2 = val;
+ break;
+ case 3: /* MMU Domain access control. */
+ env->cp15.c3 = val;
+ break;
+ case 4: /* Reserved. */
+ goto bad_reg;
+ case 5: /* MMU Fault status. */
+ switch (op2) {
+ case 0:
+ env->cp15.c5_data = val;
+ break;
+ case 1:
+ env->cp15.c5_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 6: /* MMU Fault address. */
+ switch (op2) {
+ case 0:
+ env->cp15.c6_data = val;
+ break;
+ case 1:
+ env->cp15.c6_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 7: /* Cache control. */
+ /* No cache, so nothing to do. */
+ break;
+ case 8: /* MMU TLB control. */
+ switch (op2) {
+ case 0: /* Invalidate all. */
+ tlb_flush(env, 0);
+ break;
+ case 1: /* Invalidate single TLB entry. */
+#if 0
+ /* ??? This is wrong for large pages and sections. */
+ /* As an ugly hack to make linux work we always flush a 4K
+ pages. */
+ val &= 0xfffff000;
+ tlb_flush_page(env, val);
+ tlb_flush_page(env, val + 0x400);
+ tlb_flush_page(env, val + 0x800);
+ tlb_flush_page(env, val + 0xc00);
+#else
+ tlb_flush(env, 1);
+#endif
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 9: /* Cache lockdown. */
+ switch (op2) {
+ case 0:
+ env->cp15.c9_data = val;
+ break;
+ case 1:
+ env->cp15.c9_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 10: /* MMU TLB lockdown. */
+ /* ??? TLB lockdown not implemented. */
+ break;
+ case 11: /* TCM DMA control. */
+ case 12: /* Reserved. */
+ goto bad_reg;
+ case 13: /* Process ID. */
+ switch (op2) {
+ case 0:
+ /* Unlike real hardware the qemu TLB uses virtual addresses,
+ not modified virtual addresses, so this causes a TLB flush.
+ */
+ if (env->cp15.c13_fcse != val)
+ tlb_flush(env, 1);
+ env->cp15.c13_fcse = val;
+ break;
+ case 1:
+ /* This changes the ASID, so do a TLB flush. */
+ if (env->cp15.c13_context != val)
+ tlb_flush(env, 0);
+ env->cp15.c13_context = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 14: /* Reserved. */
+ goto bad_reg;
+ case 15: /* Implementation specific. */
+ /* ??? Internal registers not implemented. */
+ break;
+ }
+ return;
+bad_reg:
+ /* ??? For debugging only. Should raise illegal instruction exception. */
+ cpu_abort(env, "Unimplemented cp15 register read\n");
+}
+
+uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
+{
+ uint32_t op2;
+
+ op2 = (insn >> 5) & 7;
+ switch ((insn >> 16) & 0xf) {
+ case 0: /* ID codes. */
+ switch (op2) {
+ default: /* Device ID. */
+ return env->cp15.c0_cpuid;
+ case 1: /* Cache Type. */
+ return 0x1dd20d2;
+ case 2: /* TCM status. */
+ return 0;
+ }
+ case 1: /* System configuration. */
+ switch (op2) {
+ case 0: /* Control register. */
+ return env->cp15.c1_sys;
+ case 1: /* Auxiliary control register. */
+ if (arm_feature(env, ARM_FEATURE_AUXCR))
+ return 1;
+ goto bad_reg;
+ case 2: /* Coprocessor access register. */
+ return env->cp15.c1_coproc;
+ default:
+ goto bad_reg;
+ }
+ case 2: /* MMU Page table control. */
+ return env->cp15.c2;
+ case 3: /* MMU Domain access control. */
+ return env->cp15.c3;
+ case 4: /* Reserved. */
+ goto bad_reg;
+ case 5: /* MMU Fault status. */
+ switch (op2) {
+ case 0:
+ return env->cp15.c5_data;
+ case 1:
+ return env->cp15.c5_insn;
+ default:
+ goto bad_reg;
+ }
+ case 6: /* MMU Fault address. */
+ switch (op2) {
+ case 0:
+ return env->cp15.c6_data;
+ case 1:
+ /* Arm9 doesn't have an IFAR, but implementing it anyway shouldn't
+ do any harm. */
+ return env->cp15.c6_insn;
+ default:
+ goto bad_reg;
+ }
+ case 7: /* Cache control. */
+ /* ??? This is for test, clean and invaidate operations that set the
+ Z flag. We can't represent N = Z = 1, so it also clears clears
+ the N flag. Oh well. */
+ env->NZF = 0;
+ return 0;
+ case 8: /* MMU TLB control. */
+ goto bad_reg;
+ case 9: /* Cache lockdown. */
+ switch (op2) {
+ case 0:
+ return env->cp15.c9_data;
+ case 1:
+ return env->cp15.c9_insn;
+ default:
+ goto bad_reg;
+ }
+ case 10: /* MMU TLB lockdown. */
+ /* ??? TLB lockdown not implemented. */
+ return 0;
+ case 11: /* TCM DMA control. */
+ case 12: /* Reserved. */
+ goto bad_reg;
+ case 13: /* Process ID. */
+ switch (op2) {
+ case 0:
+ return env->cp15.c13_fcse;
+ case 1:
+ return env->cp15.c13_context;
+ default:
+ goto bad_reg;
+ }
+ case 14: /* Reserved. */
+ goto bad_reg;
+ case 15: /* Implementation specific. */
+ /* ??? Internal registers not implemented. */
+ return 0;
+ }
+bad_reg:
+ /* ??? For debugging only. Should raise illegal instruction exception. */
+ cpu_abort(env, "Unimplemented cp15 register read\n");
+ return 0;
+}
+
+#endif
diff --git a/target-arm/nwfpe/double_cpdo.c b/target-arm/nwfpe/double_cpdo.c
new file mode 100644
index 0000000..944083a
--- /dev/null
+++ b/target-arm/nwfpe/double_cpdo.c
@@ -0,0 +1,296 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+float64 float64_exp(float64 Fm);
+float64 float64_ln(float64 Fm);
+float64 float64_sin(float64 rFm);
+float64 float64_cos(float64 rFm);
+float64 float64_arcsin(float64 rFm);
+float64 float64_arctan(float64 rFm);
+float64 float64_log(float64 rFm);
+float64 float64_tan(float64 rFm);
+float64 float64_arccos(float64 rFm);
+float64 float64_pow(float64 rFn,float64 rFm);
+float64 float64_pol(float64 rFn,float64 rFm);
+
+unsigned int DoubleCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float64 rFm, rFn = 0;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //printk("DoubleCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getDoubleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = float32_to_float64(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFm = fpa11->fpreg[Fm].fDouble;
+ break;
+
+ case typeExtended:
+ // !! patb
+ //printk("not implemented! why not?\n");
+ //!! ScottB
+ // should never get here, if extended involved
+ // then other operand should be promoted then
+ // ExtendedCPDO called.
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFn = fpa11->fpreg[Fn].fDouble;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fDouble = rFm;
+ break;
+
+ case MNF_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+#ifdef WORDS_BIGENDIAN
+ p[0] ^= 0x80000000;
+#else
+ p[1] ^= 0x80000000;
+#endif
+ fpa11->fpreg[Fd].fDouble = rFm;
+ }
+ break;
+
+ case ABS_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+#ifdef WORDS_BIGENDIAN
+ p[0] &= 0x7fffffff;
+#else
+ p[1] &= 0x7fffffff;
+#endif
+ fpa11->fpreg[Fd].fDouble = rFm;
+ }
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeDouble;
+ return nRc;
+}
+
+#if 0
+float64 float64_exp(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_ln(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_sin(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_cos(float64 rFm)
+{
+ return rFm;
+ //series
+}
+
+#if 0
+float64 float64_arcsin(float64 rFm)
+{
+//series
+}
+
+float64 float64_arctan(float64 rFm)
+{
+ //series
+}
+#endif
+
+float64 float64_log(float64 rFm)
+{
+ return float64_div(float64_ln(rFm),getDoubleConstant(7));
+}
+
+float64 float64_tan(float64 rFm)
+{
+ return float64_div(float64_sin(rFm),float64_cos(rFm));
+}
+
+float64 float64_arccos(float64 rFm)
+{
+return rFm;
+ //return float64_sub(halfPi,float64_arcsin(rFm));
+}
+
+float64 float64_pow(float64 rFn,float64 rFm)
+{
+ return float64_exp(float64_mul(rFm,float64_ln(rFn)));
+}
+
+float64 float64_pol(float64 rFn,float64 rFm)
+{
+ return float64_arctan(float64_div(rFn,rFm));
+}
+#endif
diff --git a/target-arm/nwfpe/extended_cpdo.c b/target-arm/nwfpe/extended_cpdo.c
new file mode 100644
index 0000000..f5ef623
--- /dev/null
+++ b/target-arm/nwfpe/extended_cpdo.c
@@ -0,0 +1,273 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+floatx80 floatx80_exp(floatx80 Fm);
+floatx80 floatx80_ln(floatx80 Fm);
+floatx80 floatx80_sin(floatx80 rFm);
+floatx80 floatx80_cos(floatx80 rFm);
+floatx80 floatx80_arcsin(floatx80 rFm);
+floatx80 floatx80_arctan(floatx80 rFm);
+floatx80 floatx80_log(floatx80 rFm);
+floatx80 floatx80_tan(floatx80 rFm);
+floatx80 floatx80_arccos(floatx80 rFm);
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm);
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm);
+
+unsigned int ExtendedCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ floatx80 rFm, rFn;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //printk("ExtendedCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getExtendedConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ rFm = fpa11->fpreg[Fm].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ rFn = fpa11->fpreg[Fn].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case MNF_CODE:
+ rFm.high ^= 0x8000;
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case ABS_CODE:
+ rFm.high &= 0x7fff;
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeExtended;
+ return nRc;
+}
+
+#if 0
+floatx80 floatx80_exp(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_ln(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_sin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_cos(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arcsin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arctan(floatx80 rFm)
+{
+ //series
+}
+
+floatx80 floatx80_log(floatx80 rFm)
+{
+ return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7));
+}
+
+floatx80 floatx80_tan(floatx80 rFm)
+{
+ return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm));
+}
+
+floatx80 floatx80_arccos(floatx80 rFm)
+{
+ //return floatx80_sub(halfPi,floatx80_arcsin(rFm));
+}
+
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn)));
+}
+
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_arctan(floatx80_div(rFn,rFm));
+}
+#endif
diff --git a/target-arm/nwfpe/fpa11.c b/target-arm/nwfpe/fpa11.c
new file mode 100644
index 0000000..a8141e7
--- /dev/null
+++ b/target-arm/nwfpe/fpa11.c
@@ -0,0 +1,237 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+
+#include "fpopcode.h"
+
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+//#include <asm/system.h>
+
+#include <stdio.h>
+
+/* forward declarations */
+unsigned int EmulateCPDO(const unsigned int);
+unsigned int EmulateCPDT(const unsigned int);
+unsigned int EmulateCPRT(const unsigned int);
+
+FPA11* qemufpa=0;
+CPUARMState* user_registers;
+
+/* Reset the FPA11 chip. Called to initialize and reset the emulator. */
+void resetFPA11(void)
+{
+ int i;
+ FPA11 *fpa11 = GET_FPA11();
+
+ /* initialize the register type array */
+ for (i=0;i<=7;i++)
+ {
+ fpa11->fType[i] = typeNone;
+ }
+
+ /* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
+ fpa11->fpsr = FP_EMULATOR | BIT_AC;
+
+ /* FPCR: set SB, AB and DA bits, clear all others */
+#if MAINTAIN_FPCR
+ fpa11->fpcr = MASK_RESET;
+#endif
+}
+
+void SetRoundingMode(const unsigned int opcode)
+{
+ int rounding_mode;
+ FPA11 *fpa11 = GET_FPA11();
+
+#if MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_MODE;
+#endif
+ switch (opcode & MASK_ROUNDING_MODE)
+ {
+ default:
+ case ROUND_TO_NEAREST:
+ rounding_mode = float_round_nearest_even;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_NEAREST;
+#endif
+ break;
+
+ case ROUND_TO_PLUS_INFINITY:
+ rounding_mode = float_round_up;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_MINUS_INFINITY:
+ rounding_mode = float_round_down;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_ZERO:
+ rounding_mode = float_round_to_zero;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_ZERO;
+#endif
+ break;
+ }
+ set_float_rounding_mode(rounding_mode, &fpa11->fp_status);
+}
+
+void SetRoundingPrecision(const unsigned int opcode)
+{
+ int rounding_precision;
+ FPA11 *fpa11 = GET_FPA11();
+#if MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
+#endif
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ rounding_precision = 32;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_SINGLE;
+#endif
+ break;
+
+ case ROUND_DOUBLE:
+ rounding_precision = 64;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_DOUBLE;
+#endif
+ break;
+
+ case ROUND_EXTENDED:
+ rounding_precision = 80;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_EXTENDED;
+#endif
+ break;
+
+ default: rounding_precision = 80;
+ }
+ set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status);
+}
+
+/* Emulate the instruction in the opcode. */
+/* ??? This is not thread safe. */
+unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs)
+{
+ unsigned int nRc = 0;
+// unsigned long flags;
+ FPA11 *fpa11;
+// save_flags(flags); sti();
+
+ qemufpa=qfpa;
+ user_registers=qregs;
+
+#if 0
+ fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n",
+ opcode, qregs[REG_PC]);
+#endif
+ fpa11 = GET_FPA11();
+
+ if (fpa11->initflag == 0) /* good place for __builtin_expect */
+ {
+ resetFPA11();
+ SetRoundingMode(ROUND_TO_NEAREST);
+ SetRoundingPrecision(ROUND_EXTENDED);
+ fpa11->initflag = 1;
+ }
+
+ if (TEST_OPCODE(opcode,MASK_CPRT))
+ {
+ //fprintf(stderr,"emulating CPRT\n");
+ /* Emulate conversion opcodes. */
+ /* Emulate register transfer opcodes. */
+ /* Emulate comparison opcodes. */
+ nRc = EmulateCPRT(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDO))
+ {
+ //fprintf(stderr,"emulating CPDO\n");
+ /* Emulate monadic arithmetic opcodes. */
+ /* Emulate dyadic arithmetic opcodes. */
+ nRc = EmulateCPDO(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDT))
+ {
+ //fprintf(stderr,"emulating CPDT\n");
+ /* Emulate load/store opcodes. */
+ /* Emulate load/store multiple opcodes. */
+ nRc = EmulateCPDT(opcode);
+ }
+ else
+ {
+ /* Invalid instruction detected. Return FALSE. */
+ nRc = 0;
+ }
+
+// restore_flags(flags);
+
+ //printf("returning %d\n",nRc);
+ return(nRc);
+}
+
+#if 0
+unsigned int EmulateAll1(unsigned int opcode)
+{
+ switch ((opcode >> 24) & 0xf)
+ {
+ case 0xc:
+ case 0xd:
+ if ((opcode >> 20) & 0x1)
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformLDF(opcode); break;
+ case 0x2: return PerformLFM(opcode); break;
+ default: return 0;
+ }
+ }
+ else
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformSTF(opcode); break;
+ case 0x2: return PerformSFM(opcode); break;
+ default: return 0;
+ }
+ }
+ break;
+
+ case 0xe:
+ if (opcode & 0x10)
+ return EmulateCPDO(opcode);
+ else
+ return EmulateCPRT(opcode);
+ break;
+
+ default: return 0;
+ }
+}
+#endif
+
diff --git a/target-arm/nwfpe/fpa11.h b/target-arm/nwfpe/fpa11.h
new file mode 100644
index 0000000..8751696
--- /dev/null
+++ b/target-arm/nwfpe/fpa11.h
@@ -0,0 +1,122 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPA11_H__
+#define __FPA11_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <cpu.h>
+
+#define GET_FPA11() (qemufpa)
+
+/*
+ * The processes registers are always at the very top of the 8K
+ * stack+task struct. Use the same method as 'current' uses to
+ * reach them.
+ */
+extern CPUARMState *user_registers;
+
+#define GET_USERREG() (user_registers)
+
+/* Need task_struct */
+//#include <linux/sched.h>
+
+/* includes */
+#include "fpsr.h" /* FP control and status register definitions */
+#include "softfloat.h"
+
+#define typeNone 0x00
+#define typeSingle 0x01
+#define typeDouble 0x02
+#define typeExtended 0x03
+
+/*
+ * This must be no more and no less than 12 bytes.
+ */
+typedef union tagFPREG {
+ floatx80 fExtended;
+ float64 fDouble;
+ float32 fSingle;
+} FPREG;
+
+/*
+ * FPA11 device model.
+ *
+ * This structure is exported to user space. Do not re-order.
+ * Only add new stuff to the end, and do not change the size of
+ * any element. Elements of this structure are used by user
+ * space, and must match struct user_fp in include/asm-arm/user.h.
+ * We include the byte offsets below for documentation purposes.
+ *
+ * The size of this structure and FPREG are checked by fpmodule.c
+ * on initialisation. If the rules have been broken, NWFPE will
+ * not initialise.
+ */
+typedef struct tagFPA11 {
+/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */
+/* 96 */ FPSR fpsr; /* floating point status register */
+/* 100 */ FPCR fpcr; /* floating point control register */
+/* 104 */ unsigned char fType[8]; /* type of floating point value held in
+ floating point registers. One of none
+ single, double or extended. */
+/* 112 */ int initflag; /* this is special. The kernel guarantees
+ to set it to 0 when a thread is launched,
+ so we can use it to detect whether this
+ instance of the emulator needs to be
+ initialised. */
+ float_status fp_status; /* QEMU float emulator status */
+} FPA11;
+
+extern FPA11* qemufpa;
+
+extern void resetFPA11(void);
+extern void SetRoundingMode(const unsigned int);
+extern void SetRoundingPrecision(const unsigned int);
+
+static inline unsigned int readRegister(unsigned int reg)
+{
+ return (user_registers->regs[(reg)]);
+}
+
+static inline void writeRegister(unsigned int x, unsigned int y)
+{
+#if 0
+ printf("writing %d to r%d\n",y,x);
+#endif
+ user_registers->regs[(x)]=(y);
+}
+
+static inline void writeConditionCodes(unsigned int x)
+{
+ cpsr_write(user_registers,x,CPSR_NZCV);
+}
+
+#define REG_PC 15
+
+unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs);
+
+/* included only for get_user/put_user macros */
+#include "qemu.h"
+
+#endif
diff --git a/target-arm/nwfpe/fpa11.inl b/target-arm/nwfpe/fpa11.inl
new file mode 100644
index 0000000..7183ec9
--- /dev/null
+++ b/target-arm/nwfpe/fpa11.inl
@@ -0,0 +1,51 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+
+/* Read and write floating point status register */
+static inline unsigned int readFPSR(void)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ return(fpa11->fpsr);
+}
+
+static inline void writeFPSR(FPSR reg)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ /* the sysid byte in the status register is readonly */
+ fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
+}
+
+/* Read and write floating point control register */
+static inline FPCR readFPCR(void)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ /* clear SB, AB and DA bits before returning FPCR */
+ return(fpa11->fpcr & ~MASK_RFC);
+}
+
+static inline void writeFPCR(FPCR reg)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */
+ fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */
+}
diff --git a/target-arm/nwfpe/fpa11_cpdo.c b/target-arm/nwfpe/fpa11_cpdo.c
new file mode 100644
index 0000000..cc8aa87
--- /dev/null
+++ b/target-arm/nwfpe/fpa11_cpdo.c
@@ -0,0 +1,117 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "fpopcode.h"
+
+unsigned int SingleCPDO(const unsigned int opcode);
+unsigned int DoubleCPDO(const unsigned int opcode);
+unsigned int ExtendedCPDO(const unsigned int opcode);
+
+unsigned int EmulateCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int Fd, nType, nDest, nRc = 1;
+
+ //printk("EmulateCPDO(0x%08x)\n",opcode);
+
+ /* Get the destination size. If not valid let Linux perform
+ an invalid instruction trap. */
+ nDest = getDestinationSize(opcode);
+ if (typeNone == nDest) return 0;
+
+ SetRoundingMode(opcode);
+
+ /* Compare the size of the operands in Fn and Fm.
+ Choose the largest size and perform operations in that size,
+ in order to make use of all the precision of the operands.
+ If Fm is a constant, we just grab a constant of a size
+ matching the size of the operand in Fn. */
+ if (MONADIC_INSTRUCTION(opcode))
+ nType = nDest;
+ else
+ nType = fpa11->fType[getFn(opcode)];
+
+ if (!CONSTANT_FM(opcode))
+ {
+ register unsigned int Fm = getFm(opcode);
+ if (nType < fpa11->fType[Fm])
+ {
+ nType = fpa11->fType[Fm];
+ }
+ }
+
+ switch (nType)
+ {
+ case typeSingle : nRc = SingleCPDO(opcode); break;
+ case typeDouble : nRc = DoubleCPDO(opcode); break;
+ case typeExtended : nRc = ExtendedCPDO(opcode); break;
+ default : nRc = 0;
+ }
+
+ /* If the operation succeeded, check to see if the result in the
+ destination register is the correct size. If not force it
+ to be. */
+ Fd = getFd(opcode);
+ nType = fpa11->fType[Fd];
+ if ((0 != nRc) && (nDest != nType))
+ {
+ switch (nDest)
+ {
+ case typeSingle:
+ {
+ if (typeDouble == nType)
+ fpa11->fpreg[Fd].fSingle =
+ float64_to_float32(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fSingle =
+ floatx80_to_float32(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status);
+ }
+ break;
+
+ case typeDouble:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fDouble =
+ float32_to_float64(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fDouble =
+ floatx80_to_float64(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status);
+ }
+ break;
+
+ case typeExtended:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fExtended =
+ float32_to_floatx80(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fExtended =
+ float64_to_floatx80(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status);
+ }
+ break;
+ }
+
+ fpa11->fType[Fd] = nDest;
+ }
+
+ return nRc;
+}
diff --git a/target-arm/nwfpe/fpa11_cpdt.c b/target-arm/nwfpe/fpa11_cpdt.c
new file mode 100644
index 0000000..914a86f
--- /dev/null
+++ b/target-arm/nwfpe/fpa11_cpdt.c
@@ -0,0 +1,376 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+ (c) Philip Blundell, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+//#include <asm/uaccess.h>
+
+static inline
+void loadSingle(const unsigned int Fn,const unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ fpa11->fType[Fn] = typeSingle;
+ fpa11->fpreg[Fn].fSingle = tget32(addr);
+}
+
+static inline
+void loadDouble(const unsigned int Fn,const unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fDouble;
+ fpa11->fType[Fn] = typeDouble;
+#ifdef WORDS_BIGENDIAN
+ p[0] = tget32(addr); /* sign & exponent */
+ p[1] = tget32(addr + 4);
+#else
+ p[0] = tget32(addr + 4);
+ p[1] = tget32(addr); /* sign & exponent */
+#endif
+}
+
+static inline
+void loadExtended(const unsigned int Fn,const unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fExtended;
+ fpa11->fType[Fn] = typeExtended;
+ p[0] = tget32(addr); /* sign & exponent */
+ p[1] = tget32(addr + 8); /* ls bits */
+ p[2] = tget32(addr + 4); /* ms bits */
+}
+
+static inline
+void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ register unsigned int *p;
+ unsigned long x;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn]);
+ x = tget32(addr);
+ fpa11->fType[Fn] = (x >> 14) & 0x00000003;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ p[0] = tget32(addr + 8); /* Single */
+ p[1] = tget32(addr + 4); /* double msw */
+ p[2] = 0; /* empty */
+ }
+ break;
+
+ case typeExtended:
+ {
+ p[1] = tget32(addr + 8);
+ p[2] = tget32(addr + 4); /* msw */
+ p[0] = (x & 0x80003fff);
+ }
+ break;
+ }
+}
+
+static inline
+void storeSingle(const unsigned int Fn,unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ float32 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeDouble:
+ val = float64_to_float32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fSingle;
+ }
+
+ tput32(addr, p[0]);
+}
+
+static inline
+void storeDouble(const unsigned int Fn,unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ float64 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ val = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float64(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fDouble;
+ }
+#ifdef WORDS_BIGENDIAN
+ tput32(addr, p[0]); /* msw */
+ tput32(addr + 4, p[1]); /* lsw */
+#else
+ tput32(addr, p[1]); /* msw */
+ tput32(addr + 4, p[0]); /* lsw */
+#endif
+}
+
+static inline
+void storeExtended(const unsigned int Fn,unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ floatx80 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fExtended;
+ }
+
+ tput32(addr, p[0]); /* sign & exp */
+ tput32(addr + 8, p[1]);
+ tput32(addr + 4, p[2]); /* msw */
+}
+
+static inline
+void storeMultiple(const unsigned int Fn,unsigned int *pMem)
+{
+ target_ulong addr = (target_ulong)(long)pMem;
+ FPA11 *fpa11 = GET_FPA11();
+ register unsigned int nType, *p;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn]);
+ nType = fpa11->fType[Fn];
+
+ switch (nType)
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ tput32(addr + 8, p[0]); /* single */
+ tput32(addr + 4, p[1]); /* double msw */
+ tput32(addr, nType << 14);
+ }
+ break;
+
+ case typeExtended:
+ {
+ tput32(addr + 4, p[2]); /* msw */
+ tput32(addr + 8, p[1]);
+ tput32(addr, (p[0] & 0x80003fff) | (nType << 14));
+ }
+ break;
+ }
+}
+
+unsigned int PerformLDF(const unsigned int opcode)
+{
+ unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
+ write_back = WRITE_BACK(opcode);
+
+ //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 2;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : loadSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : loadDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+unsigned int PerformSTF(const unsigned int opcode)
+{
+ unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
+ write_back = WRITE_BACK(opcode);
+
+ //printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+ SetRoundingMode(ROUND_TO_NEAREST);
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 2;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+unsigned int PerformLFM(const unsigned int opcode)
+{
+ unsigned int i, Fd, *pBase, *pAddress, *pFinal,
+ write_back = WRITE_BACK(opcode);
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 2;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ loadMultiple(Fd,pAddress);
+ pAddress += 3; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+unsigned int PerformSFM(const unsigned int opcode)
+{
+ unsigned int i, Fd, *pBase, *pAddress, *pFinal,
+ write_back = WRITE_BACK(opcode);
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 2;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ storeMultiple(Fd,pAddress);
+ pAddress += 3; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+#if 1
+unsigned int EmulateCPDT(const unsigned int opcode)
+{
+ unsigned int nRc = 0;
+
+ //printk("EmulateCPDT(0x%08x)\n",opcode);
+
+ if (LDF_OP(opcode))
+ {
+ nRc = PerformLDF(opcode);
+ }
+ else if (LFM_OP(opcode))
+ {
+ nRc = PerformLFM(opcode);
+ }
+ else if (STF_OP(opcode))
+ {
+ nRc = PerformSTF(opcode);
+ }
+ else if (SFM_OP(opcode))
+ {
+ nRc = PerformSFM(opcode);
+ }
+ else
+ {
+ nRc = 0;
+ }
+
+ return nRc;
+}
+#endif
diff --git a/target-arm/nwfpe/fpa11_cprt.c b/target-arm/nwfpe/fpa11_cprt.c
new file mode 100644
index 0000000..91f2d80
--- /dev/null
+++ b/target-arm/nwfpe/fpa11_cprt.c
@@ -0,0 +1,290 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+ (c) Philip Blundell, 1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.inl"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+extern flag floatx80_is_nan(floatx80);
+extern flag float64_is_nan( float64);
+extern flag float32_is_nan( float32);
+
+void SetRoundingMode(const unsigned int opcode);
+
+unsigned int PerformFLT(const unsigned int opcode);
+unsigned int PerformFIX(const unsigned int opcode);
+
+static unsigned int
+PerformComparison(const unsigned int opcode);
+
+unsigned int EmulateCPRT(const unsigned int opcode)
+{
+ unsigned int nRc = 1;
+
+ //printk("EmulateCPRT(0x%08x)\n",opcode);
+
+ if (opcode & 0x800000)
+ {
+ /* This is some variant of a comparison (PerformComparison will
+ sort out which one). Since most of the other CPRT
+ instructions are oddball cases of some sort or other it makes
+ sense to pull this out into a fast path. */
+ return PerformComparison(opcode);
+ }
+
+ /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
+ switch ((opcode & 0x700000) >> 20)
+ {
+ case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
+ case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
+
+ case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
+ case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
+
+#if 0 /* We currently have no use for the FPCR, so there's no point
+ in emulating it. */
+ case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
+ case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
+#endif
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFLT(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+
+ unsigned int nRc = 1;
+ SetRoundingMode(opcode);
+
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ {
+ fpa11->fType[getFn(opcode)] = typeSingle;
+ fpa11->fpreg[getFn(opcode)].fSingle =
+ int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ case ROUND_DOUBLE:
+ {
+ fpa11->fType[getFn(opcode)] = typeDouble;
+ fpa11->fpreg[getFn(opcode)].fDouble =
+ int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ case ROUND_EXTENDED:
+ {
+ fpa11->fType[getFn(opcode)] = typeExtended;
+ fpa11->fpreg[getFn(opcode)].fExtended =
+ int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFIX(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int nRc = 1;
+ unsigned int Fn = getFm(opcode);
+
+ SetRoundingMode(opcode);
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ {
+ writeRegister(getRd(opcode),
+ float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
+ }
+ break;
+
+ case typeDouble:
+ {
+ //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
+ writeRegister(getRd(opcode),
+ float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
+ }
+ break;
+
+ case typeExtended:
+ {
+ writeRegister(getRd(opcode),
+ floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+
+static unsigned int __inline__
+PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int flags = 0;
+
+ /* test for less than condition */
+ if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
+ {
+ flags |= CC_NEGATIVE;
+ }
+
+ /* test for equal condition */
+ if (floatx80_eq(Fn,Fm, &fpa11->fp_status))
+ {
+ flags |= CC_ZERO;
+ }
+
+ /* test for greater than or equal condition */
+ if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
+ {
+ flags |= CC_CARRY;
+ }
+
+ writeConditionCodes(flags);
+ return 1;
+}
+
+/* This instruction sets the flags N, Z, C, V in the FPSR. */
+
+static unsigned int PerformComparison(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int Fn, Fm;
+ floatx80 rFn, rFm;
+ int e_flag = opcode & 0x400000; /* 1 if CxFE */
+ int n_flag = opcode & 0x200000; /* 1 if CNxx */
+ unsigned int flags = 0;
+
+ //printk("PerformComparison(0x%08x)\n",opcode);
+
+ Fn = getFn(opcode);
+ Fm = getFm(opcode);
+
+ /* Check for unordered condition and convert all operands to 80-bit
+ format.
+ ?? Might be some mileage in avoiding this conversion if possible.
+ Eg, if both operands are 32-bit, detect this and do a 32-bit
+ comparison (cheaper than an 80-bit one). */
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ //printk("single.\n");
+ if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
+ goto unordered;
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ //printk("double.\n");
+ if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
+ goto unordered;
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ //printk("extended.\n");
+ if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
+ goto unordered;
+ rFn = fpa11->fpreg[Fn].fExtended;
+ break;
+
+ default: return 0;
+ }
+
+ if (CONSTANT_FM(opcode))
+ {
+ //printk("Fm is a constant: #%d.\n",Fm);
+ rFm = getExtendedConstant(Fm);
+ if (floatx80_is_nan(rFm))
+ goto unordered;
+ }
+ else
+ {
+ //printk("Fm = r%d which contains a ",Fm);
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ //printk("single.\n");
+ if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
+ goto unordered;
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ //printk("double.\n");
+ if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
+ goto unordered;
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ //printk("extended.\n");
+ if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
+ goto unordered;
+ rFm = fpa11->fpreg[Fm].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (n_flag)
+ {
+ rFm.high ^= 0x8000;
+ }
+
+ return PerformComparisonOperation(rFn,rFm);
+
+ unordered:
+ /* ?? The FPA data sheet is pretty vague about this, in particular
+ about whether the non-E comparisons can ever raise exceptions.
+ This implementation is based on a combination of what it says in
+ the data sheet, observation of how the Acorn emulator actually
+ behaves (and how programs expect it to) and guesswork. */
+ flags |= CC_OVERFLOW;
+ flags &= ~(CC_ZERO | CC_NEGATIVE);
+
+ if (BIT_AC & readFPSR()) flags |= CC_CARRY;
+
+ if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
+
+ writeConditionCodes(flags);
+ return 1;
+}
diff --git a/target-arm/nwfpe/fpopcode.c b/target-arm/nwfpe/fpopcode.c
new file mode 100644
index 0000000..d29e913
--- /dev/null
+++ b/target-arm/nwfpe/fpopcode.c
@@ -0,0 +1,148 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpsr.h"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+const floatx80 floatx80Constant[] = {
+ { 0x0000000000000000ULL, 0x0000}, /* extended 0.0 */
+ { 0x8000000000000000ULL, 0x3fff}, /* extended 1.0 */
+ { 0x8000000000000000ULL, 0x4000}, /* extended 2.0 */
+ { 0xc000000000000000ULL, 0x4000}, /* extended 3.0 */
+ { 0x8000000000000000ULL, 0x4001}, /* extended 4.0 */
+ { 0xa000000000000000ULL, 0x4001}, /* extended 5.0 */
+ { 0x8000000000000000ULL, 0x3ffe}, /* extended 0.5 */
+ { 0xa000000000000000ULL, 0x4002} /* extended 10.0 */
+};
+
+const float64 float64Constant[] = {
+ 0x0000000000000000ULL, /* double 0.0 */
+ 0x3ff0000000000000ULL, /* double 1.0 */
+ 0x4000000000000000ULL, /* double 2.0 */
+ 0x4008000000000000ULL, /* double 3.0 */
+ 0x4010000000000000ULL, /* double 4.0 */
+ 0x4014000000000000ULL, /* double 5.0 */
+ 0x3fe0000000000000ULL, /* double 0.5 */
+ 0x4024000000000000ULL /* double 10.0 */
+};
+
+const float32 float32Constant[] = {
+ 0x00000000, /* single 0.0 */
+ 0x3f800000, /* single 1.0 */
+ 0x40000000, /* single 2.0 */
+ 0x40400000, /* single 3.0 */
+ 0x40800000, /* single 4.0 */
+ 0x40a00000, /* single 5.0 */
+ 0x3f000000, /* single 0.5 */
+ 0x41200000 /* single 10.0 */
+};
+
+unsigned int getTransferLength(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case 0x00000000: nRc = 1; break; /* single precision */
+ case 0x00008000: nRc = 2; break; /* double precision */
+ case 0x00400000: nRc = 3; break; /* extended precision */
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getRegisterCount(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_REGISTER_COUNT)
+ {
+ case 0x00000000: nRc = 4; break;
+ case 0x00008000: nRc = 1; break;
+ case 0x00400000: nRc = 2; break;
+ case 0x00408000: nRc = 3; break;
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getRoundingPrecision(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case 0x00000000: nRc = 1; break;
+ case 0x00000080: nRc = 2; break;
+ case 0x00080000: nRc = 3; break;
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getDestinationSize(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_DESTINATION_SIZE)
+ {
+ case 0x00000000: nRc = typeSingle; break;
+ case 0x00000080: nRc = typeDouble; break;
+ case 0x00080000: nRc = typeExtended; break;
+ default: nRc = typeNone;
+ }
+
+ return(nRc);
+}
+
+/* condition code lookup table
+ index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ bit position in short is condition code: NZCV */
+static const unsigned short aCC[16] = {
+ 0xF0F0, // EQ == Z set
+ 0x0F0F, // NE
+ 0xCCCC, // CS == C set
+ 0x3333, // CC
+ 0xFF00, // MI == N set
+ 0x00FF, // PL
+ 0xAAAA, // VS == V set
+ 0x5555, // VC
+ 0x0C0C, // HI == C set && Z clear
+ 0xF3F3, // LS == C clear || Z set
+ 0xAA55, // GE == (N==V)
+ 0x55AA, // LT == (N!=V)
+ 0x0A05, // GT == (!Z && (N==V))
+ 0xF5FA, // LE == (Z || (N!=V))
+ 0xFFFF, // AL always
+ 0 // NV
+};
+
+unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes)
+{
+ return (aCC[opcode>>28] >> (ccodes>>28)) & 1;
+}
diff --git a/target-arm/nwfpe/fpopcode.h b/target-arm/nwfpe/fpopcode.h
new file mode 100644
index 0000000..13c7419
--- /dev/null
+++ b/target-arm/nwfpe/fpopcode.h
@@ -0,0 +1,390 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPOPCODE_H__
+#define __FPOPCODE_H__
+
+/*
+ARM Floating Point Instruction Classes
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT
+|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|0|1| o f f s e t | CPDT
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO
+|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT
+|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+
+CPDT data transfer instructions
+ LDF, STF, LFM, SFM
+
+CPDO dyadic arithmetic instructions
+ ADF, MUF, SUF, RSF, DVF, RDF,
+ POW, RPW, RMF, FML, FDV, FRD, POL
+
+CPDO monadic arithmetic instructions
+ MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
+ SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
+
+CPRT joint arithmetic/data transfer instructions
+ FIX (arithmetic followed by load/store)
+ FLT (load/store followed by arithmetic)
+ CMF, CNF CMFE, CNFE (comparisons)
+ WFS, RFS (write/read floating point status register)
+ WFC, RFC (write/read floating point control register)
+
+cond condition codes
+P pre/post index bit: 0 = postindex, 1 = preindex
+U up/down bit: 0 = stack grows down, 1 = stack grows up
+W write back bit: 1 = update base register (Rn)
+L load/store bit: 0 = store, 1 = load
+Rn base register
+Rd destination/source register
+Fd floating point destination register
+Fn floating point source register
+Fm floating point source register or floating point constant
+
+uv transfer length (TABLE 1)
+wx register count (TABLE 2)
+abcd arithmetic opcode (TABLES 3 & 4)
+ef destination size (rounding precision) (TABLE 5)
+gh rounding mode (TABLE 6)
+j dyadic/monadic bit: 0 = dyadic, 1 = monadic
+i constant bit: 1 = constant (TABLE 6)
+*/
+
+/*
+TABLE 1
++-------------------------+---+---+---------+---------+
+| Precision | u | v | FPSR.EP | length |
++-------------------------+---+---+---------+---------+
+| Single | 0 ü 0 | x | 1 words |
+| Double | 1 ü 1 | x | 2 words |
+| Extended | 1 ü 1 | x | 3 words |
+| Packed decimal | 1 ü 1 | 0 | 3 words |
+| Expanded packed decimal | 1 ü 1 | 1 | 4 words |
++-------------------------+---+---+---------+---------+
+Note: x = don't care
+*/
+
+/*
+TABLE 2
++---+---+---------------------------------+
+| w | x | Number of registers to transfer |
++---+---+---------------------------------+
+| 0 ü 1 | 1 |
+| 1 ü 0 | 2 |
+| 1 ü 1 | 3 |
+| 0 ü 0 | 4 |
++---+---+---------------------------------+
+*/
+
+/*
+TABLE 3: Dyadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm |
+| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm |
+| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm |
+| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn |
+| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm |
+| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn |
+| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm |
+| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn |
+| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) |
+| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm |
+| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm |
+| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn |
+| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) |
+| 1 | 1 | 0 | 1 | | undefined instruction | trap |
+| 1 | 1 | 1 | 0 | | undefined instruction | trap |
+| 1 | 1 | 1 | 1 | | undefined instruction | trap |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: POW, RPW, POL are deprecated, and are available for backwards
+ compatibility only.
+*/
+
+/*
+TABLE 4: Monadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm |
+| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm |
+| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) |
+| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) |
+| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) |
+| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) |
+| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) |
+| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm |
+| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) |
+| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) |
+| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) |
+| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) |
+| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) |
+| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) |
+| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) |
+| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
+ available for backwards compatibility only.
+*/
+
+/*
+TABLE 5
++-------------------------+---+---+
+| Rounding Precision | e | f |
++-------------------------+---+---+
+| IEEE Single precision | 0 ü 0 |
+| IEEE Double precision | 0 ü 1 |
+| IEEE Extended precision | 1 ü 0 |
+| undefined (trap) | 1 ü 1 |
++-------------------------+---+---+
+*/
+
+/*
+TABLE 5
++---------------------------------+---+---+
+| Rounding Mode | g | h |
++---------------------------------+---+---+
+| Round to nearest (default) | 0 ü 0 |
+| Round toward plus infinity | 0 ü 1 |
+| Round toward negative infinity | 1 ü 0 |
+| Round toward zero | 1 ü 1 |
++---------------------------------+---+---+
+*/
+
+/*
+===
+=== Definitions for load and store instructions
+===
+*/
+
+/* bit masks */
+#define BIT_PREINDEX 0x01000000
+#define BIT_UP 0x00800000
+#define BIT_WRITE_BACK 0x00200000
+#define BIT_LOAD 0x00100000
+
+/* masks for load/store */
+#define MASK_CPDT 0x0c000000 /* data processing opcode */
+#define MASK_OFFSET 0x000000ff
+#define MASK_TRANSFER_LENGTH 0x00408000
+#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
+#define MASK_COPROCESSOR 0x00000f00
+
+/* Tests for transfer length */
+#define TRANSFER_SINGLE 0x00000000
+#define TRANSFER_DOUBLE 0x00008000
+#define TRANSFER_EXTENDED 0x00400000
+#define TRANSFER_PACKED MASK_TRANSFER_LENGTH
+
+/* Get the coprocessor number from the opcode. */
+#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8)
+
+/* Get the offset from the opcode. */
+#define getOffset(opcode) (opcode & MASK_OFFSET)
+
+/* Tests for specific data transfer load/store opcodes. */
+#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask))
+
+#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
+#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
+
+#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+
+#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0)
+#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0)
+#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0)
+#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0)
+#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0)
+#define LOAD(opcode) ((opcode & BIT_LOAD) != 0)
+#define STORE(opcode) ((opcode & BIT_LOAD) == 0)
+
+/*
+===
+=== Definitions for arithmetic instructions
+===
+*/
+/* bit masks */
+#define BIT_MONADIC 0x00008000
+#define BIT_CONSTANT 0x00000008
+
+#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0)
+#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
+
+/* instruction identification masks */
+#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
+#define MASK_ARITHMETIC_OPCODE 0x00f08000
+#define MASK_DESTINATION_SIZE 0x00080080
+
+/* dyadic arithmetic opcodes. */
+#define ADF_CODE 0x00000000
+#define MUF_CODE 0x00100000
+#define SUF_CODE 0x00200000
+#define RSF_CODE 0x00300000
+#define DVF_CODE 0x00400000
+#define RDF_CODE 0x00500000
+#define POW_CODE 0x00600000
+#define RPW_CODE 0x00700000
+#define RMF_CODE 0x00800000
+#define FML_CODE 0x00900000
+#define FDV_CODE 0x00a00000
+#define FRD_CODE 0x00b00000
+#define POL_CODE 0x00c00000
+/* 0x00d00000 is an invalid dyadic arithmetic opcode */
+/* 0x00e00000 is an invalid dyadic arithmetic opcode */
+/* 0x00f00000 is an invalid dyadic arithmetic opcode */
+
+/* monadic arithmetic opcodes. */
+#define MVF_CODE 0x00008000
+#define MNF_CODE 0x00108000
+#define ABS_CODE 0x00208000
+#define RND_CODE 0x00308000
+#define SQT_CODE 0x00408000
+#define LOG_CODE 0x00508000
+#define LGN_CODE 0x00608000
+#define EXP_CODE 0x00708000
+#define SIN_CODE 0x00808000
+#define COS_CODE 0x00908000
+#define TAN_CODE 0x00a08000
+#define ASN_CODE 0x00b08000
+#define ACS_CODE 0x00c08000
+#define ATN_CODE 0x00d08000
+#define URD_CODE 0x00e08000
+#define NRM_CODE 0x00f08000
+
+/*
+===
+=== Definitions for register transfer and comparison instructions
+===
+*/
+
+#define MASK_CPRT 0x0e000010 /* register transfer opcode */
+#define MASK_CPRT_CODE 0x00f00000
+#define FLT_CODE 0x00000000
+#define FIX_CODE 0x00100000
+#define WFS_CODE 0x00200000
+#define RFS_CODE 0x00300000
+#define WFC_CODE 0x00400000
+#define RFC_CODE 0x00500000
+#define CMF_CODE 0x00900000
+#define CNF_CODE 0x00b00000
+#define CMFE_CODE 0x00d00000
+#define CNFE_CODE 0x00f00000
+
+/*
+===
+=== Common definitions
+===
+*/
+
+/* register masks */
+#define MASK_Rd 0x0000f000
+#define MASK_Rn 0x000f0000
+#define MASK_Fd 0x00007000
+#define MASK_Fm 0x00000007
+#define MASK_Fn 0x00070000
+
+/* condition code masks */
+#define CC_MASK 0xf0000000
+#define CC_NEGATIVE 0x80000000
+#define CC_ZERO 0x40000000
+#define CC_CARRY 0x20000000
+#define CC_OVERFLOW 0x10000000
+#define CC_EQ 0x00000000
+#define CC_NE 0x10000000
+#define CC_CS 0x20000000
+#define CC_HS CC_CS
+#define CC_CC 0x30000000
+#define CC_LO CC_CC
+#define CC_MI 0x40000000
+#define CC_PL 0x50000000
+#define CC_VS 0x60000000
+#define CC_VC 0x70000000
+#define CC_HI 0x80000000
+#define CC_LS 0x90000000
+#define CC_GE 0xa0000000
+#define CC_LT 0xb0000000
+#define CC_GT 0xc0000000
+#define CC_LE 0xd0000000
+#define CC_AL 0xe0000000
+#define CC_NV 0xf0000000
+
+/* rounding masks/values */
+#define MASK_ROUNDING_MODE 0x00000060
+#define ROUND_TO_NEAREST 0x00000000
+#define ROUND_TO_PLUS_INFINITY 0x00000020
+#define ROUND_TO_MINUS_INFINITY 0x00000040
+#define ROUND_TO_ZERO 0x00000060
+
+#define MASK_ROUNDING_PRECISION 0x00080080
+#define ROUND_SINGLE 0x00000000
+#define ROUND_DOUBLE 0x00000080
+#define ROUND_EXTENDED 0x00080000
+
+/* Get the condition code from the opcode. */
+#define getCondition(opcode) (opcode >> 28)
+
+/* Get the source register from the opcode. */
+#define getRn(opcode) ((opcode & MASK_Rn) >> 16)
+
+/* Get the destination floating point register from the opcode. */
+#define getFd(opcode) ((opcode & MASK_Fd) >> 12)
+
+/* Get the first source floating point register from the opcode. */
+#define getFn(opcode) ((opcode & MASK_Fn) >> 16)
+
+/* Get the second source floating point register from the opcode. */
+#define getFm(opcode) (opcode & MASK_Fm)
+
+/* Get the destination register from the opcode. */
+#define getRd(opcode) ((opcode & MASK_Rd) >> 12)
+
+/* Get the rounding mode from the opcode. */
+#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
+
+static inline const floatx80 getExtendedConstant(const unsigned int nIndex)
+{
+ extern const floatx80 floatx80Constant[];
+ return floatx80Constant[nIndex];
+}
+
+static inline const float64 getDoubleConstant(const unsigned int nIndex)
+{
+ extern const float64 float64Constant[];
+ return float64Constant[nIndex];
+}
+
+static inline const float32 getSingleConstant(const unsigned int nIndex)
+{
+ extern const float32 float32Constant[];
+ return float32Constant[nIndex];
+}
+
+extern unsigned int getRegisterCount(const unsigned int opcode);
+extern unsigned int getDestinationSize(const unsigned int opcode);
+
+#endif
diff --git a/target-arm/nwfpe/fpsr.h b/target-arm/nwfpe/fpsr.h
new file mode 100644
index 0000000..6dafb0f
--- /dev/null
+++ b/target-arm/nwfpe/fpsr.h
@@ -0,0 +1,108 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __FPSR_H__
+#define __FPSR_H__
+
+/*
+The FPSR is a 32 bit register consisting of 4 parts, each exactly
+one byte.
+
+ SYSTEM ID
+ EXCEPTION TRAP ENABLE BYTE
+ SYSTEM CONTROL BYTE
+ CUMULATIVE EXCEPTION FLAGS BYTE
+
+The FPCR is a 32 bit register consisting of bit flags.
+*/
+
+/* SYSTEM ID
+------------
+Note: the system id byte is read only */
+
+typedef unsigned int FPSR; /* type for floating point status register */
+typedef unsigned int FPCR; /* type for floating point control register */
+
+#define MASK_SYSID 0xff000000
+#define BIT_HARDWARE 0x80000000
+#define FP_EMULATOR 0x01000000 /* System ID for emulator */
+#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
+
+/* EXCEPTION TRAP ENABLE BYTE
+----------------------------- */
+
+#define MASK_TRAP_ENABLE 0x00ff0000
+#define MASK_TRAP_ENABLE_STRICT 0x001f0000
+#define BIT_IXE 0x00100000 /* inexact exception enable */
+#define BIT_UFE 0x00080000 /* underflow exception enable */
+#define BIT_OFE 0x00040000 /* overflow exception enable */
+#define BIT_DZE 0x00020000 /* divide by zero exception enable */
+#define BIT_IOE 0x00010000 /* invalid operation exception enable */
+
+/* SYSTEM CONTROL BYTE
+---------------------- */
+
+#define MASK_SYSTEM_CONTROL 0x0000ff00
+#define MASK_TRAP_STRICT 0x00001f00
+
+#define BIT_AC 0x00001000 /* use alternative C-flag definition
+ for compares */
+#define BIT_EP 0x00000800 /* use expanded packed decimal format */
+#define BIT_SO 0x00000400 /* select synchronous operation of FPA */
+#define BIT_NE 0x00000200 /* NaN exception bit */
+#define BIT_ND 0x00000100 /* no denormalized numbers bit */
+
+/* CUMULATIVE EXCEPTION FLAGS BYTE
+---------------------------------- */
+
+#define MASK_EXCEPTION_FLAGS 0x000000ff
+#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f
+
+#define BIT_IXC 0x00000010 /* inexact exception flag */
+#define BIT_UFC 0x00000008 /* underflow exception flag */
+#define BIT_OFC 0x00000004 /* overfloat exception flag */
+#define BIT_DZC 0x00000002 /* divide by zero exception flag */
+#define BIT_IOC 0x00000001 /* invalid operation exception flag */
+
+/* Floating Point Control Register
+----------------------------------*/
+
+#define BIT_RU 0x80000000 /* rounded up bit */
+#define BIT_IE 0x10000000 /* inexact bit */
+#define BIT_MO 0x08000000 /* mantissa overflow bit */
+#define BIT_EO 0x04000000 /* exponent overflow bit */
+#define BIT_SB 0x00000800 /* store bounce */
+#define BIT_AB 0x00000400 /* arithmetic bounce */
+#define BIT_RE 0x00000200 /* rounding exception */
+#define BIT_DA 0x00000100 /* disable FPA */
+
+#define MASK_OP 0x00f08010 /* AU operation code */
+#define MASK_PR 0x00080080 /* AU precision */
+#define MASK_S1 0x00070000 /* AU source register 1 */
+#define MASK_S2 0x00000007 /* AU source register 2 */
+#define MASK_DS 0x00007000 /* AU destination register */
+#define MASK_RM 0x00000060 /* AU rounding mode */
+#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */
+#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */
+#define MASK_WFC MASK_RESET
+#define MASK_RFC ~MASK_RESET
+
+#endif
diff --git a/target-arm/nwfpe/single_cpdo.c b/target-arm/nwfpe/single_cpdo.c
new file mode 100644
index 0000000..7dd2620
--- /dev/null
+++ b/target-arm/nwfpe/single_cpdo.c
@@ -0,0 +1,255 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+float32 float32_exp(float32 Fm);
+float32 float32_ln(float32 Fm);
+float32 float32_sin(float32 rFm);
+float32 float32_cos(float32 rFm);
+float32 float32_arcsin(float32 rFm);
+float32 float32_arctan(float32 rFm);
+float32 float32_log(float32 rFm);
+float32 float32_tan(float32 rFm);
+float32 float32_arccos(float32 rFm);
+float32 float32_pow(float32 rFn,float32 rFm);
+float32 float32_pol(float32 rFn,float32 rFm);
+
+unsigned int SingleCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float32 rFm, rFn = 0;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getSingleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = fpa11->fpreg[Fm].fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = fpa11->fpreg[Fn].fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fSingle = rFm;
+ break;
+
+ case MNF_CODE:
+ rFm ^= 0x80000000;
+ fpa11->fpreg[Fd].fSingle = rFm;
+ break;
+
+ case ABS_CODE:
+ rFm &= 0x7fffffff;
+ fpa11->fpreg[Fd].fSingle = rFm;
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeSingle;
+ return nRc;
+}
+
+#if 0
+float32 float32_exp(float32 Fm)
+{
+//series
+}
+
+float32 float32_ln(float32 Fm)
+{
+//series
+}
+
+float32 float32_sin(float32 rFm)
+{
+//series
+}
+
+float32 float32_cos(float32 rFm)
+{
+//series
+}
+
+float32 float32_arcsin(float32 rFm)
+{
+//series
+}
+
+float32 float32_arctan(float32 rFm)
+{
+ //series
+}
+
+float32 float32_arccos(float32 rFm)
+{
+ //return float32_sub(halfPi,float32_arcsin(rFm));
+}
+
+float32 float32_log(float32 rFm)
+{
+ return float32_div(float32_ln(rFm),getSingleConstant(7));
+}
+
+float32 float32_tan(float32 rFm)
+{
+ return float32_div(float32_sin(rFm),float32_cos(rFm));
+}
+
+float32 float32_pow(float32 rFn,float32 rFm)
+{
+ return float32_exp(float32_mul(rFm,float32_ln(rFn)));
+}
+
+float32 float32_pol(float32 rFn,float32 rFm)
+{
+ return float32_arctan(float32_div(rFn,rFm));
+}
+#endif
diff --git a/target-arm/op.c b/target-arm/op.c
new file mode 100644
index 0000000..f17b812
--- /dev/null
+++ b/target-arm/op.c
@@ -0,0 +1,1203 @@
+/*
+ * ARM micro operations
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2005 CodeSourcery, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+#define REGNAME r0
+#define REG (env->regs[0])
+#include "op_template.h"
+
+#define REGNAME r1
+#define REG (env->regs[1])
+#include "op_template.h"
+
+#define REGNAME r2
+#define REG (env->regs[2])
+#include "op_template.h"
+
+#define REGNAME r3
+#define REG (env->regs[3])
+#include "op_template.h"
+
+#define REGNAME r4
+#define REG (env->regs[4])
+#include "op_template.h"
+
+#define REGNAME r5
+#define REG (env->regs[5])
+#include "op_template.h"
+
+#define REGNAME r6
+#define REG (env->regs[6])
+#include "op_template.h"
+
+#define REGNAME r7
+#define REG (env->regs[7])
+#include "op_template.h"
+
+#define REGNAME r8
+#define REG (env->regs[8])
+#include "op_template.h"
+
+#define REGNAME r9
+#define REG (env->regs[9])
+#include "op_template.h"
+
+#define REGNAME r10
+#define REG (env->regs[10])
+#include "op_template.h"
+
+#define REGNAME r11
+#define REG (env->regs[11])
+#include "op_template.h"
+
+#define REGNAME r12
+#define REG (env->regs[12])
+#include "op_template.h"
+
+#define REGNAME r13
+#define REG (env->regs[13])
+#include "op_template.h"
+
+#define REGNAME r14
+#define REG (env->regs[14])
+#include "op_template.h"
+
+#define REGNAME r15
+#define REG (env->regs[15])
+#define SET_REG(x) REG = x & ~(uint32_t)1
+#include "op_template.h"
+
+void OPPROTO op_bx_T0(void)
+{
+ env->regs[15] = T0 & ~(uint32_t)1;
+ env->thumb = (T0 & 1) != 0;
+}
+
+void OPPROTO op_movl_T0_0(void)
+{
+ T0 = 0;
+}
+
+void OPPROTO op_movl_T0_im(void)
+{
+ T0 = PARAM1;
+}
+
+void OPPROTO op_movl_T0_T1(void)
+{
+ T0 = T1;
+}
+
+void OPPROTO op_movl_T1_im(void)
+{
+ T1 = PARAM1;
+}
+
+void OPPROTO op_mov_CF_T1(void)
+{
+ env->CF = ((uint32_t)T1) >> 31;
+}
+
+void OPPROTO op_movl_T2_im(void)
+{
+ T2 = PARAM1;
+}
+
+void OPPROTO op_addl_T1_im(void)
+{
+ T1 += PARAM1;
+}
+
+void OPPROTO op_addl_T1_T2(void)
+{
+ T1 += T2;
+}
+
+void OPPROTO op_subl_T1_T2(void)
+{
+ T1 -= T2;
+}
+
+void OPPROTO op_addl_T0_T1(void)
+{
+ T0 += T1;
+}
+
+void OPPROTO op_addl_T0_T1_cc(void)
+{
+ unsigned int src1;
+ src1 = T0;
+ T0 += T1;
+ env->NZF = T0;
+ env->CF = T0 < src1;
+ env->VF = (src1 ^ T1 ^ -1) & (src1 ^ T0);
+}
+
+void OPPROTO op_adcl_T0_T1(void)
+{
+ T0 += T1 + env->CF;
+}
+
+void OPPROTO op_adcl_T0_T1_cc(void)
+{
+ unsigned int src1;
+ src1 = T0;
+ if (!env->CF) {
+ T0 += T1;
+ env->CF = T0 < src1;
+ } else {
+ T0 += T1 + 1;
+ env->CF = T0 <= src1;
+ }
+ env->VF = (src1 ^ T1 ^ -1) & (src1 ^ T0);
+ env->NZF = T0;
+ FORCE_RET();
+}
+
+#define OPSUB(sub, sbc, res, T0, T1) \
+ \
+void OPPROTO op_ ## sub ## l_T0_T1(void) \
+{ \
+ res = T0 - T1; \
+} \
+ \
+void OPPROTO op_ ## sub ## l_T0_T1_cc(void) \
+{ \
+ unsigned int src1; \
+ src1 = T0; \
+ T0 -= T1; \
+ env->NZF = T0; \
+ env->CF = src1 >= T1; \
+ env->VF = (src1 ^ T1) & (src1 ^ T0); \
+ res = T0; \
+} \
+ \
+void OPPROTO op_ ## sbc ## l_T0_T1(void) \
+{ \
+ res = T0 - T1 + env->CF - 1; \
+} \
+ \
+void OPPROTO op_ ## sbc ## l_T0_T1_cc(void) \
+{ \
+ unsigned int src1; \
+ src1 = T0; \
+ if (!env->CF) { \
+ T0 = T0 - T1 - 1; \
+ env->CF = src1 > T1; \
+ } else { \
+ T0 = T0 - T1; \
+ env->CF = src1 >= T1; \
+ } \
+ env->VF = (src1 ^ T1) & (src1 ^ T0); \
+ env->NZF = T0; \
+ res = T0; \
+ FORCE_RET(); \
+}
+
+OPSUB(sub, sbc, T0, T0, T1)
+
+OPSUB(rsb, rsc, T0, T1, T0)
+
+void OPPROTO op_andl_T0_T1(void)
+{
+ T0 &= T1;
+}
+
+void OPPROTO op_xorl_T0_T1(void)
+{
+ T0 ^= T1;
+}
+
+void OPPROTO op_orl_T0_T1(void)
+{
+ T0 |= T1;
+}
+
+void OPPROTO op_bicl_T0_T1(void)
+{
+ T0 &= ~T1;
+}
+
+void OPPROTO op_notl_T1(void)
+{
+ T1 = ~T1;
+}
+
+void OPPROTO op_logic_T0_cc(void)
+{
+ env->NZF = T0;
+}
+
+void OPPROTO op_logic_T1_cc(void)
+{
+ env->NZF = T1;
+}
+
+#define EIP (env->regs[15])
+
+void OPPROTO op_test_eq(void)
+{
+ if (env->NZF == 0)
+ GOTO_LABEL_PARAM(1);;
+ FORCE_RET();
+}
+
+void OPPROTO op_test_ne(void)
+{
+ if (env->NZF != 0)
+ GOTO_LABEL_PARAM(1);;
+ FORCE_RET();
+}
+
+void OPPROTO op_test_cs(void)
+{
+ if (env->CF != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_cc(void)
+{
+ if (env->CF == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_mi(void)
+{
+ if ((env->NZF & 0x80000000) != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_pl(void)
+{
+ if ((env->NZF & 0x80000000) == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_vs(void)
+{
+ if ((env->VF & 0x80000000) != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_vc(void)
+{
+ if ((env->VF & 0x80000000) == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_hi(void)
+{
+ if (env->CF != 0 && env->NZF != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_ls(void)
+{
+ if (env->CF == 0 || env->NZF == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_ge(void)
+{
+ if (((env->VF ^ env->NZF) & 0x80000000) == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_lt(void)
+{
+ if (((env->VF ^ env->NZF) & 0x80000000) != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_gt(void)
+{
+ if (env->NZF != 0 && ((env->VF ^ env->NZF) & 0x80000000) == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_test_le(void)
+{
+ if (env->NZF == 0 || ((env->VF ^ env->NZF) & 0x80000000) != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_goto_tb0(void)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+void OPPROTO op_goto_tb1(void)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+void OPPROTO op_exit_tb(void)
+{
+ EXIT_TB();
+}
+
+void OPPROTO op_movl_T0_cpsr(void)
+{
+ T0 = cpsr_read(env);
+ FORCE_RET();
+}
+
+void OPPROTO op_movl_T0_spsr(void)
+{
+ T0 = env->spsr;
+}
+
+void OPPROTO op_movl_spsr_T0(void)
+{
+ uint32_t mask = PARAM1;
+ env->spsr = (env->spsr & ~mask) | (T0 & mask);
+}
+
+void OPPROTO op_movl_cpsr_T0(void)
+{
+ cpsr_write(env, T0, PARAM1);
+ FORCE_RET();
+}
+
+void OPPROTO op_mul_T0_T1(void)
+{
+ T0 = T0 * T1;
+}
+
+/* 64 bit unsigned mul */
+void OPPROTO op_mull_T0_T1(void)
+{
+ uint64_t res;
+ res = (uint64_t)T0 * (uint64_t)T1;
+ T1 = res >> 32;
+ T0 = res;
+}
+
+/* 64 bit signed mul */
+void OPPROTO op_imull_T0_T1(void)
+{
+ uint64_t res;
+ res = (int64_t)((int32_t)T0) * (int64_t)((int32_t)T1);
+ T1 = res >> 32;
+ T0 = res;
+}
+
+/* 48 bit signed mul, top 32 bits */
+void OPPROTO op_imulw_T0_T1(void)
+{
+ uint64_t res;
+ res = (int64_t)((int32_t)T0) * (int64_t)((int32_t)T1);
+ T0 = res >> 16;
+}
+
+void OPPROTO op_addq_T0_T1(void)
+{
+ uint64_t res;
+ res = ((uint64_t)T1 << 32) | T0;
+ res += ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]);
+ T1 = res >> 32;
+ T0 = res;
+}
+
+void OPPROTO op_addq_lo_T0_T1(void)
+{
+ uint64_t res;
+ res = ((uint64_t)T1 << 32) | T0;
+ res += (uint64_t)(env->regs[PARAM1]);
+ T1 = res >> 32;
+ T0 = res;
+}
+
+void OPPROTO op_logicq_cc(void)
+{
+ env->NZF = (T1 & 0x80000000) | ((T0 | T1) != 0);
+}
+
+/* memory access */
+
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
+
+/* shifts */
+
+/* T1 based */
+
+void OPPROTO op_shll_T1_im(void)
+{
+ T1 = T1 << PARAM1;
+}
+
+void OPPROTO op_shrl_T1_im(void)
+{
+ T1 = (uint32_t)T1 >> PARAM1;
+}
+
+void OPPROTO op_shrl_T1_0(void)
+{
+ T1 = 0;
+}
+
+void OPPROTO op_sarl_T1_im(void)
+{
+ T1 = (int32_t)T1 >> PARAM1;
+}
+
+void OPPROTO op_sarl_T1_0(void)
+{
+ T1 = (int32_t)T1 >> 31;
+}
+
+void OPPROTO op_rorl_T1_im(void)
+{
+ int shift;
+ shift = PARAM1;
+ T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift));
+}
+
+void OPPROTO op_rrxl_T1(void)
+{
+ T1 = ((uint32_t)T1 >> 1) | ((uint32_t)env->CF << 31);
+}
+
+/* T1 based, set C flag */
+void OPPROTO op_shll_T1_im_cc(void)
+{
+ env->CF = (T1 >> (32 - PARAM1)) & 1;
+ T1 = T1 << PARAM1;
+}
+
+void OPPROTO op_shrl_T1_im_cc(void)
+{
+ env->CF = (T1 >> (PARAM1 - 1)) & 1;
+ T1 = (uint32_t)T1 >> PARAM1;
+}
+
+void OPPROTO op_shrl_T1_0_cc(void)
+{
+ env->CF = (T1 >> 31) & 1;
+ T1 = 0;
+}
+
+void OPPROTO op_sarl_T1_im_cc(void)
+{
+ env->CF = (T1 >> (PARAM1 - 1)) & 1;
+ T1 = (int32_t)T1 >> PARAM1;
+}
+
+void OPPROTO op_sarl_T1_0_cc(void)
+{
+ env->CF = (T1 >> 31) & 1;
+ T1 = (int32_t)T1 >> 31;
+}
+
+void OPPROTO op_rorl_T1_im_cc(void)
+{
+ int shift;
+ shift = PARAM1;
+ env->CF = (T1 >> (shift - 1)) & 1;
+ T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift));
+}
+
+void OPPROTO op_rrxl_T1_cc(void)
+{
+ uint32_t c;
+ c = T1 & 1;
+ T1 = ((uint32_t)T1 >> 1) | ((uint32_t)env->CF << 31);
+ env->CF = c;
+}
+
+/* T2 based */
+void OPPROTO op_shll_T2_im(void)
+{
+ T2 = T2 << PARAM1;
+}
+
+void OPPROTO op_shrl_T2_im(void)
+{
+ T2 = (uint32_t)T2 >> PARAM1;
+}
+
+void OPPROTO op_shrl_T2_0(void)
+{
+ T2 = 0;
+}
+
+void OPPROTO op_sarl_T2_im(void)
+{
+ T2 = (int32_t)T2 >> PARAM1;
+}
+
+void OPPROTO op_sarl_T2_0(void)
+{
+ T2 = (int32_t)T2 >> 31;
+}
+
+void OPPROTO op_rorl_T2_im(void)
+{
+ int shift;
+ shift = PARAM1;
+ T2 = ((uint32_t)T2 >> shift) | (T2 << (32 - shift));
+}
+
+void OPPROTO op_rrxl_T2(void)
+{
+ T2 = ((uint32_t)T2 >> 1) | ((uint32_t)env->CF << 31);
+}
+
+/* T1 based, use T0 as shift count */
+
+void OPPROTO op_shll_T1_T0(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32)
+ T1 = 0;
+ else
+ T1 = T1 << shift;
+ FORCE_RET();
+}
+
+void OPPROTO op_shrl_T1_T0(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32)
+ T1 = 0;
+ else
+ T1 = (uint32_t)T1 >> shift;
+ FORCE_RET();
+}
+
+void OPPROTO op_sarl_T1_T0(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32)
+ shift = 31;
+ T1 = (int32_t)T1 >> shift;
+}
+
+void OPPROTO op_rorl_T1_T0(void)
+{
+ int shift;
+ shift = T0 & 0x1f;
+ if (shift) {
+ T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift));
+ }
+ FORCE_RET();
+}
+
+/* T1 based, use T0 as shift count and compute CF */
+
+void OPPROTO op_shll_T1_T0_cc(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32) {
+ if (shift == 32)
+ env->CF = T1 & 1;
+ else
+ env->CF = 0;
+ T1 = 0;
+ } else if (shift != 0) {
+ env->CF = (T1 >> (32 - shift)) & 1;
+ T1 = T1 << shift;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_shrl_T1_T0_cc(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32) {
+ if (shift == 32)
+ env->CF = (T1 >> 31) & 1;
+ else
+ env->CF = 0;
+ T1 = 0;
+ } else if (shift != 0) {
+ env->CF = (T1 >> (shift - 1)) & 1;
+ T1 = (uint32_t)T1 >> shift;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_sarl_T1_T0_cc(void)
+{
+ int shift;
+ shift = T0 & 0xff;
+ if (shift >= 32) {
+ env->CF = (T1 >> 31) & 1;
+ T1 = (int32_t)T1 >> 31;
+ } else {
+ env->CF = (T1 >> (shift - 1)) & 1;
+ T1 = (int32_t)T1 >> shift;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_rorl_T1_T0_cc(void)
+{
+ int shift1, shift;
+ shift1 = T0 & 0xff;
+ shift = shift1 & 0x1f;
+ if (shift == 0) {
+ if (shift1 != 0)
+ env->CF = (T1 >> 31) & 1;
+ } else {
+ env->CF = (T1 >> (shift - 1)) & 1;
+ T1 = ((uint32_t)T1 >> shift) | (T1 << (32 - shift));
+ }
+ FORCE_RET();
+}
+
+/* misc */
+void OPPROTO op_clz_T0(void)
+{
+ int count;
+ for (count = 32; T0 > 0; count--)
+ T0 = T0 >> 1;
+ T0 = count;
+ FORCE_RET();
+}
+
+void OPPROTO op_sarl_T0_im(void)
+{
+ T0 = (int32_t)T0 >> PARAM1;
+}
+
+/* Sign/zero extend */
+void OPPROTO op_sxth_T0(void)
+{
+ T0 = (int16_t)T0;
+}
+
+void OPPROTO op_sxth_T1(void)
+{
+ T1 = (int16_t)T1;
+}
+
+void OPPROTO op_sxtb_T1(void)
+{
+ T1 = (int8_t)T1;
+}
+
+void OPPROTO op_uxtb_T1(void)
+{
+ T1 = (uint8_t)T1;
+}
+
+void OPPROTO op_uxth_T1(void)
+{
+ T1 = (uint16_t)T1;
+}
+
+void OPPROTO op_sxtb16_T1(void)
+{
+ uint32_t res;
+ res = (uint16_t)(int8_t)T1;
+ res |= (uint32_t)(int8_t)(T1 >> 16) << 16;
+ T1 = res;
+}
+
+void OPPROTO op_uxtb16_T1(void)
+{
+ uint32_t res;
+ res = (uint16_t)(uint8_t)T1;
+ res |= (uint32_t)(uint8_t)(T1 >> 16) << 16;
+ T1 = res;
+}
+
+#define SIGNBIT (uint32_t)0x80000000
+/* saturating arithmetic */
+void OPPROTO op_addl_T0_T1_setq(void)
+{
+ uint32_t res;
+
+ res = T0 + T1;
+ if (((res ^ T0) & SIGNBIT) && !((T0 ^ T1) & SIGNBIT))
+ env->QF = 1;
+
+ T0 = res;
+ FORCE_RET();
+}
+
+void OPPROTO op_addl_T0_T1_saturate(void)
+{
+ uint32_t res;
+
+ res = T0 + T1;
+ if (((res ^ T0) & SIGNBIT) && !((T0 ^ T1) & SIGNBIT)) {
+ env->QF = 1;
+ if (T0 & SIGNBIT)
+ T0 = 0x80000000;
+ else
+ T0 = 0x7fffffff;
+ }
+ else
+ T0 = res;
+
+ FORCE_RET();
+}
+
+void OPPROTO op_subl_T0_T1_saturate(void)
+{
+ uint32_t res;
+
+ res = T0 - T1;
+ if (((res ^ T0) & SIGNBIT) && ((T0 ^ T1) & SIGNBIT)) {
+ env->QF = 1;
+ if (T0 & SIGNBIT)
+ T0 = 0x80000000;
+ else
+ T0 = 0x7fffffff;
+ }
+ else
+ T0 = res;
+
+ FORCE_RET();
+}
+
+void OPPROTO op_double_T1_saturate(void)
+{
+ int32_t val;
+
+ val = T1;
+ if (val >= 0x40000000) {
+ T1 = 0x7fffffff;
+ env->QF = 1;
+ } else if (val <= (int32_t)0xc0000000) {
+ T1 = 0x80000000;
+ env->QF = 1;
+ } else {
+ T1 = val << 1;
+ }
+ FORCE_RET();
+}
+
+/* thumb shift by immediate */
+void OPPROTO op_shll_T0_im_thumb(void)
+{
+ int shift;
+ shift = PARAM1;
+ if (shift != 0) {
+ env->CF = (T1 >> (32 - shift)) & 1;
+ T0 = T0 << shift;
+ }
+ env->NZF = T0;
+ FORCE_RET();
+}
+
+void OPPROTO op_shrl_T0_im_thumb(void)
+{
+ int shift;
+
+ shift = PARAM1;
+ if (shift == 0) {
+ env->CF = ((uint32_t)shift) >> 31;
+ T0 = 0;
+ } else {
+ env->CF = (T0 >> (shift - 1)) & 1;
+ T0 = T0 >> shift;
+ }
+ env->NZF = T0;
+ FORCE_RET();
+}
+
+void OPPROTO op_sarl_T0_im_thumb(void)
+{
+ int shift;
+
+ shift = PARAM1;
+ if (shift == 0) {
+ T0 = ((int32_t)T0) >> 31;
+ env->CF = T0 & 1;
+ } else {
+ env->CF = (T0 >> (shift - 1)) & 1;
+ T0 = ((int32_t)T0) >> shift;
+ }
+ env->NZF = T0;
+ FORCE_RET();
+}
+
+/* exceptions */
+
+void OPPROTO op_swi(void)
+{
+ env->exception_index = EXCP_SWI;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_undef_insn(void)
+{
+ env->exception_index = EXCP_UDEF;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_wfi(void)
+{
+ env->exception_index = EXCP_HLT;
+ env->halted = 1;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_bkpt(void)
+{
+ env->exception_index = EXCP_BKPT;
+ cpu_loop_exit();
+}
+
+/* VFP support. We follow the convention used for VFP instrunctions:
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+#define VFP_OP(name, p) void OPPROTO op_vfp_##name##p(void)
+
+#define VFP_BINOP(name) \
+VFP_OP(name, s) \
+{ \
+ FT0s = float32_ ## name (FT0s, FT1s, &env->vfp.fp_status); \
+} \
+VFP_OP(name, d) \
+{ \
+ FT0d = float64_ ## name (FT0d, FT1d, &env->vfp.fp_status); \
+}
+VFP_BINOP(add)
+VFP_BINOP(sub)
+VFP_BINOP(mul)
+VFP_BINOP(div)
+#undef VFP_BINOP
+
+#define VFP_HELPER(name) \
+VFP_OP(name, s) \
+{ \
+ do_vfp_##name##s(); \
+} \
+VFP_OP(name, d) \
+{ \
+ do_vfp_##name##d(); \
+}
+VFP_HELPER(abs)
+VFP_HELPER(sqrt)
+VFP_HELPER(cmp)
+VFP_HELPER(cmpe)
+#undef VFP_HELPER
+
+/* XXX: Will this do the right thing for NANs. Should invert the signbit
+ without looking at the rest of the value. */
+VFP_OP(neg, s)
+{
+ FT0s = float32_chs(FT0s);
+}
+
+VFP_OP(neg, d)
+{
+ FT0d = float64_chs(FT0d);
+}
+
+VFP_OP(F1_ld0, s)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+ v.i = 0;
+ FT1s = v.s;
+}
+
+VFP_OP(F1_ld0, d)
+{
+ union {
+ uint64_t i;
+ float64 d;
+ } v;
+ v.i = 0;
+ FT1d = v.d;
+}
+
+/* Helper routines to perform bitwise copies between float and int. */
+static inline float32 vfp_itos(uint32_t i)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.i = i;
+ return v.s;
+}
+
+static inline uint32_t vfp_stoi(float32 s)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.s = s;
+ return v.i;
+}
+
+/* Integer to float conversion. */
+VFP_OP(uito, s)
+{
+ FT0s = uint32_to_float32(vfp_stoi(FT0s), &env->vfp.fp_status);
+}
+
+VFP_OP(uito, d)
+{
+ FT0d = uint32_to_float64(vfp_stoi(FT0s), &env->vfp.fp_status);
+}
+
+VFP_OP(sito, s)
+{
+ FT0s = int32_to_float32(vfp_stoi(FT0s), &env->vfp.fp_status);
+}
+
+VFP_OP(sito, d)
+{
+ FT0d = int32_to_float64(vfp_stoi(FT0s), &env->vfp.fp_status);
+}
+
+/* Float to integer conversion. */
+VFP_OP(toui, s)
+{
+ FT0s = vfp_itos(float32_to_uint32(FT0s, &env->vfp.fp_status));
+}
+
+VFP_OP(toui, d)
+{
+ FT0s = vfp_itos(float64_to_uint32(FT0d, &env->vfp.fp_status));
+}
+
+VFP_OP(tosi, s)
+{
+ FT0s = vfp_itos(float32_to_int32(FT0s, &env->vfp.fp_status));
+}
+
+VFP_OP(tosi, d)
+{
+ FT0s = vfp_itos(float64_to_int32(FT0d, &env->vfp.fp_status));
+}
+
+/* TODO: Set rounding mode properly. */
+VFP_OP(touiz, s)
+{
+ FT0s = vfp_itos(float32_to_uint32_round_to_zero(FT0s, &env->vfp.fp_status));
+}
+
+VFP_OP(touiz, d)
+{
+ FT0s = vfp_itos(float64_to_uint32_round_to_zero(FT0d, &env->vfp.fp_status));
+}
+
+VFP_OP(tosiz, s)
+{
+ FT0s = vfp_itos(float32_to_int32_round_to_zero(FT0s, &env->vfp.fp_status));
+}
+
+VFP_OP(tosiz, d)
+{
+ FT0s = vfp_itos(float64_to_int32_round_to_zero(FT0d, &env->vfp.fp_status));
+}
+
+/* floating point conversion */
+VFP_OP(fcvtd, s)
+{
+ FT0d = float32_to_float64(FT0s, &env->vfp.fp_status);
+}
+
+VFP_OP(fcvts, d)
+{
+ FT0s = float64_to_float32(FT0d, &env->vfp.fp_status);
+}
+
+/* Get and Put values from registers. */
+VFP_OP(getreg_F0, d)
+{
+ FT0d = *(float64 *)((char *) env + PARAM1);
+}
+
+VFP_OP(getreg_F0, s)
+{
+ FT0s = *(float32 *)((char *) env + PARAM1);
+}
+
+VFP_OP(getreg_F1, d)
+{
+ FT1d = *(float64 *)((char *) env + PARAM1);
+}
+
+VFP_OP(getreg_F1, s)
+{
+ FT1s = *(float32 *)((char *) env + PARAM1);
+}
+
+VFP_OP(setreg_F0, d)
+{
+ *(float64 *)((char *) env + PARAM1) = FT0d;
+}
+
+VFP_OP(setreg_F0, s)
+{
+ *(float32 *)((char *) env + PARAM1) = FT0s;
+}
+
+void OPPROTO op_vfp_movl_T0_fpscr(void)
+{
+ do_vfp_get_fpscr ();
+}
+
+void OPPROTO op_vfp_movl_T0_fpscr_flags(void)
+{
+ T0 = env->vfp.xregs[ARM_VFP_FPSCR] & (0xf << 28);
+}
+
+void OPPROTO op_vfp_movl_fpscr_T0(void)
+{
+ do_vfp_set_fpscr();
+}
+
+void OPPROTO op_vfp_movl_T0_xreg(void)
+{
+ T0 = env->vfp.xregs[PARAM1];
+}
+
+void OPPROTO op_vfp_movl_xreg_T0(void)
+{
+ env->vfp.xregs[PARAM1] = T0;
+}
+
+/* Move between FT0s to T0 */
+void OPPROTO op_vfp_mrs(void)
+{
+ T0 = vfp_stoi(FT0s);
+}
+
+void OPPROTO op_vfp_msr(void)
+{
+ FT0s = vfp_itos(T0);
+}
+
+/* Move between FT0d and {T0,T1} */
+void OPPROTO op_vfp_mrrd(void)
+{
+ CPU_DoubleU u;
+
+ u.d = FT0d;
+ T0 = u.l.lower;
+ T1 = u.l.upper;
+}
+
+void OPPROTO op_vfp_mdrr(void)
+{
+ CPU_DoubleU u;
+
+ u.l.lower = T0;
+ u.l.upper = T1;
+ FT0d = u.d;
+}
+
+/* Copy the most significant bit to T0 to all bits of T1. */
+void OPPROTO op_signbit_T1_T0(void)
+{
+ T1 = (int32_t)T0 >> 31;
+}
+
+void OPPROTO op_movl_cp15_T0(void)
+{
+ helper_set_cp15(env, PARAM1, T0);
+ FORCE_RET();
+}
+
+void OPPROTO op_movl_T0_cp15(void)
+{
+ T0 = helper_get_cp15(env, PARAM1);
+ FORCE_RET();
+}
+
+/* Access to user mode registers from privileged modes. */
+void OPPROTO op_movl_T0_user(void)
+{
+ int regno = PARAM1;
+ if (regno == 13) {
+ T0 = env->banked_r13[0];
+ } else if (regno == 14) {
+ T0 = env->banked_r14[0];
+ } else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+ T0 = env->usr_regs[regno - 8];
+ } else {
+ T0 = env->regs[regno];
+ }
+ FORCE_RET();
+}
+
+
+void OPPROTO op_movl_user_T0(void)
+{
+ int regno = PARAM1;
+ if (regno == 13) {
+ env->banked_r13[0] = T0;
+ } else if (regno == 14) {
+ env->banked_r14[0] = T0;
+ } else if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+ env->usr_regs[regno - 8] = T0;
+ } else {
+ env->regs[regno] = T0;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_movl_T2_T0(void)
+{
+ T2 = T0;
+}
+
+void OPPROTO op_movl_T0_T2(void)
+{
+ T0 = T2;
+}
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
new file mode 100644
index 0000000..af5c61d
--- /dev/null
+++ b/target-arm/op_helper.c
@@ -0,0 +1,227 @@
+/*
+ * ARM helper routines
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+void raise_exception(int tt)
+{
+ env->exception_index = tt;
+ cpu_loop_exit();
+}
+
+/* thread support */
+
+spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+
+void cpu_lock(void)
+{
+ spin_lock(&global_cpu_lock);
+}
+
+void cpu_unlock(void)
+{
+ spin_unlock(&global_cpu_lock);
+}
+
+/* VFP support. */
+
+void do_vfp_abss(void)
+{
+ FT0s = float32_abs(FT0s);
+}
+
+void do_vfp_absd(void)
+{
+ FT0d = float64_abs(FT0d);
+}
+
+void do_vfp_sqrts(void)
+{
+ FT0s = float32_sqrt(FT0s, &env->vfp.fp_status);
+}
+
+void do_vfp_sqrtd(void)
+{
+ FT0d = float64_sqrt(FT0d, &env->vfp.fp_status);
+}
+
+/* XXX: check quiet/signaling case */
+#define DO_VFP_cmp(p, size) \
+void do_vfp_cmp##p(void) \
+{ \
+ uint32_t flags; \
+ switch(float ## size ## _compare_quiet(FT0##p, FT1##p, &env->vfp.fp_status)) {\
+ case 0: flags = 0x6; break;\
+ case -1: flags = 0x8; break;\
+ case 1: flags = 0x2; break;\
+ default: case 2: flags = 0x3; break;\
+ }\
+ env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
+ | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+ FORCE_RET(); \
+}\
+\
+void do_vfp_cmpe##p(void) \
+{ \
+ uint32_t flags; \
+ switch(float ## size ## _compare(FT0##p, FT1##p, &env->vfp.fp_status)) {\
+ case 0: flags = 0x6; break;\
+ case -1: flags = 0x8; break;\
+ case 1: flags = 0x2; break;\
+ default: case 2: flags = 0x3; break;\
+ }\
+ env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
+ | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+ FORCE_RET(); \
+}
+DO_VFP_cmp(s, 32)
+DO_VFP_cmp(d, 64)
+#undef DO_VFP_cmp
+
+/* Convert host exception flags to vfp form. */
+static inline int vfp_exceptbits_from_host(int host_bits)
+{
+ int target_bits = 0;
+
+ if (host_bits & float_flag_invalid)
+ target_bits |= 1;
+ if (host_bits & float_flag_divbyzero)
+ target_bits |= 2;
+ if (host_bits & float_flag_overflow)
+ target_bits |= 4;
+ if (host_bits & float_flag_underflow)
+ target_bits |= 8;
+ if (host_bits & float_flag_inexact)
+ target_bits |= 0x10;
+ return target_bits;
+}
+
+/* Convert vfp exception flags to target form. */
+static inline int vfp_exceptbits_to_host(int target_bits)
+{
+ int host_bits = 0;
+
+ if (target_bits & 1)
+ host_bits |= float_flag_invalid;
+ if (target_bits & 2)
+ host_bits |= float_flag_divbyzero;
+ if (target_bits & 4)
+ host_bits |= float_flag_overflow;
+ if (target_bits & 8)
+ host_bits |= float_flag_underflow;
+ if (target_bits & 0x10)
+ host_bits |= float_flag_inexact;
+ return host_bits;
+}
+
+void do_vfp_set_fpscr(void)
+{
+ int i;
+ uint32_t changed;
+
+ changed = env->vfp.xregs[ARM_VFP_FPSCR];
+ env->vfp.xregs[ARM_VFP_FPSCR] = (T0 & 0xffc8ffff);
+ env->vfp.vec_len = (T0 >> 16) & 7;
+ env->vfp.vec_stride = (T0 >> 20) & 3;
+
+ changed ^= T0;
+ if (changed & (3 << 22)) {
+ i = (T0 >> 22) & 3;
+ switch (i) {
+ case 0:
+ i = float_round_nearest_even;
+ break;
+ case 1:
+ i = float_round_up;
+ break;
+ case 2:
+ i = float_round_down;
+ break;
+ case 3:
+ i = float_round_to_zero;
+ break;
+ }
+ set_float_rounding_mode(i, &env->vfp.fp_status);
+ }
+
+ i = vfp_exceptbits_to_host((T0 >> 8) & 0x1f);
+ set_float_exception_flags(i, &env->vfp.fp_status);
+ /* XXX: FZ and DN are not implemented. */
+}
+
+void do_vfp_get_fpscr(void)
+{
+ int i;
+
+ T0 = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff) | (env->vfp.vec_len << 16)
+ | (env->vfp.vec_stride << 20);
+ i = get_float_exception_flags(&env->vfp.fp_status);
+ T0 |= vfp_exceptbits_from_host(i);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ target_phys_addr_t pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_arm_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (__builtin_expect(ret, 0)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (target_phys_addr_t)retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ raise_exception(env->exception_index);
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-arm/op_mem.h b/target-arm/op_mem.h
new file mode 100644
index 0000000..29fd85b
--- /dev/null
+++ b/target-arm/op_mem.h
@@ -0,0 +1,70 @@
+/* ARM memory operations. */
+
+/* Load from address T1 into T0. */
+#define MEM_LD_OP(name) \
+void OPPROTO glue(op_ld##name,MEMSUFFIX)(void) \
+{ \
+ T0 = glue(ld##name,MEMSUFFIX)(T1); \
+ FORCE_RET(); \
+}
+
+MEM_LD_OP(ub)
+MEM_LD_OP(sb)
+MEM_LD_OP(uw)
+MEM_LD_OP(sw)
+MEM_LD_OP(l)
+
+#undef MEM_LD_OP
+
+/* Store T0 to address T1. */
+#define MEM_ST_OP(name) \
+void OPPROTO glue(op_st##name,MEMSUFFIX)(void) \
+{ \
+ glue(st##name,MEMSUFFIX)(T1, T0); \
+ FORCE_RET(); \
+}
+
+MEM_ST_OP(b)
+MEM_ST_OP(w)
+MEM_ST_OP(l)
+
+#undef MEM_ST_OP
+
+/* Swap T0 with memory at address T1. */
+/* ??? Is this exception safe? */
+#define MEM_SWP_OP(name, lname) \
+void OPPROTO glue(op_swp##name,MEMSUFFIX)(void) \
+{ \
+ uint32_t tmp; \
+ cpu_lock(); \
+ tmp = glue(ld##lname,MEMSUFFIX)(T1); \
+ glue(st##name,MEMSUFFIX)(T1, T0); \
+ T0 = tmp; \
+ cpu_unlock(); \
+ FORCE_RET(); \
+}
+
+MEM_SWP_OP(b, ub)
+MEM_SWP_OP(l, l)
+
+#undef MEM_SWP_OP
+
+/* Floating point load/store. Address is in T1 */
+#define VFP_MEM_OP(p, w) \
+void OPPROTO glue(op_vfp_ld##p,MEMSUFFIX)(void) \
+{ \
+ FT0##p = glue(ldf##w,MEMSUFFIX)(T1); \
+ FORCE_RET(); \
+} \
+void OPPROTO glue(op_vfp_st##p,MEMSUFFIX)(void) \
+{ \
+ glue(stf##w,MEMSUFFIX)(T1, FT0##p); \
+ FORCE_RET(); \
+}
+
+VFP_MEM_OP(s,l)
+VFP_MEM_OP(d,q)
+
+#undef VFP_MEM_OP
+
+#undef MEMSUFFIX
diff --git a/target-arm/op_template.h b/target-arm/op_template.h
new file mode 100644
index 0000000..fb2add1
--- /dev/null
+++ b/target-arm/op_template.h
@@ -0,0 +1,53 @@
+/*
+ * ARM micro operations (templates for various register related
+ * operations)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SET_REG
+#define SET_REG(x) REG = x
+#endif
+
+void OPPROTO glue(op_movl_T0_, REGNAME)(void)
+{
+ T0 = REG;
+}
+
+void OPPROTO glue(op_movl_T1_, REGNAME)(void)
+{
+ T1 = REG;
+}
+
+void OPPROTO glue(op_movl_T2_, REGNAME)(void)
+{
+ T2 = REG;
+}
+
+void OPPROTO glue(glue(op_movl_, REGNAME), _T0)(void)
+{
+ SET_REG (T0);
+}
+
+void OPPROTO glue(glue(op_movl_, REGNAME), _T1)(void)
+{
+ SET_REG (T1);
+}
+
+#undef REG
+#undef REGNAME
+#undef SET_REG
diff --git a/target-arm/translate.c b/target-arm/translate.c
new file mode 100644
index 0000000..fa7ad60
--- /dev/null
+++ b/target-arm/translate.c
@@ -0,0 +1,2576 @@
+/*
+ * ARM translation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2005 CodeSourcery, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+#define ENABLE_ARCH_5J 0
+#define ENABLE_ARCH_6 1
+#define ENABLE_ARCH_6T2 1
+
+#define ARCH(x) if (!ENABLE_ARCH_##x) goto illegal_op;
+
+/* internal defines */
+typedef struct DisasContext {
+ target_ulong pc;
+ int is_jmp;
+ /* Nonzero if this instruction has been conditionally skipped. */
+ int condjmp;
+ /* The label that will be jumped to when the instruction is skipped. */
+ int condlabel;
+ struct TranslationBlock *tb;
+ int singlestep_enabled;
+ int thumb;
+#if !defined(CONFIG_USER_ONLY)
+ int user;
+#endif
+} DisasContext;
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(s) 1
+#else
+#define IS_USER(s) (s->user)
+#endif
+
+#define DISAS_JUMP_NEXT 4
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+/* XXX: move that elsewhere */
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+extern FILE *logfile;
+extern int loglevel;
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+#include "gen-op.h"
+
+static GenOpFunc1 *gen_test_cc[14] = {
+ gen_op_test_eq,
+ gen_op_test_ne,
+ gen_op_test_cs,
+ gen_op_test_cc,
+ gen_op_test_mi,
+ gen_op_test_pl,
+ gen_op_test_vs,
+ gen_op_test_vc,
+ gen_op_test_hi,
+ gen_op_test_ls,
+ gen_op_test_ge,
+ gen_op_test_lt,
+ gen_op_test_gt,
+ gen_op_test_le,
+};
+
+const uint8_t table_logic_cc[16] = {
+ 1, /* and */
+ 1, /* xor */
+ 0, /* sub */
+ 0, /* rsb */
+ 0, /* add */
+ 0, /* adc */
+ 0, /* sbc */
+ 0, /* rsc */
+ 1, /* andl */
+ 1, /* xorl */
+ 0, /* cmp */
+ 0, /* cmn */
+ 1, /* orr */
+ 1, /* mov */
+ 1, /* bic */
+ 1, /* mvn */
+};
+
+static GenOpFunc1 *gen_shift_T1_im[4] = {
+ gen_op_shll_T1_im,
+ gen_op_shrl_T1_im,
+ gen_op_sarl_T1_im,
+ gen_op_rorl_T1_im,
+};
+
+static GenOpFunc *gen_shift_T1_0[4] = {
+ NULL,
+ gen_op_shrl_T1_0,
+ gen_op_sarl_T1_0,
+ gen_op_rrxl_T1,
+};
+
+static GenOpFunc1 *gen_shift_T2_im[4] = {
+ gen_op_shll_T2_im,
+ gen_op_shrl_T2_im,
+ gen_op_sarl_T2_im,
+ gen_op_rorl_T2_im,
+};
+
+static GenOpFunc *gen_shift_T2_0[4] = {
+ NULL,
+ gen_op_shrl_T2_0,
+ gen_op_sarl_T2_0,
+ gen_op_rrxl_T2,
+};
+
+static GenOpFunc1 *gen_shift_T1_im_cc[4] = {
+ gen_op_shll_T1_im_cc,
+ gen_op_shrl_T1_im_cc,
+ gen_op_sarl_T1_im_cc,
+ gen_op_rorl_T1_im_cc,
+};
+
+static GenOpFunc *gen_shift_T1_0_cc[4] = {
+ NULL,
+ gen_op_shrl_T1_0_cc,
+ gen_op_sarl_T1_0_cc,
+ gen_op_rrxl_T1_cc,
+};
+
+static GenOpFunc *gen_shift_T1_T0[4] = {
+ gen_op_shll_T1_T0,
+ gen_op_shrl_T1_T0,
+ gen_op_sarl_T1_T0,
+ gen_op_rorl_T1_T0,
+};
+
+static GenOpFunc *gen_shift_T1_T0_cc[4] = {
+ gen_op_shll_T1_T0_cc,
+ gen_op_shrl_T1_T0_cc,
+ gen_op_sarl_T1_T0_cc,
+ gen_op_rorl_T1_T0_cc,
+};
+
+static GenOpFunc *gen_op_movl_TN_reg[3][16] = {
+ {
+ gen_op_movl_T0_r0,
+ gen_op_movl_T0_r1,
+ gen_op_movl_T0_r2,
+ gen_op_movl_T0_r3,
+ gen_op_movl_T0_r4,
+ gen_op_movl_T0_r5,
+ gen_op_movl_T0_r6,
+ gen_op_movl_T0_r7,
+ gen_op_movl_T0_r8,
+ gen_op_movl_T0_r9,
+ gen_op_movl_T0_r10,
+ gen_op_movl_T0_r11,
+ gen_op_movl_T0_r12,
+ gen_op_movl_T0_r13,
+ gen_op_movl_T0_r14,
+ gen_op_movl_T0_r15,
+ },
+ {
+ gen_op_movl_T1_r0,
+ gen_op_movl_T1_r1,
+ gen_op_movl_T1_r2,
+ gen_op_movl_T1_r3,
+ gen_op_movl_T1_r4,
+ gen_op_movl_T1_r5,
+ gen_op_movl_T1_r6,
+ gen_op_movl_T1_r7,
+ gen_op_movl_T1_r8,
+ gen_op_movl_T1_r9,
+ gen_op_movl_T1_r10,
+ gen_op_movl_T1_r11,
+ gen_op_movl_T1_r12,
+ gen_op_movl_T1_r13,
+ gen_op_movl_T1_r14,
+ gen_op_movl_T1_r15,
+ },
+ {
+ gen_op_movl_T2_r0,
+ gen_op_movl_T2_r1,
+ gen_op_movl_T2_r2,
+ gen_op_movl_T2_r3,
+ gen_op_movl_T2_r4,
+ gen_op_movl_T2_r5,
+ gen_op_movl_T2_r6,
+ gen_op_movl_T2_r7,
+ gen_op_movl_T2_r8,
+ gen_op_movl_T2_r9,
+ gen_op_movl_T2_r10,
+ gen_op_movl_T2_r11,
+ gen_op_movl_T2_r12,
+ gen_op_movl_T2_r13,
+ gen_op_movl_T2_r14,
+ gen_op_movl_T2_r15,
+ },
+};
+
+static GenOpFunc *gen_op_movl_reg_TN[2][16] = {
+ {
+ gen_op_movl_r0_T0,
+ gen_op_movl_r1_T0,
+ gen_op_movl_r2_T0,
+ gen_op_movl_r3_T0,
+ gen_op_movl_r4_T0,
+ gen_op_movl_r5_T0,
+ gen_op_movl_r6_T0,
+ gen_op_movl_r7_T0,
+ gen_op_movl_r8_T0,
+ gen_op_movl_r9_T0,
+ gen_op_movl_r10_T0,
+ gen_op_movl_r11_T0,
+ gen_op_movl_r12_T0,
+ gen_op_movl_r13_T0,
+ gen_op_movl_r14_T0,
+ gen_op_movl_r15_T0,
+ },
+ {
+ gen_op_movl_r0_T1,
+ gen_op_movl_r1_T1,
+ gen_op_movl_r2_T1,
+ gen_op_movl_r3_T1,
+ gen_op_movl_r4_T1,
+ gen_op_movl_r5_T1,
+ gen_op_movl_r6_T1,
+ gen_op_movl_r7_T1,
+ gen_op_movl_r8_T1,
+ gen_op_movl_r9_T1,
+ gen_op_movl_r10_T1,
+ gen_op_movl_r11_T1,
+ gen_op_movl_r12_T1,
+ gen_op_movl_r13_T1,
+ gen_op_movl_r14_T1,
+ gen_op_movl_r15_T1,
+ },
+};
+
+static GenOpFunc1 *gen_op_movl_TN_im[3] = {
+ gen_op_movl_T0_im,
+ gen_op_movl_T1_im,
+ gen_op_movl_T2_im,
+};
+
+static GenOpFunc1 *gen_shift_T0_im_thumb[3] = {
+ gen_op_shll_T0_im_thumb,
+ gen_op_shrl_T0_im_thumb,
+ gen_op_sarl_T0_im_thumb,
+};
+
+static inline void gen_bx(DisasContext *s)
+{
+ s->is_jmp = DISAS_UPDATE;
+ gen_op_bx_T0();
+}
+
+
+#if defined(CONFIG_USER_ONLY)
+#define gen_ldst(name, s) gen_op_##name##_raw()
+#else
+#define gen_ldst(name, s) do { \
+ if (IS_USER(s)) \
+ gen_op_##name##_user(); \
+ else \
+ gen_op_##name##_kernel(); \
+ } while (0)
+#endif
+
+static inline void gen_movl_TN_reg(DisasContext *s, int reg, int t)
+{
+ int val;
+
+ if (reg == 15) {
+ /* normaly, since we updated PC, we need only to add one insn */
+ if (s->thumb)
+ val = (long)s->pc + 2;
+ else
+ val = (long)s->pc + 4;
+ gen_op_movl_TN_im[t](val);
+ } else {
+ gen_op_movl_TN_reg[t][reg]();
+ }
+}
+
+static inline void gen_movl_T0_reg(DisasContext *s, int reg)
+{
+ gen_movl_TN_reg(s, reg, 0);
+}
+
+static inline void gen_movl_T1_reg(DisasContext *s, int reg)
+{
+ gen_movl_TN_reg(s, reg, 1);
+}
+
+static inline void gen_movl_T2_reg(DisasContext *s, int reg)
+{
+ gen_movl_TN_reg(s, reg, 2);
+}
+
+static inline void gen_movl_reg_TN(DisasContext *s, int reg, int t)
+{
+ gen_op_movl_reg_TN[t][reg]();
+ if (reg == 15) {
+ s->is_jmp = DISAS_JUMP;
+ }
+}
+
+static inline void gen_movl_reg_T0(DisasContext *s, int reg)
+{
+ gen_movl_reg_TN(s, reg, 0);
+}
+
+static inline void gen_movl_reg_T1(DisasContext *s, int reg)
+{
+ gen_movl_reg_TN(s, reg, 1);
+}
+
+/* Force a TB lookup after an instruction that changes the CPU state. */
+static inline void gen_lookup_tb(DisasContext *s)
+{
+ gen_op_movl_T0_im(s->pc);
+ gen_movl_reg_T0(s, 15);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+static inline void gen_add_data_offset(DisasContext *s, unsigned int insn)
+{
+ int val, rm, shift, shiftop;
+
+ if (!(insn & (1 << 25))) {
+ /* immediate */
+ val = insn & 0xfff;
+ if (!(insn & (1 << 23)))
+ val = -val;
+ if (val != 0)
+ gen_op_addl_T1_im(val);
+ } else {
+ /* shift/register */
+ rm = (insn) & 0xf;
+ shift = (insn >> 7) & 0x1f;
+ gen_movl_T2_reg(s, rm);
+ shiftop = (insn >> 5) & 3;
+ if (shift != 0) {
+ gen_shift_T2_im[shiftop](shift);
+ } else if (shiftop != 0) {
+ gen_shift_T2_0[shiftop]();
+ }
+ if (!(insn & (1 << 23)))
+ gen_op_subl_T1_T2();
+ else
+ gen_op_addl_T1_T2();
+ }
+}
+
+static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
+ int extra)
+{
+ int val, rm;
+
+ if (insn & (1 << 22)) {
+ /* immediate */
+ val = (insn & 0xf) | ((insn >> 4) & 0xf0);
+ val += extra;
+ if (!(insn & (1 << 23)))
+ val = -val;
+ if (val != 0)
+ gen_op_addl_T1_im(val);
+ } else {
+ /* register */
+ if (extra)
+ gen_op_addl_T1_im(extra);
+ rm = (insn) & 0xf;
+ gen_movl_T2_reg(s, rm);
+ if (!(insn & (1 << 23)))
+ gen_op_subl_T1_T2();
+ else
+ gen_op_addl_T1_T2();
+ }
+}
+
+#define VFP_OP(name) \
+static inline void gen_vfp_##name(int dp) \
+{ \
+ if (dp) \
+ gen_op_vfp_##name##d(); \
+ else \
+ gen_op_vfp_##name##s(); \
+}
+
+VFP_OP(add)
+VFP_OP(sub)
+VFP_OP(mul)
+VFP_OP(div)
+VFP_OP(neg)
+VFP_OP(abs)
+VFP_OP(sqrt)
+VFP_OP(cmp)
+VFP_OP(cmpe)
+VFP_OP(F1_ld0)
+VFP_OP(uito)
+VFP_OP(sito)
+VFP_OP(toui)
+VFP_OP(touiz)
+VFP_OP(tosi)
+VFP_OP(tosiz)
+
+#undef VFP_OP
+
+static inline void gen_vfp_ld(DisasContext *s, int dp)
+{
+ if (dp)
+ gen_ldst(vfp_ldd, s);
+ else
+ gen_ldst(vfp_lds, s);
+}
+
+static inline void gen_vfp_st(DisasContext *s, int dp)
+{
+ if (dp)
+ gen_ldst(vfp_std, s);
+ else
+ gen_ldst(vfp_sts, s);
+}
+
+static inline long
+vfp_reg_offset (int dp, int reg)
+{
+ if (dp)
+ return offsetof(CPUARMState, vfp.regs[reg]);
+ else if (reg & 1) {
+ return offsetof(CPUARMState, vfp.regs[reg >> 1])
+ + offsetof(CPU_DoubleU, l.upper);
+ } else {
+ return offsetof(CPUARMState, vfp.regs[reg >> 1])
+ + offsetof(CPU_DoubleU, l.lower);
+ }
+}
+static inline void gen_mov_F0_vreg(int dp, int reg)
+{
+ if (dp)
+ gen_op_vfp_getreg_F0d(vfp_reg_offset(dp, reg));
+ else
+ gen_op_vfp_getreg_F0s(vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_F1_vreg(int dp, int reg)
+{
+ if (dp)
+ gen_op_vfp_getreg_F1d(vfp_reg_offset(dp, reg));
+ else
+ gen_op_vfp_getreg_F1s(vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_vreg_F0(int dp, int reg)
+{
+ if (dp)
+ gen_op_vfp_setreg_F0d(vfp_reg_offset(dp, reg));
+ else
+ gen_op_vfp_setreg_F0s(vfp_reg_offset(dp, reg));
+}
+
+/* Disassemble system coprocessor (cp15) instruction. Return nonzero if
+ instruction is not defined. */
+static int disas_cp15_insn(DisasContext *s, uint32_t insn)
+{
+ uint32_t rd;
+
+ /* ??? Some cp15 registers are accessible from userspace. */
+ if (IS_USER(s)) {
+ return 1;
+ }
+ if ((insn & 0x0fff0fff) == 0x0e070f90
+ || (insn & 0x0fff0fff) == 0x0e070f58) {
+ /* Wait for interrupt. */
+ gen_op_movl_T0_im((long)s->pc);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_wfi();
+ s->is_jmp = DISAS_JUMP;
+ return 0;
+ }
+ rd = (insn >> 12) & 0xf;
+ if (insn & (1 << 20)) {
+ gen_op_movl_T0_cp15(insn);
+ /* If the destination register is r15 then sets condition codes. */
+ if (rd != 15)
+ gen_movl_reg_T0(s, rd);
+ } else {
+ gen_movl_T0_reg(s, rd);
+ gen_op_movl_cp15_T0(insn);
+ }
+ gen_lookup_tb(s);
+ return 0;
+}
+
+/* Disassemble a VFP instruction. Returns nonzero if an error occured
+ (ie. an undefined instruction). */
+static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask;
+ int dp, veclen;
+
+ if (!arm_feature(env, ARM_FEATURE_VFP))
+ return 1;
+
+ if ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) == 0) {
+ /* VFP disabled. Only allow fmxr/fmrx to/from fpexc and fpsid. */
+ if ((insn & 0x0fe00fff) != 0x0ee00a10)
+ return 1;
+ rn = (insn >> 16) & 0xf;
+ if (rn != 0 && rn != 8)
+ return 1;
+ }
+ dp = ((insn & 0xf00) == 0xb00);
+ switch ((insn >> 24) & 0xf) {
+ case 0xe:
+ if (insn & (1 << 4)) {
+ /* single register transfer */
+ if ((insn & 0x6f) != 0x00)
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ if (dp) {
+ if (insn & 0x80)
+ return 1;
+ rn = (insn >> 16) & 0xf;
+ /* Get the existing value even for arm->vfp moves because
+ we only set half the register. */
+ gen_mov_F0_vreg(1, rn);
+ gen_op_vfp_mrrd();
+ if (insn & (1 << 20)) {
+ /* vfp->arm */
+ if (insn & (1 << 21))
+ gen_movl_reg_T1(s, rd);
+ else
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* arm->vfp */
+ if (insn & (1 << 21))
+ gen_movl_T1_reg(s, rd);
+ else
+ gen_movl_T0_reg(s, rd);
+ gen_op_vfp_mdrr();
+ gen_mov_vreg_F0(dp, rn);
+ }
+ } else {
+ rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
+ if (insn & (1 << 20)) {
+ /* vfp->arm */
+ if (insn & (1 << 21)) {
+ /* system register */
+ rn >>= 1;
+ switch (rn) {
+ case ARM_VFP_FPSID:
+ case ARM_VFP_FPEXC:
+ case ARM_VFP_FPINST:
+ case ARM_VFP_FPINST2:
+ gen_op_vfp_movl_T0_xreg(rn);
+ break;
+ case ARM_VFP_FPSCR:
+ if (rd == 15)
+ gen_op_vfp_movl_T0_fpscr_flags();
+ else
+ gen_op_vfp_movl_T0_fpscr();
+ break;
+ default:
+ return 1;
+ }
+ } else {
+ gen_mov_F0_vreg(0, rn);
+ gen_op_vfp_mrs();
+ }
+ if (rd == 15) {
+ /* Set the 4 flag bits in the CPSR. */
+ gen_op_movl_cpsr_T0(0xf0000000);
+ } else
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* arm->vfp */
+ gen_movl_T0_reg(s, rd);
+ if (insn & (1 << 21)) {
+ rn >>= 1;
+ /* system register */
+ switch (rn) {
+ case ARM_VFP_FPSID:
+ /* Writes are ignored. */
+ break;
+ case ARM_VFP_FPSCR:
+ gen_op_vfp_movl_fpscr_T0();
+ gen_lookup_tb(s);
+ break;
+ case ARM_VFP_FPEXC:
+ gen_op_vfp_movl_xreg_T0(rn);
+ gen_lookup_tb(s);
+ break;
+ case ARM_VFP_FPINST:
+ case ARM_VFP_FPINST2:
+ gen_op_vfp_movl_xreg_T0(rn);
+ break;
+ default:
+ return 1;
+ }
+ } else {
+ gen_op_vfp_msr();
+ gen_mov_vreg_F0(0, rn);
+ }
+ }
+ }
+ } else {
+ /* data processing */
+ /* The opcode is in bits 23, 21, 20 and 6. */
+ op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1);
+ if (dp) {
+ if (op == 15) {
+ /* rn is opcode */
+ rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
+ } else {
+ /* rn is register number */
+ if (insn & (1 << 7))
+ return 1;
+ rn = (insn >> 16) & 0xf;
+ }
+
+ if (op == 15 && (rn == 15 || rn > 17)) {
+ /* Integer or single precision destination. */
+ rd = ((insn >> 11) & 0x1e) | ((insn >> 22) & 1);
+ } else {
+ if (insn & (1 << 22))
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ }
+
+ if (op == 15 && (rn == 16 || rn == 17)) {
+ /* Integer source. */
+ rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1);
+ } else {
+ if (insn & (1 << 5))
+ return 1;
+ rm = insn & 0xf;
+ }
+ } else {
+ rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
+ if (op == 15 && rn == 15) {
+ /* Double precision destination. */
+ if (insn & (1 << 22))
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ } else
+ rd = ((insn >> 11) & 0x1e) | ((insn >> 22) & 1);
+ rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1);
+ }
+
+ veclen = env->vfp.vec_len;
+ if (op == 15 && rn > 3)
+ veclen = 0;
+
+ /* Shut up compiler warnings. */
+ delta_m = 0;
+ delta_d = 0;
+ bank_mask = 0;
+
+ if (veclen > 0) {
+ if (dp)
+ bank_mask = 0xc;
+ else
+ bank_mask = 0x18;
+
+ /* Figure out what type of vector operation this is. */
+ if ((rd & bank_mask) == 0) {
+ /* scalar */
+ veclen = 0;
+ } else {
+ if (dp)
+ delta_d = (env->vfp.vec_stride >> 1) + 1;
+ else
+ delta_d = env->vfp.vec_stride + 1;
+
+ if ((rm & bank_mask) == 0) {
+ /* mixed scalar/vector */
+ delta_m = 0;
+ } else {
+ /* vector */
+ delta_m = delta_d;
+ }
+ }
+ }
+
+ /* Load the initial operands. */
+ if (op == 15) {
+ switch (rn) {
+ case 16:
+ case 17:
+ /* Integer source */
+ gen_mov_F0_vreg(0, rm);
+ break;
+ case 8:
+ case 9:
+ /* Compare */
+ gen_mov_F0_vreg(dp, rd);
+ gen_mov_F1_vreg(dp, rm);
+ break;
+ case 10:
+ case 11:
+ /* Compare with zero */
+ gen_mov_F0_vreg(dp, rd);
+ gen_vfp_F1_ld0(dp);
+ break;
+ default:
+ /* One source operand. */
+ gen_mov_F0_vreg(dp, rm);
+ }
+ } else {
+ /* Two source operands. */
+ gen_mov_F0_vreg(dp, rn);
+ gen_mov_F1_vreg(dp, rm);
+ }
+
+ for (;;) {
+ /* Perform the calculation. */
+ switch (op) {
+ case 0: /* mac: fd + (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_add(dp);
+ break;
+ case 1: /* nmac: fd - (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_vfp_neg(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_add(dp);
+ break;
+ case 2: /* msc: -fd + (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_sub(dp);
+ break;
+ case 3: /* nmsc: -fd - (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_add(dp);
+ gen_vfp_neg(dp);
+ break;
+ case 4: /* mul: fn * fm */
+ gen_vfp_mul(dp);
+ break;
+ case 5: /* nmul: -(fn * fm) */
+ gen_vfp_mul(dp);
+ gen_vfp_neg(dp);
+ break;
+ case 6: /* add: fn + fm */
+ gen_vfp_add(dp);
+ break;
+ case 7: /* sub: fn - fm */
+ gen_vfp_sub(dp);
+ break;
+ case 8: /* div: fn / fm */
+ gen_vfp_div(dp);
+ break;
+ case 15: /* extension space */
+ switch (rn) {
+ case 0: /* cpy */
+ /* no-op */
+ break;
+ case 1: /* abs */
+ gen_vfp_abs(dp);
+ break;
+ case 2: /* neg */
+ gen_vfp_neg(dp);
+ break;
+ case 3: /* sqrt */
+ gen_vfp_sqrt(dp);
+ break;
+ case 8: /* cmp */
+ gen_vfp_cmp(dp);
+ break;
+ case 9: /* cmpe */
+ gen_vfp_cmpe(dp);
+ break;
+ case 10: /* cmpz */
+ gen_vfp_cmp(dp);
+ break;
+ case 11: /* cmpez */
+ gen_vfp_F1_ld0(dp);
+ gen_vfp_cmpe(dp);
+ break;
+ case 15: /* single<->double conversion */
+ if (dp)
+ gen_op_vfp_fcvtsd();
+ else
+ gen_op_vfp_fcvtds();
+ break;
+ case 16: /* fuito */
+ gen_vfp_uito(dp);
+ break;
+ case 17: /* fsito */
+ gen_vfp_sito(dp);
+ break;
+ case 24: /* ftoui */
+ gen_vfp_toui(dp);
+ break;
+ case 25: /* ftouiz */
+ gen_vfp_touiz(dp);
+ break;
+ case 26: /* ftosi */
+ gen_vfp_tosi(dp);
+ break;
+ case 27: /* ftosiz */
+ gen_vfp_tosiz(dp);
+ break;
+ default: /* undefined */
+ printf ("rn:%d\n", rn);
+ return 1;
+ }
+ break;
+ default: /* undefined */
+ printf ("op:%d\n", op);
+ return 1;
+ }
+
+ /* Write back the result. */
+ if (op == 15 && (rn >= 8 && rn <= 11))
+ ; /* Comparison, do nothing. */
+ else if (op == 15 && rn > 17)
+ /* Integer result. */
+ gen_mov_vreg_F0(0, rd);
+ else if (op == 15 && rn == 15)
+ /* conversion */
+ gen_mov_vreg_F0(!dp, rd);
+ else
+ gen_mov_vreg_F0(dp, rd);
+
+ /* break out of the loop if we have finished */
+ if (veclen == 0)
+ break;
+
+ if (op == 15 && delta_m == 0) {
+ /* single source one-many */
+ while (veclen--) {
+ rd = ((rd + delta_d) & (bank_mask - 1))
+ | (rd & bank_mask);
+ gen_mov_vreg_F0(dp, rd);
+ }
+ break;
+ }
+ /* Setup the next operands. */
+ veclen--;
+ rd = ((rd + delta_d) & (bank_mask - 1))
+ | (rd & bank_mask);
+
+ if (op == 15) {
+ /* One source operand. */
+ rm = ((rm + delta_m) & (bank_mask - 1))
+ | (rm & bank_mask);
+ gen_mov_F0_vreg(dp, rm);
+ } else {
+ /* Two source operands. */
+ rn = ((rn + delta_d) & (bank_mask - 1))
+ | (rn & bank_mask);
+ gen_mov_F0_vreg(dp, rn);
+ if (delta_m) {
+ rm = ((rm + delta_m) & (bank_mask - 1))
+ | (rm & bank_mask);
+ gen_mov_F1_vreg(dp, rm);
+ }
+ }
+ }
+ }
+ break;
+ case 0xc:
+ case 0xd:
+ if (dp && (insn & (1 << 22))) {
+ /* two-register transfer */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (dp) {
+ if (insn & (1 << 5))
+ return 1;
+ rm = insn & 0xf;
+ } else
+ rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1);
+
+ if (insn & (1 << 20)) {
+ /* vfp->arm */
+ if (dp) {
+ gen_mov_F0_vreg(1, rm);
+ gen_op_vfp_mrrd();
+ gen_movl_reg_T0(s, rd);
+ gen_movl_reg_T1(s, rn);
+ } else {
+ gen_mov_F0_vreg(0, rm);
+ gen_op_vfp_mrs();
+ gen_movl_reg_T0(s, rn);
+ gen_mov_F0_vreg(0, rm + 1);
+ gen_op_vfp_mrs();
+ gen_movl_reg_T0(s, rd);
+ }
+ } else {
+ /* arm->vfp */
+ if (dp) {
+ gen_movl_T0_reg(s, rd);
+ gen_movl_T1_reg(s, rn);
+ gen_op_vfp_mdrr();
+ gen_mov_vreg_F0(1, rm);
+ } else {
+ gen_movl_T0_reg(s, rn);
+ gen_op_vfp_msr();
+ gen_mov_vreg_F0(0, rm);
+ gen_movl_T0_reg(s, rd);
+ gen_op_vfp_msr();
+ gen_mov_vreg_F0(0, rm + 1);
+ }
+ }
+ } else {
+ /* Load/store */
+ rn = (insn >> 16) & 0xf;
+ if (dp)
+ rd = (insn >> 12) & 0xf;
+ else
+ rd = ((insn >> 11) & 0x1e) | ((insn >> 22) & 1);
+ gen_movl_T1_reg(s, rn);
+ if ((insn & 0x01200000) == 0x01000000) {
+ /* Single load/store */
+ offset = (insn & 0xff) << 2;
+ if ((insn & (1 << 23)) == 0)
+ offset = -offset;
+ gen_op_addl_T1_im(offset);
+ if (insn & (1 << 20)) {
+ gen_vfp_ld(s, dp);
+ gen_mov_vreg_F0(dp, rd);
+ } else {
+ gen_mov_F0_vreg(dp, rd);
+ gen_vfp_st(s, dp);
+ }
+ } else {
+ /* load/store multiple */
+ if (dp)
+ n = (insn >> 1) & 0x7f;
+ else
+ n = insn & 0xff;
+
+ if (insn & (1 << 24)) /* pre-decrement */
+ gen_op_addl_T1_im(-((insn & 0xff) << 2));
+
+ if (dp)
+ offset = 8;
+ else
+ offset = 4;
+ for (i = 0; i < n; i++) {
+ if (insn & (1 << 20)) {
+ /* load */
+ gen_vfp_ld(s, dp);
+ gen_mov_vreg_F0(dp, rd + i);
+ } else {
+ /* store */
+ gen_mov_F0_vreg(dp, rd + i);
+ gen_vfp_st(s, dp);
+ }
+ gen_op_addl_T1_im(offset);
+ }
+ if (insn & (1 << 21)) {
+ /* writeback */
+ if (insn & (1 << 24))
+ offset = -offset * n;
+ else if (dp && (insn & 1))
+ offset = 4;
+ else
+ offset = 0;
+
+ if (offset != 0)
+ gen_op_addl_T1_im(offset);
+ gen_movl_reg_T1(s, rn);
+ }
+ }
+ }
+ break;
+ default:
+ /* Should never happen. */
+ return 1;
+ }
+ return 0;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ if (n == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_op_movl_T0_im(dest);
+ gen_op_movl_r15_T0();
+ gen_op_movl_T0_im((long)tb + n);
+ gen_op_exit_tb();
+ } else {
+ gen_op_movl_T0_im(dest);
+ gen_op_movl_r15_T0();
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ }
+}
+
+static inline void gen_jmp (DisasContext *s, uint32_t dest)
+{
+ if (__builtin_expect(s->singlestep_enabled, 0)) {
+ /* An indirect jump so that we still trigger the debug exception. */
+ if (s->thumb)
+ dest |= 1;
+ gen_op_movl_T0_im(dest);
+ gen_bx(s);
+ } else {
+ gen_goto_tb(s, 0, dest);
+ s->is_jmp = DISAS_TB_JUMP;
+ }
+}
+
+static inline void gen_mulxy(int x, int y)
+{
+ if (x)
+ gen_op_sarl_T0_im(16);
+ else
+ gen_op_sxth_T0();
+ if (y)
+ gen_op_sarl_T1_im(16);
+ else
+ gen_op_sxth_T1();
+ gen_op_mul_T0_T1();
+}
+
+/* Return the mask of PSR bits set by a MSR instruction. */
+static uint32_t msr_mask(DisasContext *s, int flags, int spsr) {
+ uint32_t mask;
+
+ mask = 0;
+ if (flags & (1 << 0))
+ mask |= 0xff;
+ if (flags & (1 << 1))
+ mask |= 0xff00;
+ if (flags & (1 << 2))
+ mask |= 0xff0000;
+ if (flags & (1 << 3))
+ mask |= 0xff000000;
+ /* Mask out undefined bits. */
+ mask &= 0xf90f03ff;
+ /* Mask out state bits. */
+ if (!spsr)
+ mask &= ~0x01000020;
+ /* Mask out privileged bits. */
+ if (IS_USER(s))
+ mask &= 0xf80f0200;
+ return mask;
+}
+
+/* Returns nonzero if access to the PSR is not permitted. */
+static int gen_set_psr_T0(DisasContext *s, uint32_t mask, int spsr)
+{
+ if (spsr) {
+ /* ??? This is also undefined in system mode. */
+ if (IS_USER(s))
+ return 1;
+ gen_op_movl_spsr_T0(mask);
+ } else {
+ gen_op_movl_cpsr_T0(mask);
+ }
+ gen_lookup_tb(s);
+ return 0;
+}
+
+static void gen_exception_return(DisasContext *s)
+{
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_movl_T0_spsr();
+ gen_op_movl_cpsr_T0(0xffffffff);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+static void disas_arm_insn(CPUState * env, DisasContext *s)
+{
+ unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
+
+ insn = ldl_code(s->pc);
+ s->pc += 4;
+
+ cond = insn >> 28;
+ if (cond == 0xf){
+ /* Unconditional instructions. */
+ if ((insn & 0x0d70f000) == 0x0550f000)
+ return; /* PLD */
+ else if ((insn & 0x0e000000) == 0x0a000000) {
+ /* branch link and change to thumb (blx <offset>) */
+ int32_t offset;
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T0_im(val);
+ gen_movl_reg_T0(s, 14);
+ /* Sign-extend the 24-bit offset */
+ offset = (((int32_t)insn) << 8) >> 8;
+ /* offset * 4 + bit24 * 2 + (thumb bit) */
+ val += (offset << 2) | ((insn >> 23) & 2) | 1;
+ /* pipeline offset */
+ val += 4;
+ gen_op_movl_T0_im(val);
+ gen_bx(s);
+ return;
+ } else if ((insn & 0x0fe00000) == 0x0c400000) {
+ /* Coprocessor double register transfer. */
+ } else if ((insn & 0x0f000010) == 0x0e000010) {
+ /* Additional coprocessor register transfer. */
+ } else if ((insn & 0x0ff10010) == 0x01000000) {
+ /* cps (privileged) */
+ } else if ((insn & 0x0ffffdff) == 0x01010000) {
+ /* setend */
+ if (insn & (1 << 9)) {
+ /* BE8 mode not implemented. */
+ goto illegal_op;
+ }
+ return;
+ }
+ goto illegal_op;
+ }
+ if (cond != 0xe) {
+ /* if not always execute, we generate a conditional jump to
+ next instruction */
+ s->condlabel = gen_new_label();
+ gen_test_cc[cond ^ 1](s->condlabel);
+ s->condjmp = 1;
+ //gen_test_cc[cond ^ 1]((long)s->tb, (long)s->pc);
+ //s->is_jmp = DISAS_JUMP_NEXT;
+ }
+ if ((insn & 0x0f900000) == 0x03000000) {
+ if ((insn & 0x0fb0f000) != 0x0320f000)
+ goto illegal_op;
+ /* CPSR = immediate */
+ val = insn & 0xff;
+ shift = ((insn >> 8) & 0xf) * 2;
+ if (shift)
+ val = (val >> shift) | (val << (32 - shift));
+ gen_op_movl_T0_im(val);
+ i = ((insn & (1 << 22)) != 0);
+ if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf, i), i))
+ goto illegal_op;
+ } else if ((insn & 0x0f900000) == 0x01000000
+ && (insn & 0x00000090) != 0x00000090) {
+ /* miscellaneous instructions */
+ op1 = (insn >> 21) & 3;
+ sh = (insn >> 4) & 0xf;
+ rm = insn & 0xf;
+ switch (sh) {
+ case 0x0: /* move program status register */
+ if (op1 & 1) {
+ /* PSR = reg */
+ gen_movl_T0_reg(s, rm);
+ i = ((op1 & 2) != 0);
+ if (gen_set_psr_T0(s, msr_mask(s, (insn >> 16) & 0xf, i), i))
+ goto illegal_op;
+ } else {
+ /* reg = PSR */
+ rd = (insn >> 12) & 0xf;
+ if (op1 & 2) {
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_movl_T0_spsr();
+ } else {
+ gen_op_movl_T0_cpsr();
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+ case 0x1:
+ if (op1 == 1) {
+ /* branch/exchange thumb (bx). */
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ } else if (op1 == 3) {
+ /* clz */
+ rd = (insn >> 12) & 0xf;
+ gen_movl_T0_reg(s, rm);
+ gen_op_clz_T0();
+ gen_movl_reg_T0(s, rd);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x2:
+ if (op1 == 1) {
+ ARCH(5J); /* bxj */
+ /* Trivial implementation equivalent to bx. */
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x3:
+ if (op1 != 1)
+ goto illegal_op;
+
+ /* branch link/exchange thumb (blx) */
+ val = (uint32_t)s->pc;
+ gen_op_movl_T0_im(val);
+ gen_movl_reg_T0(s, 14);
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ break;
+ case 0x5: /* saturating add/subtract */
+ rd = (insn >> 12) & 0xf;
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rn);
+ if (op1 & 2)
+ gen_op_double_T1_saturate();
+ if (op1 & 1)
+ gen_op_subl_T0_T1_saturate();
+ else
+ gen_op_addl_T0_T1_saturate();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 7: /* bkpt */
+ gen_op_movl_T0_im((long)s->pc - 4);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_bkpt();
+ s->is_jmp = DISAS_JUMP;
+ break;
+ case 0x8: /* signed multiply */
+ case 0xa:
+ case 0xc:
+ case 0xe:
+ rs = (insn >> 8) & 0xf;
+ rn = (insn >> 12) & 0xf;
+ rd = (insn >> 16) & 0xf;
+ if (op1 == 1) {
+ /* (32 * 16) >> 16 */
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rs);
+ if (sh & 4)
+ gen_op_sarl_T1_im(16);
+ else
+ gen_op_sxth_T1();
+ gen_op_imulw_T0_T1();
+ if ((sh & 2) == 0) {
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1_setq();
+ }
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* 16 * 16 */
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rs);
+ gen_mulxy(sh & 2, sh & 4);
+ if (op1 == 2) {
+ gen_op_signbit_T1_T0();
+ gen_op_addq_T0_T1(rn, rd);
+ gen_movl_reg_T0(s, rn);
+ gen_movl_reg_T1(s, rd);
+ } else {
+ if (op1 == 0) {
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1_setq();
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else if (((insn & 0x0e000000) == 0 &&
+ (insn & 0x00000090) != 0x90) ||
+ ((insn & 0x0e000000) == (1 << 25))) {
+ int set_cc, logic_cc, shiftop;
+
+ op1 = (insn >> 21) & 0xf;
+ set_cc = (insn >> 20) & 1;
+ logic_cc = table_logic_cc[op1] & set_cc;
+
+ /* data processing instruction */
+ if (insn & (1 << 25)) {
+ /* immediate operand */
+ val = insn & 0xff;
+ shift = ((insn >> 8) & 0xf) * 2;
+ if (shift)
+ val = (val >> shift) | (val << (32 - shift));
+ gen_op_movl_T1_im(val);
+ if (logic_cc && shift)
+ gen_op_mov_CF_T1();
+ } else {
+ /* register */
+ rm = (insn) & 0xf;
+ gen_movl_T1_reg(s, rm);
+ shiftop = (insn >> 5) & 3;
+ if (!(insn & (1 << 4))) {
+ shift = (insn >> 7) & 0x1f;
+ if (shift != 0) {
+ if (logic_cc) {
+ gen_shift_T1_im_cc[shiftop](shift);
+ } else {
+ gen_shift_T1_im[shiftop](shift);
+ }
+ } else if (shiftop != 0) {
+ if (logic_cc) {
+ gen_shift_T1_0_cc[shiftop]();
+ } else {
+ gen_shift_T1_0[shiftop]();
+ }
+ }
+ } else {
+ rs = (insn >> 8) & 0xf;
+ gen_movl_T0_reg(s, rs);
+ if (logic_cc) {
+ gen_shift_T1_T0_cc[shiftop]();
+ } else {
+ gen_shift_T1_T0[shiftop]();
+ }
+ }
+ }
+ if (op1 != 0x0f && op1 != 0x0d) {
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T0_reg(s, rn);
+ }
+ rd = (insn >> 12) & 0xf;
+ switch(op1) {
+ case 0x00:
+ gen_op_andl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x01:
+ gen_op_xorl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x02:
+ if (set_cc && rd == 15) {
+ /* SUBS r15, ... is used for exception return. */
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_subl_T0_T1_cc();
+ gen_exception_return(s);
+ } else {
+ if (set_cc)
+ gen_op_subl_T0_T1_cc();
+ else
+ gen_op_subl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+ case 0x03:
+ if (set_cc)
+ gen_op_rsbl_T0_T1_cc();
+ else
+ gen_op_rsbl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x04:
+ if (set_cc)
+ gen_op_addl_T0_T1_cc();
+ else
+ gen_op_addl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x05:
+ if (set_cc)
+ gen_op_adcl_T0_T1_cc();
+ else
+ gen_op_adcl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x06:
+ if (set_cc)
+ gen_op_sbcl_T0_T1_cc();
+ else
+ gen_op_sbcl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x07:
+ if (set_cc)
+ gen_op_rscl_T0_T1_cc();
+ else
+ gen_op_rscl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x08:
+ if (set_cc) {
+ gen_op_andl_T0_T1();
+ gen_op_logic_T0_cc();
+ }
+ break;
+ case 0x09:
+ if (set_cc) {
+ gen_op_xorl_T0_T1();
+ gen_op_logic_T0_cc();
+ }
+ break;
+ case 0x0a:
+ if (set_cc) {
+ gen_op_subl_T0_T1_cc();
+ }
+ break;
+ case 0x0b:
+ if (set_cc) {
+ gen_op_addl_T0_T1_cc();
+ }
+ break;
+ case 0x0c:
+ gen_op_orl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x0d:
+ if (logic_cc && rd == 15) {
+ /* MOVS r15, ... is used for exception return. */
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_movl_T0_T1();
+ gen_exception_return(s);
+ } else {
+ gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
+ }
+ break;
+ case 0x0e:
+ gen_op_bicl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ default:
+ case 0x0f:
+ gen_op_notl_T1();
+ gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
+ break;
+ }
+ } else {
+ /* other instructions */
+ op1 = (insn >> 24) & 0xf;
+ switch(op1) {
+ case 0x0:
+ case 0x1:
+ /* multiplies, extra load/stores */
+ sh = (insn >> 5) & 3;
+ if (sh == 0) {
+ if (op1 == 0x0) {
+ rd = (insn >> 16) & 0xf;
+ rn = (insn >> 12) & 0xf;
+ rs = (insn >> 8) & 0xf;
+ rm = (insn) & 0xf;
+ if (((insn >> 22) & 3) == 0) {
+ /* 32 bit mul */
+ gen_movl_T0_reg(s, rs);
+ gen_movl_T1_reg(s, rm);
+ gen_op_mul_T0_T1();
+ if (insn & (1 << 21)) {
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1();
+ }
+ if (insn & (1 << 20))
+ gen_op_logic_T0_cc();
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* 64 bit mul */
+ gen_movl_T0_reg(s, rs);
+ gen_movl_T1_reg(s, rm);
+ if (insn & (1 << 22))
+ gen_op_imull_T0_T1();
+ else
+ gen_op_mull_T0_T1();
+ if (insn & (1 << 21)) /* mult accumulate */
+ gen_op_addq_T0_T1(rn, rd);
+ if (!(insn & (1 << 23))) { /* double accumulate */
+ ARCH(6);
+ gen_op_addq_lo_T0_T1(rn);
+ gen_op_addq_lo_T0_T1(rd);
+ }
+ if (insn & (1 << 20))
+ gen_op_logicq_cc();
+ gen_movl_reg_T0(s, rn);
+ gen_movl_reg_T1(s, rd);
+ }
+ } else {
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (insn & (1 << 23)) {
+ /* load/store exclusive */
+ goto illegal_op;
+ } else {
+ /* SWP instruction */
+ rm = (insn) & 0xf;
+
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rn);
+ if (insn & (1 << 22)) {
+ gen_ldst(swpb, s);
+ } else {
+ gen_ldst(swpl, s);
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ }
+ } else {
+ int address_offset;
+ /* Misc load/store */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ gen_movl_T1_reg(s, rn);
+ if (insn & (1 << 24))
+ gen_add_datah_offset(s, insn, 0);
+ address_offset = 0;
+ if (insn & (1 << 20)) {
+ /* load */
+ switch(sh) {
+ case 1:
+ gen_ldst(lduw, s);
+ break;
+ case 2:
+ gen_ldst(ldsb, s);
+ break;
+ default:
+ case 3:
+ gen_ldst(ldsw, s);
+ break;
+ }
+ gen_movl_reg_T0(s, rd);
+ } else if (sh & 2) {
+ /* doubleword */
+ if (sh & 1) {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stl, s);
+ gen_op_addl_T1_im(4);
+ gen_movl_T0_reg(s, rd + 1);
+ gen_ldst(stl, s);
+ } else {
+ /* load */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd);
+ gen_op_addl_T1_im(4);
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd + 1);
+ }
+ address_offset = -4;
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stw, s);
+ }
+ if (!(insn & (1 << 24))) {
+ gen_add_datah_offset(s, insn, address_offset);
+ gen_movl_reg_T1(s, rn);
+ } else if (insn & (1 << 21)) {
+ if (address_offset)
+ gen_op_addl_T1_im(address_offset);
+ gen_movl_reg_T1(s, rn);
+ }
+ }
+ break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ /* Check for undefined extension instructions
+ * per the ARM Bible IE:
+ * xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
+ */
+ sh = (0xf << 20) | (0xf << 4);
+ if (op1 == 0x7 && ((insn & sh) == sh))
+ {
+ goto illegal_op;
+ }
+ /* load/store byte/word */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ gen_movl_T1_reg(s, rn);
+ i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000);
+ if (insn & (1 << 24))
+ gen_add_data_offset(s, insn);
+ if (insn & (1 << 20)) {
+ /* load */
+#if defined(CONFIG_USER_ONLY)
+ if (insn & (1 << 22))
+ gen_op_ldub_raw();
+ else
+ gen_op_ldl_raw();
+#else
+ if (insn & (1 << 22)) {
+ if (i)
+ gen_op_ldub_user();
+ else
+ gen_op_ldub_kernel();
+ } else {
+ if (i)
+ gen_op_ldl_user();
+ else
+ gen_op_ldl_kernel();
+ }
+#endif
+ if (rd == 15)
+ gen_bx(s);
+ else
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+#if defined(CONFIG_USER_ONLY)
+ if (insn & (1 << 22))
+ gen_op_stb_raw();
+ else
+ gen_op_stl_raw();
+#else
+ if (insn & (1 << 22)) {
+ if (i)
+ gen_op_stb_user();
+ else
+ gen_op_stb_kernel();
+ } else {
+ if (i)
+ gen_op_stl_user();
+ else
+ gen_op_stl_kernel();
+ }
+#endif
+ }
+ if (!(insn & (1 << 24))) {
+ gen_add_data_offset(s, insn);
+ gen_movl_reg_T1(s, rn);
+ } else if (insn & (1 << 21))
+ gen_movl_reg_T1(s, rn); {
+ }
+ break;
+ case 0x08:
+ case 0x09:
+ {
+ int j, n, user, loaded_base;
+ /* load/store multiple words */
+ /* XXX: store correct base if write back */
+ user = 0;
+ if (insn & (1 << 22)) {
+ if (IS_USER(s))
+ goto illegal_op; /* only usable in supervisor mode */
+
+ if ((insn & (1 << 15)) == 0)
+ user = 1;
+ }
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T1_reg(s, rn);
+
+ /* compute total size */
+ loaded_base = 0;
+ n = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i))
+ n++;
+ }
+ /* XXX: test invalid n == 0 case ? */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ gen_op_addl_T1_im(4);
+ } else {
+ /* post increment */
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ gen_op_addl_T1_im(-(n * 4));
+ } else {
+ /* post decrement */
+ if (n != 1)
+ gen_op_addl_T1_im(-((n - 1) * 4));
+ }
+ }
+ j = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 20)) {
+ /* load */
+ gen_ldst(ldl, s);
+ if (i == 15) {
+ gen_bx(s);
+ } else if (user) {
+ gen_op_movl_user_T0(i);
+ } else if (i == rn) {
+ gen_op_movl_T2_T0();
+ loaded_base = 1;
+ } else {
+ gen_movl_reg_T0(s, i);
+ }
+ } else {
+ /* store */
+ if (i == 15) {
+ /* special case: r15 = PC + 12 */
+ val = (long)s->pc + 8;
+ gen_op_movl_TN_im[0](val);
+ } else if (user) {
+ gen_op_movl_T0_user(i);
+ } else {
+ gen_movl_T0_reg(s, i);
+ }
+ gen_ldst(stl, s);
+ }
+ j++;
+ /* no need to add after the last transfer */
+ if (j != n)
+ gen_op_addl_T1_im(4);
+ }
+ }
+ if (insn & (1 << 21)) {
+ /* write back */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ } else {
+ /* post increment */
+ gen_op_addl_T1_im(4);
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ if (n != 1)
+ gen_op_addl_T1_im(-((n - 1) * 4));
+ } else {
+ /* post decrement */
+ gen_op_addl_T1_im(-(n * 4));
+ }
+ }
+ gen_movl_reg_T1(s, rn);
+ }
+ if (loaded_base) {
+ gen_op_movl_T0_T2();
+ gen_movl_reg_T0(s, rn);
+ }
+ if ((insn & (1 << 22)) && !user) {
+ /* Restore CPSR from SPSR. */
+ gen_op_movl_T0_spsr();
+ gen_op_movl_cpsr_T0(0xffffffff);
+ s->is_jmp = DISAS_UPDATE;
+ }
+ }
+ break;
+ case 0xa:
+ case 0xb:
+ {
+ int32_t offset;
+
+ /* branch (and link) */
+ val = (int32_t)s->pc;
+ if (insn & (1 << 24)) {
+ gen_op_movl_T0_im(val);
+ gen_op_movl_reg_TN[0][14]();
+ }
+ offset = (((int32_t)insn << 8) >> 8);
+ val += (offset << 2) + 4;
+ gen_jmp(s, val);
+ }
+ break;
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ /* Coprocessor. */
+ op1 = (insn >> 8) & 0xf;
+ switch (op1) {
+ case 10:
+ case 11:
+ if (disas_vfp_insn (env, s, insn))
+ goto illegal_op;
+ break;
+ case 15:
+ if (disas_cp15_insn (s, insn))
+ goto illegal_op;
+ break;
+ default:
+ /* unknown coprocessor. */
+ goto illegal_op;
+ }
+ break;
+ case 0xf:
+ /* swi */
+ gen_op_movl_T0_im((long)s->pc);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_swi();
+ s->is_jmp = DISAS_JUMP;
+ break;
+ default:
+ illegal_op:
+ gen_op_movl_T0_im((long)s->pc - 4);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_undef_insn();
+ s->is_jmp = DISAS_JUMP;
+ break;
+ }
+ }
+}
+
+static void disas_thumb_insn(DisasContext *s)
+{
+ uint32_t val, insn, op, rm, rn, rd, shift, cond;
+ int32_t offset;
+ int i;
+
+ insn = lduw_code(s->pc);
+ s->pc += 2;
+
+ switch (insn >> 12) {
+ case 0: case 1:
+ rd = insn & 7;
+ op = (insn >> 11) & 3;
+ if (op == 3) {
+ /* add/subtract */
+ rn = (insn >> 3) & 7;
+ gen_movl_T0_reg(s, rn);
+ if (insn & (1 << 10)) {
+ /* immediate */
+ gen_op_movl_T1_im((insn >> 6) & 7);
+ } else {
+ /* reg */
+ rm = (insn >> 6) & 7;
+ gen_movl_T1_reg(s, rm);
+ }
+ if (insn & (1 << 9))
+ gen_op_subl_T0_T1_cc();
+ else
+ gen_op_addl_T0_T1_cc();
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* shift immediate */
+ rm = (insn >> 3) & 7;
+ shift = (insn >> 6) & 0x1f;
+ gen_movl_T0_reg(s, rm);
+ gen_shift_T0_im_thumb[op](shift);
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+ case 2: case 3:
+ /* arithmetic large immediate */
+ op = (insn >> 11) & 3;
+ rd = (insn >> 8) & 0x7;
+ if (op == 0) {
+ gen_op_movl_T0_im(insn & 0xff);
+ } else {
+ gen_movl_T0_reg(s, rd);
+ gen_op_movl_T1_im(insn & 0xff);
+ }
+ switch (op) {
+ case 0: /* mov */
+ gen_op_logic_T0_cc();
+ break;
+ case 1: /* cmp */
+ gen_op_subl_T0_T1_cc();
+ break;
+ case 2: /* add */
+ gen_op_addl_T0_T1_cc();
+ break;
+ case 3: /* sub */
+ gen_op_subl_T0_T1_cc();
+ break;
+ }
+ if (op != 1)
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 4:
+ if (insn & (1 << 11)) {
+ rd = (insn >> 8) & 7;
+ /* load pc-relative. Bit 1 of PC is ignored. */
+ val = s->pc + 2 + ((insn & 0xff) * 4);
+ val &= ~(uint32_t)2;
+ gen_op_movl_T1_im(val);
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd);
+ break;
+ }
+ if (insn & (1 << 10)) {
+ /* data processing extended or blx */
+ rd = (insn & 7) | ((insn >> 4) & 8);
+ rm = (insn >> 3) & 0xf;
+ op = (insn >> 8) & 3;
+ switch (op) {
+ case 0: /* add */
+ gen_movl_T0_reg(s, rd);
+ gen_movl_T1_reg(s, rm);
+ gen_op_addl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 1: /* cmp */
+ gen_movl_T0_reg(s, rd);
+ gen_movl_T1_reg(s, rm);
+ gen_op_subl_T0_T1_cc();
+ break;
+ case 2: /* mov/cpy */
+ gen_movl_T0_reg(s, rm);
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 3:/* branch [and link] exchange thumb register */
+ if (insn & (1 << 7)) {
+ val = (uint32_t)s->pc | 1;
+ gen_op_movl_T1_im(val);
+ gen_movl_reg_T1(s, 14);
+ }
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ break;
+ }
+ break;
+ }
+
+ /* data processing register */
+ rd = insn & 7;
+ rm = (insn >> 3) & 7;
+ op = (insn >> 6) & 0xf;
+ if (op == 2 || op == 3 || op == 4 || op == 7) {
+ /* the shift/rotate ops want the operands backwards */
+ val = rm;
+ rm = rd;
+ rd = val;
+ val = 1;
+ } else {
+ val = 0;
+ }
+
+ if (op == 9) /* neg */
+ gen_op_movl_T0_im(0);
+ else if (op != 0xf) /* mvn doesn't read its first operand */
+ gen_movl_T0_reg(s, rd);
+
+ gen_movl_T1_reg(s, rm);
+ switch (op) {
+ case 0x0: /* and */
+ gen_op_andl_T0_T1();
+ gen_op_logic_T0_cc();
+ break;
+ case 0x1: /* eor */
+ gen_op_xorl_T0_T1();
+ gen_op_logic_T0_cc();
+ break;
+ case 0x2: /* lsl */
+ gen_op_shll_T1_T0_cc();
+ gen_op_logic_T1_cc();
+ break;
+ case 0x3: /* lsr */
+ gen_op_shrl_T1_T0_cc();
+ gen_op_logic_T1_cc();
+ break;
+ case 0x4: /* asr */
+ gen_op_sarl_T1_T0_cc();
+ gen_op_logic_T1_cc();
+ break;
+ case 0x5: /* adc */
+ gen_op_adcl_T0_T1_cc();
+ break;
+ case 0x6: /* sbc */
+ gen_op_sbcl_T0_T1_cc();
+ break;
+ case 0x7: /* ror */
+ gen_op_rorl_T1_T0_cc();
+ gen_op_logic_T1_cc();
+ break;
+ case 0x8: /* tst */
+ gen_op_andl_T0_T1();
+ gen_op_logic_T0_cc();
+ rd = 16;
+ break;
+ case 0x9: /* neg */
+ gen_op_subl_T0_T1_cc();
+ break;
+ case 0xa: /* cmp */
+ gen_op_subl_T0_T1_cc();
+ rd = 16;
+ break;
+ case 0xb: /* cmn */
+ gen_op_addl_T0_T1_cc();
+ rd = 16;
+ break;
+ case 0xc: /* orr */
+ gen_op_orl_T0_T1();
+ gen_op_logic_T0_cc();
+ break;
+ case 0xd: /* mul */
+ gen_op_mull_T0_T1();
+ gen_op_logic_T0_cc();
+ break;
+ case 0xe: /* bic */
+ gen_op_bicl_T0_T1();
+ gen_op_logic_T0_cc();
+ break;
+ case 0xf: /* mvn */
+ gen_op_notl_T1();
+ gen_op_logic_T1_cc();
+ val = 1;
+ rm = rd;
+ break;
+ }
+ if (rd != 16) {
+ if (val)
+ gen_movl_reg_T1(s, rm);
+ else
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+
+ case 5:
+ /* load/store register offset. */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ rm = (insn >> 6) & 7;
+ op = (insn >> 9) & 7;
+ gen_movl_T1_reg(s, rn);
+ gen_movl_T2_reg(s, rm);
+ gen_op_addl_T1_T2();
+
+ if (op < 3) /* store */
+ gen_movl_T0_reg(s, rd);
+
+ switch (op) {
+ case 0: /* str */
+ gen_ldst(stl, s);
+ break;
+ case 1: /* strh */
+ gen_ldst(stw, s);
+ break;
+ case 2: /* strb */
+ gen_ldst(stb, s);
+ break;
+ case 3: /* ldrsb */
+ gen_ldst(ldsb, s);
+ break;
+ case 4: /* ldr */
+ gen_ldst(ldl, s);
+ break;
+ case 5: /* ldrh */
+ gen_ldst(lduw, s);
+ break;
+ case 6: /* ldrb */
+ gen_ldst(ldub, s);
+ break;
+ case 7: /* ldrsh */
+ gen_ldst(ldsw, s);
+ break;
+ }
+ if (op >= 3) /* load */
+ gen_movl_reg_T0(s, rd);
+ break;
+
+ case 6:
+ /* load/store word immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ gen_movl_T1_reg(s, rn);
+ val = (insn >> 4) & 0x7c;
+ gen_op_movl_T2_im(val);
+ gen_op_addl_T1_T2();
+
+ if (insn & (1 << 11)) {
+ /* load */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stl, s);
+ }
+ break;
+
+ case 7:
+ /* load/store byte immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ gen_movl_T1_reg(s, rn);
+ val = (insn >> 6) & 0x1f;
+ gen_op_movl_T2_im(val);
+ gen_op_addl_T1_T2();
+
+ if (insn & (1 << 11)) {
+ /* load */
+ gen_ldst(ldub, s);
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stb, s);
+ }
+ break;
+
+ case 8:
+ /* load/store halfword immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ gen_movl_T1_reg(s, rn);
+ val = (insn >> 5) & 0x3e;
+ gen_op_movl_T2_im(val);
+ gen_op_addl_T1_T2();
+
+ if (insn & (1 << 11)) {
+ /* load */
+ gen_ldst(lduw, s);
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stw, s);
+ }
+ break;
+
+ case 9:
+ /* load/store from stack */
+ rd = (insn >> 8) & 7;
+ gen_movl_T1_reg(s, 13);
+ val = (insn & 0xff) * 4;
+ gen_op_movl_T2_im(val);
+ gen_op_addl_T1_T2();
+
+ if (insn & (1 << 11)) {
+ /* load */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stl, s);
+ }
+ break;
+
+ case 10:
+ /* add to high reg */
+ rd = (insn >> 8) & 7;
+ if (insn & (1 << 11)) {
+ /* SP */
+ gen_movl_T0_reg(s, 13);
+ } else {
+ /* PC. bit 1 is ignored. */
+ gen_op_movl_T0_im((s->pc + 2) & ~(uint32_t)2);
+ }
+ val = (insn & 0xff) * 4;
+ gen_op_movl_T1_im(val);
+ gen_op_addl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+
+ case 11:
+ /* misc */
+ op = (insn >> 8) & 0xf;
+ switch (op) {
+ case 0:
+ /* adjust stack pointer */
+ gen_movl_T1_reg(s, 13);
+ val = (insn & 0x7f) * 4;
+ if (insn & (1 << 7))
+ val = -(int32_t)val;
+ gen_op_movl_T2_im(val);
+ gen_op_addl_T1_T2();
+ gen_movl_reg_T1(s, 13);
+ break;
+
+ case 4: case 5: case 0xc: case 0xd:
+ /* push/pop */
+ gen_movl_T1_reg(s, 13);
+ if (insn & (1 << 8))
+ offset = 4;
+ else
+ offset = 0;
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i))
+ offset += 4;
+ }
+ if ((insn & (1 << 11)) == 0) {
+ gen_op_movl_T2_im(-offset);
+ gen_op_addl_T1_T2();
+ }
+ gen_op_movl_T2_im(4);
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 11)) {
+ /* pop */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, i);
+ } else {
+ /* push */
+ gen_movl_T0_reg(s, i);
+ gen_ldst(stl, s);
+ }
+ /* advance to the next address. */
+ gen_op_addl_T1_T2();
+ }
+ }
+ if (insn & (1 << 8)) {
+ if (insn & (1 << 11)) {
+ /* pop pc */
+ gen_ldst(ldl, s);
+ /* don't set the pc until the rest of the instruction
+ has completed */
+ } else {
+ /* push lr */
+ gen_movl_T0_reg(s, 14);
+ gen_ldst(stl, s);
+ }
+ gen_op_addl_T1_T2();
+ }
+ if ((insn & (1 << 11)) == 0) {
+ gen_op_movl_T2_im(-offset);
+ gen_op_addl_T1_T2();
+ }
+ /* write back the new stack pointer */
+ gen_movl_reg_T1(s, 13);
+ /* set the new PC value */
+ if ((insn & 0x0900) == 0x0900)
+ gen_bx(s);
+ break;
+
+ case 0xe: /* bkpt */
+ gen_op_movl_T0_im((long)s->pc - 2);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_bkpt();
+ s->is_jmp = DISAS_JUMP;
+ break;
+
+ default:
+ goto undef;
+ }
+ break;
+
+ case 12:
+ /* load/store multiple */
+ rn = (insn >> 8) & 0x7;
+ gen_movl_T1_reg(s, rn);
+ gen_op_movl_T2_im(4);
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 11)) {
+ /* load */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, i);
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, i);
+ gen_ldst(stl, s);
+ }
+ /* advance to the next address */
+ gen_op_addl_T1_T2();
+ }
+ }
+ /* Base register writeback. */
+ if ((insn & (1 << rn)) == 0)
+ gen_movl_reg_T1(s, rn);
+ break;
+
+ case 13:
+ /* conditional branch or swi */
+ cond = (insn >> 8) & 0xf;
+ if (cond == 0xe)
+ goto undef;
+
+ if (cond == 0xf) {
+ /* swi */
+ gen_op_movl_T0_im((long)s->pc | 1);
+ /* Don't set r15. */
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_swi();
+ s->is_jmp = DISAS_JUMP;
+ break;
+ }
+ /* generate a conditional jump to next instruction */
+ s->condlabel = gen_new_label();
+ gen_test_cc[cond ^ 1](s->condlabel);
+ s->condjmp = 1;
+ //gen_test_cc[cond ^ 1]((long)s->tb, (long)s->pc);
+ //s->is_jmp = DISAS_JUMP_NEXT;
+ gen_movl_T1_reg(s, 15);
+
+ /* jump to the offset */
+ val = (uint32_t)s->pc + 2;
+ offset = ((int32_t)insn << 24) >> 24;
+ val += offset << 1;
+ gen_jmp(s, val);
+ break;
+
+ case 14:
+ /* unconditional branch */
+ if (insn & (1 << 11)) {
+ /* Second half of blx. */
+ offset = ((insn & 0x7ff) << 1);
+ gen_movl_T0_reg(s, 14);
+ gen_op_movl_T1_im(offset);
+ gen_op_addl_T0_T1();
+ gen_op_movl_T1_im(0xfffffffc);
+ gen_op_andl_T0_T1();
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T1_im(val | 1);
+ gen_movl_reg_T1(s, 14);
+ gen_bx(s);
+ break;
+ }
+ val = (uint32_t)s->pc;
+ offset = ((int32_t)insn << 21) >> 21;
+ val += (offset << 1) + 2;
+ gen_jmp(s, val);
+ break;
+
+ case 15:
+ /* branch and link [and switch to arm] */
+ if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+ /* Instruction spans a page boundary. Implement it as two
+ 16-bit instructions in case the second half causes an
+ prefetch abort. */
+ offset = ((int32_t)insn << 21) >> 9;
+ val = s->pc + 2 + offset;
+ gen_op_movl_T0_im(val);
+ gen_movl_reg_T0(s, 14);
+ break;
+ }
+ if (insn & (1 << 11)) {
+ /* Second half of bl. */
+ offset = ((insn & 0x7ff) << 1) | 1;
+ gen_movl_T0_reg(s, 14);
+ gen_op_movl_T1_im(offset);
+ gen_op_addl_T0_T1();
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T1_im(val | 1);
+ gen_movl_reg_T1(s, 14);
+ gen_bx(s);
+ break;
+ }
+ offset = ((int32_t)insn << 21) >> 10;
+ insn = lduw_code(s->pc);
+ offset |= insn & 0x7ff;
+
+ val = (uint32_t)s->pc + 2;
+ gen_op_movl_T1_im(val | 1);
+ gen_movl_reg_T1(s, 14);
+
+ val += offset << 1;
+ if (insn & (1 << 12)) {
+ /* bl */
+ gen_jmp(s, val);
+ } else {
+ /* blx */
+ val &= ~(uint32_t)2;
+ gen_op_movl_T0_im(val);
+ gen_bx(s);
+ }
+ }
+ return;
+undef:
+ gen_op_movl_T0_im((long)s->pc - 2);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_undef_insn();
+ s->is_jmp = DISAS_JUMP;
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+ basic block 'tb'. If search_pc is TRUE, also generate PC
+ information for each intermediate instruction. */
+static inline int gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ uint16_t *gen_opc_end;
+ int j, lj;
+ target_ulong pc_start;
+ uint32_t next_page_start;
+
+ /* generate intermediate code */
+ pc_start = tb->pc;
+
+ dc->tb = tb;
+
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = pc_start;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->condjmp = 0;
+ dc->thumb = env->thumb;
+#if !defined(CONFIG_USER_ONLY)
+ dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR;
+#endif
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ nb_gen_labels = 0;
+ lj = -1;
+ do {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == dc->pc) {
+ gen_op_movl_T0_im((long)dc->pc);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_debug();
+ dc->is_jmp = DISAS_JUMP;
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+ gen_opc_pc[lj] = dc->pc;
+ gen_opc_instr_start[lj] = 1;
+ }
+
+ if (env->thumb)
+ disas_thumb_insn(dc);
+ else
+ disas_arm_insn(env, dc);
+
+ if (dc->condjmp && !dc->is_jmp) {
+ gen_set_label(dc->condlabel);
+ dc->condjmp = 0;
+ }
+ /* Translation stops when a conditional branch is enoutered.
+ * Otherwise the subsequent code could get translated several times.
+ * Also stop translation when a page boundary is reached. This
+ * ensures prefech aborts occur at the right place. */
+ } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+ !env->singlestep_enabled &&
+ dc->pc < next_page_start);
+ /* At this stage dc->condjmp will only be set when the skipped
+ * instruction was a conditional branch, and the PC has already been
+ * written. */
+ if (__builtin_expect(env->singlestep_enabled, 0)) {
+ /* Make sure the pc is updated, and raise a debug exception. */
+ if (dc->condjmp) {
+ gen_op_debug();
+ gen_set_label(dc->condlabel);
+ }
+ if (dc->condjmp || !dc->is_jmp) {
+ gen_op_movl_T0_im((long)dc->pc);
+ gen_op_movl_reg_TN[0][15]();
+ dc->condjmp = 0;
+ }
+ gen_op_debug();
+ } else {
+ switch(dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_goto_tb(dc, 1, dc->pc);
+ break;
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* indicate that the hash table must be used to find the next TB */
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ break;
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+ if (dc->condjmp) {
+ gen_set_label(dc->condlabel);
+ gen_goto_tb(dc, 1, dc->pc);
+ dc->condjmp = 0;
+ }
+ }
+ *gen_opc_ptr = INDEX_op_end;
+
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "----------------\n");
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+ target_disas(logfile, pc_start, dc->pc - pc_start, env->thumb);
+ fprintf(logfile, "\n");
+ if (loglevel & (CPU_LOG_TB_OP)) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+ }
+#endif
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ tb->size = 0;
+ } else {
+ tb->size = dc->pc - pc_start;
+ }
+ return 0;
+}
+
+int gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
+
+static const char *cpu_mode_names[16] = {
+ "usr", "fiq", "irq", "svc", "???", "???", "???", "abt",
+ "???", "???", "???", "und", "???", "???", "???", "sys"
+};
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ int i;
+ union {
+ uint32_t i;
+ float s;
+ } s0, s1;
+ CPU_DoubleU d;
+ uint32_t psr;
+
+ for(i=0;i<16;i++) {
+ cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]);
+ if ((i % 4) == 3)
+ cpu_fprintf(f, "\n");
+ else
+ cpu_fprintf(f, " ");
+ }
+ psr = cpsr_read(env);
+ cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d %x\n",
+ psr,
+ psr & (1 << 31) ? 'N' : '-',
+ psr & (1 << 30) ? 'Z' : '-',
+ psr & (1 << 29) ? 'C' : '-',
+ psr & (1 << 28) ? 'V' : '-',
+ psr & CPSR_T ? 'T' : 'A',
+ cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26);
+
+ for (i = 0; i < 16; i++) {
+ d.d = env->vfp.regs[i];
+ s0.i = d.l.lower;
+ s1.i = d.l.upper;
+ cpu_fprintf(f, "s%02d=%08x(%8f) s%02d=%08x(%8f) d%02d=%08x%08x(%8f)\n",
+ i * 2, (int)s0.i, s0.s,
+ i * 2 + 1, (int)s0.i, s0.s,
+ i, (int)(uint32_t)d.l.upper, (int)(uint32_t)d.l.lower,
+ d.d);
+ }
+ cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]);
+}
+
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
new file mode 100644
index 0000000..2f23617
--- /dev/null
+++ b/target-i386/cpu.h
@@ -0,0 +1,653 @@
+/*
+ * i386 virtual CPU header
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_I386_H
+#define CPU_I386_H
+
+#include "config.h"
+
+#ifdef TARGET_X86_64
+#define TARGET_LONG_BITS 64
+#else
+#define TARGET_LONG_BITS 32
+#endif
+
+/* target supports implicit self modifying code */
+#define TARGET_HAS_SMC
+/* support for self modifying code even if the modified instruction is
+ close to the modifying instruction */
+#define TARGET_HAS_PRECISE_SMC
+
+#define TARGET_HAS_ICE 1
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#if defined(__i386__) && !defined(CONFIG_SOFTMMU)
+#define USE_CODE_COPY
+#endif
+
+#define R_EAX 0
+#define R_ECX 1
+#define R_EDX 2
+#define R_EBX 3
+#define R_ESP 4
+#define R_EBP 5
+#define R_ESI 6
+#define R_EDI 7
+
+#define R_AL 0
+#define R_CL 1
+#define R_DL 2
+#define R_BL 3
+#define R_AH 4
+#define R_CH 5
+#define R_DH 6
+#define R_BH 7
+
+#define R_ES 0
+#define R_CS 1
+#define R_SS 2
+#define R_DS 3
+#define R_FS 4
+#define R_GS 5
+
+/* segment descriptor fields */
+#define DESC_G_MASK (1 << 23)
+#define DESC_B_SHIFT 22
+#define DESC_B_MASK (1 << DESC_B_SHIFT)
+#define DESC_L_SHIFT 21 /* x86_64 only : 64 bit code segment */
+#define DESC_L_MASK (1 << DESC_L_SHIFT)
+#define DESC_AVL_MASK (1 << 20)
+#define DESC_P_MASK (1 << 15)
+#define DESC_DPL_SHIFT 13
+#define DESC_S_MASK (1 << 12)
+#define DESC_TYPE_SHIFT 8
+#define DESC_A_MASK (1 << 8)
+
+#define DESC_CS_MASK (1 << 11) /* 1=code segment 0=data segment */
+#define DESC_C_MASK (1 << 10) /* code: conforming */
+#define DESC_R_MASK (1 << 9) /* code: readable */
+
+#define DESC_E_MASK (1 << 10) /* data: expansion direction */
+#define DESC_W_MASK (1 << 9) /* data: writable */
+
+#define DESC_TSS_BUSY_MASK (1 << 9)
+
+/* eflags masks */
+#define CC_C 0x0001
+#define CC_P 0x0004
+#define CC_A 0x0010
+#define CC_Z 0x0040
+#define CC_S 0x0080
+#define CC_O 0x0800
+
+#define TF_SHIFT 8
+#define IOPL_SHIFT 12
+#define VM_SHIFT 17
+
+#define TF_MASK 0x00000100
+#define IF_MASK 0x00000200
+#define DF_MASK 0x00000400
+#define IOPL_MASK 0x00003000
+#define NT_MASK 0x00004000
+#define RF_MASK 0x00010000
+#define VM_MASK 0x00020000
+#define AC_MASK 0x00040000
+#define VIF_MASK 0x00080000
+#define VIP_MASK 0x00100000
+#define ID_MASK 0x00200000
+
+/* hidden flags - used internally by qemu to represent additionnal cpu
+ states. Only the CPL, INHIBIT_IRQ and HALTED are not redundant. We avoid
+ using the IOPL_MASK, TF_MASK and VM_MASK bit position to ease oring
+ with eflags. */
+/* current cpl */
+#define HF_CPL_SHIFT 0
+/* true if soft mmu is being used */
+#define HF_SOFTMMU_SHIFT 2
+/* true if hardware interrupts must be disabled for next instruction */
+#define HF_INHIBIT_IRQ_SHIFT 3
+/* 16 or 32 segments */
+#define HF_CS32_SHIFT 4
+#define HF_SS32_SHIFT 5
+/* zero base for DS, ES and SS : can be '0' only in 32 bit CS segment */
+#define HF_ADDSEG_SHIFT 6
+/* copy of CR0.PE (protected mode) */
+#define HF_PE_SHIFT 7
+#define HF_TF_SHIFT 8 /* must be same as eflags */
+#define HF_MP_SHIFT 9 /* the order must be MP, EM, TS */
+#define HF_EM_SHIFT 10
+#define HF_TS_SHIFT 11
+#define HF_IOPL_SHIFT 12 /* must be same as eflags */
+#define HF_LMA_SHIFT 14 /* only used on x86_64: long mode active */
+#define HF_CS64_SHIFT 15 /* only used on x86_64: 64 bit code segment */
+#define HF_OSFXSR_SHIFT 16 /* CR4.OSFXSR */
+#define HF_VM_SHIFT 17 /* must be same as eflags */
+#define HF_HALTED_SHIFT 18 /* CPU halted */
+
+#define HF_CPL_MASK (3 << HF_CPL_SHIFT)
+#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
+#define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT)
+#define HF_CS32_MASK (1 << HF_CS32_SHIFT)
+#define HF_SS32_MASK (1 << HF_SS32_SHIFT)
+#define HF_ADDSEG_MASK (1 << HF_ADDSEG_SHIFT)
+#define HF_PE_MASK (1 << HF_PE_SHIFT)
+#define HF_TF_MASK (1 << HF_TF_SHIFT)
+#define HF_MP_MASK (1 << HF_MP_SHIFT)
+#define HF_EM_MASK (1 << HF_EM_SHIFT)
+#define HF_TS_MASK (1 << HF_TS_SHIFT)
+#define HF_LMA_MASK (1 << HF_LMA_SHIFT)
+#define HF_CS64_MASK (1 << HF_CS64_SHIFT)
+#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
+#define HF_HALTED_MASK (1 << HF_HALTED_SHIFT)
+
+#define CR0_PE_MASK (1 << 0)
+#define CR0_MP_MASK (1 << 1)
+#define CR0_EM_MASK (1 << 2)
+#define CR0_TS_MASK (1 << 3)
+#define CR0_ET_MASK (1 << 4)
+#define CR0_NE_MASK (1 << 5)
+#define CR0_WP_MASK (1 << 16)
+#define CR0_AM_MASK (1 << 18)
+#define CR0_PG_MASK (1 << 31)
+
+#define CR4_VME_MASK (1 << 0)
+#define CR4_PVI_MASK (1 << 1)
+#define CR4_TSD_MASK (1 << 2)
+#define CR4_DE_MASK (1 << 3)
+#define CR4_PSE_MASK (1 << 4)
+#define CR4_PAE_MASK (1 << 5)
+#define CR4_PGE_MASK (1 << 7)
+#define CR4_PCE_MASK (1 << 8)
+#define CR4_OSFXSR_MASK (1 << 9)
+#define CR4_OSXMMEXCPT_MASK (1 << 10)
+
+#define PG_PRESENT_BIT 0
+#define PG_RW_BIT 1
+#define PG_USER_BIT 2
+#define PG_PWT_BIT 3
+#define PG_PCD_BIT 4
+#define PG_ACCESSED_BIT 5
+#define PG_DIRTY_BIT 6
+#define PG_PSE_BIT 7
+#define PG_GLOBAL_BIT 8
+#define PG_NX_BIT 63
+
+#define PG_PRESENT_MASK (1 << PG_PRESENT_BIT)
+#define PG_RW_MASK (1 << PG_RW_BIT)
+#define PG_USER_MASK (1 << PG_USER_BIT)
+#define PG_PWT_MASK (1 << PG_PWT_BIT)
+#define PG_PCD_MASK (1 << PG_PCD_BIT)
+#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
+#define PG_DIRTY_MASK (1 << PG_DIRTY_BIT)
+#define PG_PSE_MASK (1 << PG_PSE_BIT)
+#define PG_GLOBAL_MASK (1 << PG_GLOBAL_BIT)
+#define PG_NX_MASK (1LL << PG_NX_BIT)
+
+#define PG_ERROR_W_BIT 1
+
+#define PG_ERROR_P_MASK 0x01
+#define PG_ERROR_W_MASK (1 << PG_ERROR_W_BIT)
+#define PG_ERROR_U_MASK 0x04
+#define PG_ERROR_RSVD_MASK 0x08
+#define PG_ERROR_I_D_MASK 0x10
+
+#define MSR_IA32_APICBASE 0x1b
+#define MSR_IA32_APICBASE_BSP (1<<8)
+#define MSR_IA32_APICBASE_ENABLE (1<<11)
+#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+
+#define MSR_IA32_SYSENTER_CS 0x174
+#define MSR_IA32_SYSENTER_ESP 0x175
+#define MSR_IA32_SYSENTER_EIP 0x176
+
+#define MSR_MCG_CAP 0x179
+#define MSR_MCG_STATUS 0x17a
+#define MSR_MCG_CTL 0x17b
+
+#define MSR_PAT 0x277
+
+#define MSR_EFER 0xc0000080
+
+#define MSR_EFER_SCE (1 << 0)
+#define MSR_EFER_LME (1 << 8)
+#define MSR_EFER_LMA (1 << 10)
+#define MSR_EFER_NXE (1 << 11)
+#define MSR_EFER_FFXSR (1 << 14)
+
+#define MSR_STAR 0xc0000081
+#define MSR_LSTAR 0xc0000082
+#define MSR_CSTAR 0xc0000083
+#define MSR_FMASK 0xc0000084
+#define MSR_FSBASE 0xc0000100
+#define MSR_GSBASE 0xc0000101
+#define MSR_KERNELGSBASE 0xc0000102
+
+/* cpuid_features bits */
+#define CPUID_FP87 (1 << 0)
+#define CPUID_VME (1 << 1)
+#define CPUID_DE (1 << 2)
+#define CPUID_PSE (1 << 3)
+#define CPUID_TSC (1 << 4)
+#define CPUID_MSR (1 << 5)
+#define CPUID_PAE (1 << 6)
+#define CPUID_MCE (1 << 7)
+#define CPUID_CX8 (1 << 8)
+#define CPUID_APIC (1 << 9)
+#define CPUID_SEP (1 << 11) /* sysenter/sysexit */
+#define CPUID_MTRR (1 << 12)
+#define CPUID_PGE (1 << 13)
+#define CPUID_MCA (1 << 14)
+#define CPUID_CMOV (1 << 15)
+#define CPUID_PAT (1 << 16)
+#define CPUID_CLFLUSH (1 << 19)
+/* ... */
+#define CPUID_MMX (1 << 23)
+#define CPUID_FXSR (1 << 24)
+#define CPUID_SSE (1 << 25)
+#define CPUID_SSE2 (1 << 26)
+
+#define CPUID_EXT_SSE3 (1 << 0)
+#define CPUID_EXT_MONITOR (1 << 3)
+#define CPUID_EXT_CX16 (1 << 13)
+
+#define CPUID_EXT2_SYSCALL (1 << 11)
+#define CPUID_EXT2_NX (1 << 20)
+#define CPUID_EXT2_FFXSR (1 << 25)
+#define CPUID_EXT2_LM (1 << 29)
+
+#define EXCP00_DIVZ 0
+#define EXCP01_SSTP 1
+#define EXCP02_NMI 2
+#define EXCP03_INT3 3
+#define EXCP04_INTO 4
+#define EXCP05_BOUND 5
+#define EXCP06_ILLOP 6
+#define EXCP07_PREX 7
+#define EXCP08_DBLE 8
+#define EXCP09_XERR 9
+#define EXCP0A_TSS 10
+#define EXCP0B_NOSEG 11
+#define EXCP0C_STACK 12
+#define EXCP0D_GPF 13
+#define EXCP0E_PAGE 14
+#define EXCP10_COPR 16
+#define EXCP11_ALGN 17
+#define EXCP12_MCHK 18
+
+enum {
+ CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
+ CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */
+
+ CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */
+ CC_OP_MULW,
+ CC_OP_MULL,
+ CC_OP_MULQ,
+
+ CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_ADDW,
+ CC_OP_ADDL,
+ CC_OP_ADDQ,
+
+ CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_ADCW,
+ CC_OP_ADCL,
+ CC_OP_ADCQ,
+
+ CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SUBW,
+ CC_OP_SUBL,
+ CC_OP_SUBQ,
+
+ CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SBBW,
+ CC_OP_SBBL,
+ CC_OP_SBBQ,
+
+ CC_OP_LOGICB, /* modify all flags, CC_DST = res */
+ CC_OP_LOGICW,
+ CC_OP_LOGICL,
+ CC_OP_LOGICQ,
+
+ CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */
+ CC_OP_INCW,
+ CC_OP_INCL,
+ CC_OP_INCQ,
+
+ CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C */
+ CC_OP_DECW,
+ CC_OP_DECL,
+ CC_OP_DECQ,
+
+ CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.msb = C */
+ CC_OP_SHLW,
+ CC_OP_SHLL,
+ CC_OP_SHLQ,
+
+ CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
+ CC_OP_SARW,
+ CC_OP_SARL,
+ CC_OP_SARQ,
+
+ CC_OP_NB,
+};
+
+#ifdef FLOATX80
+#define USE_X86LDOUBLE
+#endif
+
+#ifdef USE_X86LDOUBLE
+typedef floatx80 CPU86_LDouble;
+#else
+typedef float64 CPU86_LDouble;
+#endif
+
+typedef struct SegmentCache {
+ uint32_t selector;
+ target_ulong base;
+ uint32_t limit;
+ uint32_t flags;
+} SegmentCache;
+
+typedef union {
+ uint8_t _b[16];
+ uint16_t _w[8];
+ uint32_t _l[4];
+ uint64_t _q[2];
+ float32 _s[4];
+ float64 _d[2];
+} XMMReg;
+
+typedef union {
+ uint8_t _b[8];
+ uint16_t _w[2];
+ uint32_t _l[1];
+ uint64_t q;
+} MMXReg;
+
+#ifdef WORDS_BIGENDIAN
+#define XMM_B(n) _b[15 - (n)]
+#define XMM_W(n) _w[7 - (n)]
+#define XMM_L(n) _l[3 - (n)]
+#define XMM_S(n) _s[3 - (n)]
+#define XMM_Q(n) _q[1 - (n)]
+#define XMM_D(n) _d[1 - (n)]
+
+#define MMX_B(n) _b[7 - (n)]
+#define MMX_W(n) _w[3 - (n)]
+#define MMX_L(n) _l[1 - (n)]
+#else
+#define XMM_B(n) _b[n]
+#define XMM_W(n) _w[n]
+#define XMM_L(n) _l[n]
+#define XMM_S(n) _s[n]
+#define XMM_Q(n) _q[n]
+#define XMM_D(n) _d[n]
+
+#define MMX_B(n) _b[n]
+#define MMX_W(n) _w[n]
+#define MMX_L(n) _l[n]
+#endif
+#define MMX_Q(n) q
+
+#ifdef TARGET_X86_64
+#define CPU_NB_REGS 16
+#else
+#define CPU_NB_REGS 8
+#endif
+
+typedef struct CPUX86State {
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+ /* temporaries if we cannot store them in host registers */
+ target_ulong t0, t1, t2;
+#endif
+
+ /* standard registers */
+ target_ulong regs[CPU_NB_REGS];
+ target_ulong eip;
+ target_ulong eflags; /* eflags register. During CPU emulation, CC
+ flags and DF are set to zero because they are
+ stored elsewhere */
+
+ /* emulator internal eflags handling */
+ target_ulong cc_src;
+ target_ulong cc_dst;
+ uint32_t cc_op;
+ int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */
+ uint32_t hflags; /* hidden flags, see HF_xxx constants */
+
+ /* segments */
+ SegmentCache segs[6]; /* selector values */
+ SegmentCache ldt;
+ SegmentCache tr;
+ SegmentCache gdt; /* only base and limit are used */
+ SegmentCache idt; /* only base and limit are used */
+
+ target_ulong cr[5]; /* NOTE: cr1 is unused */
+ uint32_t a20_mask;
+
+ /* FPU state */
+ unsigned int fpstt; /* top of stack index */
+ unsigned int fpus;
+ unsigned int fpuc;
+ uint8_t fptags[8]; /* 0 = valid, 1 = empty */
+ union {
+#ifdef USE_X86LDOUBLE
+ CPU86_LDouble d __attribute__((aligned(16)));
+#else
+ CPU86_LDouble d;
+#endif
+ MMXReg mmx;
+ } fpregs[8];
+
+ /* emulator internal variables */
+ float_status fp_status;
+ CPU86_LDouble ft0;
+ union {
+ float f;
+ double d;
+ int i32;
+ int64_t i64;
+ } fp_convert;
+
+ float_status sse_status;
+ uint32_t mxcsr;
+ XMMReg xmm_regs[CPU_NB_REGS];
+ XMMReg xmm_t0;
+ MMXReg mmx_t0;
+
+ /* sysenter registers */
+ uint32_t sysenter_cs;
+ uint32_t sysenter_esp;
+ uint32_t sysenter_eip;
+ uint64_t efer;
+ uint64_t star;
+#ifdef TARGET_X86_64
+ target_ulong lstar;
+ target_ulong cstar;
+ target_ulong fmask;
+ target_ulong kernelgsbase;
+#endif
+
+ uint64_t pat;
+
+ /* temporary data for USE_CODE_COPY mode */
+#ifdef USE_CODE_COPY
+ uint32_t tmp0;
+ uint32_t saved_esp;
+ int native_fp_regs; /* if true, the FPU state is in the native CPU regs */
+#endif
+
+ /* exception/interrupt handling */
+ jmp_buf jmp_env;
+ int exception_index;
+ int error_code;
+ int exception_is_int;
+ target_ulong exception_next_eip;
+ target_ulong dr[8]; /* debug registers */
+ int interrupt_request;
+ int user_mode_only; /* user mode only simulation */
+
+ CPU_COMMON
+
+ /* processor features (e.g. for CPUID insn) */
+ uint32_t cpuid_level;
+ uint32_t cpuid_vendor1;
+ uint32_t cpuid_vendor2;
+ uint32_t cpuid_vendor3;
+ uint32_t cpuid_version;
+ uint32_t cpuid_features;
+ uint32_t cpuid_ext_features;
+ uint32_t cpuid_xlevel;
+ uint32_t cpuid_model[12];
+ uint32_t cpuid_ext2_features;
+
+#ifdef USE_KQEMU
+ int kqemu_enabled;
+ int last_io_time;
+#endif
+ /* in order to simplify APIC support, we leave this pointer to the
+ user */
+ struct APICState *apic_state;
+} CPUX86State;
+
+CPUX86State *cpu_x86_init(void);
+int cpu_x86_exec(CPUX86State *s);
+void cpu_x86_close(CPUX86State *s);
+int cpu_get_pic_interrupt(CPUX86State *s);
+/* MSDOS compatibility mode FPU exception support */
+void cpu_set_ferr(CPUX86State *s);
+
+/* this function must always be used to load data in the segment
+ cache: it synchronizes the hflags with the segment cache values */
+static inline void cpu_x86_load_seg_cache(CPUX86State *env,
+ int seg_reg, unsigned int selector,
+ uint32_t base, unsigned int limit,
+ unsigned int flags)
+{
+ SegmentCache *sc;
+ unsigned int new_hflags;
+
+ sc = &env->segs[seg_reg];
+ sc->selector = selector;
+ sc->base = base;
+ sc->limit = limit;
+ sc->flags = flags;
+
+ /* update the hidden flags */
+ {
+ if (seg_reg == R_CS) {
+#ifdef TARGET_X86_64
+ if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) {
+ /* long mode */
+ env->hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ env->hflags &= ~(HF_ADDSEG_MASK);
+ } else
+#endif
+ {
+ /* legacy / compatibility case */
+ new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_CS32_SHIFT);
+ env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) |
+ new_hflags;
+ }
+ }
+ new_hflags = (env->segs[R_SS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (env->hflags & HF_CS64_MASK) {
+ /* zero base assumed for DS, ES and SS in long mode */
+ } else if (!(env->cr[0] & CR0_PE_MASK) ||
+ (env->eflags & VM_MASK) ||
+ !(env->hflags & HF_CS32_MASK)) {
+ /* XXX: try to avoid this test. The problem comes from the
+ fact that is real mode or vm86 mode we only modify the
+ 'base' and 'selector' fields of the segment cache to go
+ faster. A solution may be to force addseg to one in
+ translate-i386.c. */
+ new_hflags |= HF_ADDSEG_MASK;
+ } else {
+ new_hflags |= ((env->segs[R_DS].base |
+ env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) <<
+ HF_ADDSEG_SHIFT;
+ }
+ env->hflags = (env->hflags &
+ ~(HF_SS32_MASK | HF_ADDSEG_MASK)) | new_hflags;
+ }
+}
+
+/* wrapper, just in case memory mappings must be changed */
+static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl)
+{
+#if HF_CPL_MASK == 3
+ s->hflags = (s->hflags & ~HF_CPL_MASK) | cpl;
+#else
+#error HF_CPL_MASK is hardcoded
+#endif
+}
+
+/* used for debug or cpu save/restore */
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f);
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper);
+
+/* the following helpers are only usable in user mode simulation as
+ they can trigger unexpected exceptions */
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
+void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32);
+void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+struct siginfo;
+int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
+ void *puc);
+void cpu_x86_set_a20(CPUX86State *env, int a20_state);
+
+uint64_t cpu_get_tsc(CPUX86State *env);
+
+void cpu_set_apic_base(CPUX86State *env, uint64_t val);
+uint64_t cpu_get_apic_base(CPUX86State *env);
+void cpu_set_apic_tpr(CPUX86State *env, uint8_t val);
+#ifndef NO_CPU_IO_DEFS
+uint8_t cpu_get_apic_tpr(CPUX86State *env);
+#endif
+
+/* will be suppressed */
+void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0);
+
+/* used to debug */
+#define X86_DUMP_FPU 0x0001 /* dump FPU state too */
+#define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */
+
+#ifdef USE_KQEMU
+static inline int cpu_get_time_fast(void)
+{
+ int low, high;
+ asm volatile("rdtsc" : "=a" (low), "=d" (high));
+ return low;
+}
+#endif
+
+#define TARGET_PAGE_BITS 12
+#include "cpu-all.h"
+
+#endif /* CPU_I386_H */
diff --git a/target-i386/exec.h b/target-i386/exec.h
new file mode 100644
index 0000000..609a586
--- /dev/null
+++ b/target-i386/exec.h
@@ -0,0 +1,575 @@
+/*
+ * i386 execution defines
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+#include "dyngen-exec.h"
+
+/* XXX: factorize this mess */
+#ifdef TARGET_X86_64
+#define TARGET_LONG_BITS 64
+#else
+#define TARGET_LONG_BITS 32
+#endif
+
+#include "cpu-defs.h"
+
+/* at least 4 register variables are defined */
+register struct CPUX86State *env asm(AREG0);
+
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+
+/* no registers can be used */
+#define T0 (env->t0)
+#define T1 (env->t1)
+#define T2 (env->t2)
+
+#else
+
+/* XXX: use unsigned long instead of target_ulong - better code will
+ be generated for 64 bit CPUs */
+register target_ulong T0 asm(AREG1);
+register target_ulong T1 asm(AREG2);
+register target_ulong T2 asm(AREG3);
+
+/* if more registers are available, we define some registers too */
+#ifdef AREG4
+register target_ulong EAX asm(AREG4);
+#define reg_EAX
+#endif
+
+#ifdef AREG5
+register target_ulong ESP asm(AREG5);
+#define reg_ESP
+#endif
+
+#ifdef AREG6
+register target_ulong EBP asm(AREG6);
+#define reg_EBP
+#endif
+
+#ifdef AREG7
+register target_ulong ECX asm(AREG7);
+#define reg_ECX
+#endif
+
+#ifdef AREG8
+register target_ulong EDX asm(AREG8);
+#define reg_EDX
+#endif
+
+#ifdef AREG9
+register target_ulong EBX asm(AREG9);
+#define reg_EBX
+#endif
+
+#ifdef AREG10
+register target_ulong ESI asm(AREG10);
+#define reg_ESI
+#endif
+
+#ifdef AREG11
+register target_ulong EDI asm(AREG11);
+#define reg_EDI
+#endif
+
+#endif /* ! (TARGET_LONG_BITS > HOST_LONG_BITS) */
+
+#define A0 T2
+
+extern FILE *logfile;
+extern int loglevel;
+
+#ifndef reg_EAX
+#define EAX (env->regs[R_EAX])
+#endif
+#ifndef reg_ECX
+#define ECX (env->regs[R_ECX])
+#endif
+#ifndef reg_EDX
+#define EDX (env->regs[R_EDX])
+#endif
+#ifndef reg_EBX
+#define EBX (env->regs[R_EBX])
+#endif
+#ifndef reg_ESP
+#define ESP (env->regs[R_ESP])
+#endif
+#ifndef reg_EBP
+#define EBP (env->regs[R_EBP])
+#endif
+#ifndef reg_ESI
+#define ESI (env->regs[R_ESI])
+#endif
+#ifndef reg_EDI
+#define EDI (env->regs[R_EDI])
+#endif
+#define EIP (env->eip)
+#define DF (env->df)
+
+#define CC_SRC (env->cc_src)
+#define CC_DST (env->cc_dst)
+#define CC_OP (env->cc_op)
+
+/* float macros */
+#define FT0 (env->ft0)
+#define ST0 (env->fpregs[env->fpstt].d)
+#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d)
+#define ST1 ST(1)
+
+#ifdef USE_FP_CONVERT
+#define FP_CONVERT (env->fp_convert)
+#endif
+
+#include "cpu.h"
+#include "exec-all.h"
+
+typedef struct CCTable {
+ int (*compute_all)(void); /* return all the flags */
+ int (*compute_c)(void); /* return the C flag */
+} CCTable;
+
+extern CCTable cc_table[];
+
+void load_seg(int seg_reg, int selector);
+void helper_ljmp_protected_T0_T1(int next_eip);
+void helper_lcall_real_T0_T1(int shift, int next_eip);
+void helper_lcall_protected_T0_T1(int shift, int next_eip);
+void helper_iret_real(int shift);
+void helper_iret_protected(int shift, int next_eip);
+void helper_lret_protected(int shift, int addend);
+void helper_lldt_T0(void);
+void helper_ltr_T0(void);
+void helper_movl_crN_T0(int reg);
+void helper_movl_drN_T0(int reg);
+void helper_invlpg(target_ulong addr);
+void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0);
+void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3);
+void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4);
+void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr);
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write, int is_user, int is_softmmu);
+void tlb_fill(target_ulong addr, int is_write, int is_user,
+ void *retaddr);
+void __hidden cpu_lock(void);
+void __hidden cpu_unlock(void);
+void do_interrupt(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw);
+void do_interrupt_user(int intno, int is_int, int error_code,
+ target_ulong next_eip);
+void raise_interrupt(int intno, int is_int, int error_code,
+ int next_eip_addend);
+void raise_exception_err(int exception_index, int error_code);
+void raise_exception(int exception_index);
+void __hidden cpu_loop_exit(void);
+
+void OPPROTO op_movl_eflags_T0(void);
+void OPPROTO op_movl_T0_eflags(void);
+void helper_divl_EAX_T0(void);
+void helper_idivl_EAX_T0(void);
+void helper_mulq_EAX_T0(void);
+void helper_imulq_EAX_T0(void);
+void helper_imulq_T0_T1(void);
+void helper_divq_EAX_T0(void);
+void helper_idivq_EAX_T0(void);
+void helper_bswapq_T0(void);
+void helper_cmpxchg8b(void);
+void helper_cpuid(void);
+void helper_enter_level(int level, int data32);
+void helper_enter64_level(int level, int data64);
+void helper_sysenter(void);
+void helper_sysexit(void);
+void helper_syscall(int next_eip_addend);
+void helper_sysret(int dflag);
+void helper_rdtsc(void);
+void helper_rdmsr(void);
+void helper_wrmsr(void);
+void helper_lsl(void);
+void helper_lar(void);
+void helper_verr(void);
+void helper_verw(void);
+
+void check_iob_T0(void);
+void check_iow_T0(void);
+void check_iol_T0(void);
+void check_iob_DX(void);
+void check_iow_DX(void);
+void check_iol_DX(void);
+
+#if !defined(CONFIG_USER_ONLY)
+
+#include "softmmu_exec.h"
+
+static inline double ldfq(target_ulong ptr)
+{
+ union {
+ double d;
+ uint64_t i;
+ } u;
+ u.i = ldq(ptr);
+ return u.d;
+}
+
+static inline void stfq(target_ulong ptr, double v)
+{
+ union {
+ double d;
+ uint64_t i;
+ } u;
+ u.d = v;
+ stq(ptr, u.i);
+}
+
+static inline float ldfl(target_ulong ptr)
+{
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ u.i = ldl(ptr);
+ return u.f;
+}
+
+static inline void stfl(target_ulong ptr, float v)
+{
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl(ptr, u.i);
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+#ifdef USE_X86LDOUBLE
+/* use long double functions */
+#define floatx_to_int32 floatx80_to_int32
+#define floatx_to_int64 floatx80_to_int64
+#define floatx_to_int32_round_to_zero floatx80_to_int32_round_to_zero
+#define floatx_to_int64_round_to_zero floatx80_to_int64_round_to_zero
+#define floatx_abs floatx80_abs
+#define floatx_chs floatx80_chs
+#define floatx_round_to_int floatx80_round_to_int
+#define floatx_compare floatx80_compare
+#define floatx_compare_quiet floatx80_compare_quiet
+#define sin sinl
+#define cos cosl
+#define sqrt sqrtl
+#define pow powl
+#define log logl
+#define tan tanl
+#define atan2 atan2l
+#define floor floorl
+#define ceil ceill
+#define ldexp ldexpl
+#else
+#define floatx_to_int32 float64_to_int32
+#define floatx_to_int64 float64_to_int64
+#define floatx_to_int32_round_to_zero float64_to_int32_round_to_zero
+#define floatx_to_int64_round_to_zero float64_to_int64_round_to_zero
+#define floatx_abs float64_abs
+#define floatx_chs float64_chs
+#define floatx_round_to_int float64_round_to_int
+#define floatx_compare float64_compare
+#define floatx_compare_quiet float64_compare_quiet
+#endif
+
+extern CPU86_LDouble sin(CPU86_LDouble x);
+extern CPU86_LDouble cos(CPU86_LDouble x);
+extern CPU86_LDouble sqrt(CPU86_LDouble x);
+extern CPU86_LDouble pow(CPU86_LDouble, CPU86_LDouble);
+extern CPU86_LDouble log(CPU86_LDouble x);
+extern CPU86_LDouble tan(CPU86_LDouble x);
+extern CPU86_LDouble atan2(CPU86_LDouble, CPU86_LDouble);
+extern CPU86_LDouble floor(CPU86_LDouble x);
+extern CPU86_LDouble ceil(CPU86_LDouble x);
+
+#define RC_MASK 0xc00
+#define RC_NEAR 0x000
+#define RC_DOWN 0x400
+#define RC_UP 0x800
+#define RC_CHOP 0xc00
+
+#define MAXTAN 9223372036854775808.0
+
+#ifdef USE_X86LDOUBLE
+
+/* only for x86 */
+typedef union {
+ long double d;
+ struct {
+ unsigned long long lower;
+ unsigned short upper;
+ } l;
+} CPU86_LDoubleU;
+
+/* the following deal with x86 long double-precision numbers */
+#define MAXEXPD 0x7fff
+#define EXPBIAS 16383
+#define EXPD(fp) (fp.l.upper & 0x7fff)
+#define SIGND(fp) ((fp.l.upper) & 0x8000)
+#define MANTD(fp) (fp.l.lower)
+#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
+
+#else
+
+/* NOTE: arm is horrible as double 32 bit words are stored in big endian ! */
+typedef union {
+ double d;
+#if !defined(WORDS_BIGENDIAN) && !defined(__arm__)
+ struct {
+ uint32_t lower;
+ int32_t upper;
+ } l;
+#else
+ struct {
+ int32_t upper;
+ uint32_t lower;
+ } l;
+#endif
+#ifndef __arm__
+ int64_t ll;
+#endif
+} CPU86_LDoubleU;
+
+/* the following deal with IEEE double-precision numbers */
+#define MAXEXPD 0x7ff
+#define EXPBIAS 1023
+#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF)
+#define SIGND(fp) ((fp.l.upper) & 0x80000000)
+#ifdef __arm__
+#define MANTD(fp) (fp.l.lower | ((uint64_t)(fp.l.upper & ((1 << 20) - 1)) << 32))
+#else
+#define MANTD(fp) (fp.ll & ((1LL << 52) - 1))
+#endif
+#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7ff << 20)) | (EXPBIAS << 20)
+#endif
+
+static inline void fpush(void)
+{
+ env->fpstt = (env->fpstt - 1) & 7;
+ env->fptags[env->fpstt] = 0; /* validate stack entry */
+}
+
+static inline void fpop(void)
+{
+ env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
+ env->fpstt = (env->fpstt + 1) & 7;
+}
+
+#ifndef USE_X86LDOUBLE
+static inline CPU86_LDouble helper_fldt(target_ulong ptr)
+{
+ CPU86_LDoubleU temp;
+ int upper, e;
+ uint64_t ll;
+
+ /* mantissa */
+ upper = lduw(ptr + 8);
+ /* XXX: handle overflow ? */
+ e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
+ e |= (upper >> 4) & 0x800; /* sign */
+ ll = (ldq(ptr) >> 11) & ((1LL << 52) - 1);
+#ifdef __arm__
+ temp.l.upper = (e << 20) | (ll >> 32);
+ temp.l.lower = ll;
+#else
+ temp.ll = ll | ((uint64_t)e << 52);
+#endif
+ return temp.d;
+}
+
+static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr)
+{
+ CPU86_LDoubleU temp;
+ int e;
+
+ temp.d = f;
+ /* mantissa */
+ stq(ptr, (MANTD(temp) << 11) | (1LL << 63));
+ /* exponent + sign */
+ e = EXPD(temp) - EXPBIAS + 16383;
+ e |= SIGND(temp) >> 16;
+ stw(ptr + 8, e);
+}
+#else
+
+/* XXX: same endianness assumed */
+
+#ifdef CONFIG_USER_ONLY
+
+static inline CPU86_LDouble helper_fldt(target_ulong ptr)
+{
+ return *(CPU86_LDouble *)ptr;
+}
+
+static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr)
+{
+ *(CPU86_LDouble *)ptr = f;
+}
+
+#else
+
+/* we use memory access macros */
+
+static inline CPU86_LDouble helper_fldt(target_ulong ptr)
+{
+ CPU86_LDoubleU temp;
+
+ temp.l.lower = ldq(ptr);
+ temp.l.upper = lduw(ptr + 8);
+ return temp.d;
+}
+
+static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr)
+{
+ CPU86_LDoubleU temp;
+
+ temp.d = f;
+ stq(ptr, temp.l.lower);
+ stw(ptr + 8, temp.l.upper);
+}
+
+#endif /* !CONFIG_USER_ONLY */
+
+#endif /* USE_X86LDOUBLE */
+
+#define FPUS_IE (1 << 0)
+#define FPUS_DE (1 << 1)
+#define FPUS_ZE (1 << 2)
+#define FPUS_OE (1 << 3)
+#define FPUS_UE (1 << 4)
+#define FPUS_PE (1 << 5)
+#define FPUS_SF (1 << 6)
+#define FPUS_SE (1 << 7)
+#define FPUS_B (1 << 15)
+
+#define FPUC_EM 0x3f
+
+extern const CPU86_LDouble f15rk[7];
+
+void helper_fldt_ST0_A0(void);
+void helper_fstt_ST0_A0(void);
+void fpu_raise_exception(void);
+CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b);
+void helper_fbld_ST0_A0(void);
+void helper_fbst_ST0_A0(void);
+void helper_f2xm1(void);
+void helper_fyl2x(void);
+void helper_fptan(void);
+void helper_fpatan(void);
+void helper_fxtract(void);
+void helper_fprem1(void);
+void helper_fprem(void);
+void helper_fyl2xp1(void);
+void helper_fsqrt(void);
+void helper_fsincos(void);
+void helper_frndint(void);
+void helper_fscale(void);
+void helper_fsin(void);
+void helper_fcos(void);
+void helper_fxam_ST0(void);
+void helper_fstenv(target_ulong ptr, int data32);
+void helper_fldenv(target_ulong ptr, int data32);
+void helper_fsave(target_ulong ptr, int data32);
+void helper_frstor(target_ulong ptr, int data32);
+void helper_fxsave(target_ulong ptr, int data64);
+void helper_fxrstor(target_ulong ptr, int data64);
+void restore_native_fp_state(CPUState *env);
+void save_native_fp_state(CPUState *env);
+float approx_rsqrt(float a);
+float approx_rcp(float a);
+void update_fp_status(void);
+void helper_hlt(void);
+void helper_monitor(void);
+void helper_mwait(void);
+
+extern const uint8_t parity_table[256];
+extern const uint8_t rclw_table[32];
+extern const uint8_t rclb_table[32];
+
+static inline uint32_t compute_eflags(void)
+{
+ return env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+}
+
+/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
+static inline void load_eflags(int eflags, int update_mask)
+{
+ CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((eflags >> 10) & 1));
+ env->eflags = (env->eflags & ~update_mask) |
+ (eflags & update_mask);
+}
+
+static inline void env_to_regs(void)
+{
+#ifdef reg_EAX
+ EAX = env->regs[R_EAX];
+#endif
+#ifdef reg_ECX
+ ECX = env->regs[R_ECX];
+#endif
+#ifdef reg_EDX
+ EDX = env->regs[R_EDX];
+#endif
+#ifdef reg_EBX
+ EBX = env->regs[R_EBX];
+#endif
+#ifdef reg_ESP
+ ESP = env->regs[R_ESP];
+#endif
+#ifdef reg_EBP
+ EBP = env->regs[R_EBP];
+#endif
+#ifdef reg_ESI
+ ESI = env->regs[R_ESI];
+#endif
+#ifdef reg_EDI
+ EDI = env->regs[R_EDI];
+#endif
+}
+
+static inline void regs_to_env(void)
+{
+#ifdef reg_EAX
+ env->regs[R_EAX] = EAX;
+#endif
+#ifdef reg_ECX
+ env->regs[R_ECX] = ECX;
+#endif
+#ifdef reg_EDX
+ env->regs[R_EDX] = EDX;
+#endif
+#ifdef reg_EBX
+ env->regs[R_EBX] = EBX;
+#endif
+#ifdef reg_ESP
+ env->regs[R_ESP] = ESP;
+#endif
+#ifdef reg_EBP
+ env->regs[R_EBP] = EBP;
+#endif
+#ifdef reg_ESI
+ env->regs[R_ESI] = ESI;
+#endif
+#ifdef reg_EDI
+ env->regs[R_EDI] = EDI;
+#endif
+}
diff --git a/target-i386/helper.c b/target-i386/helper.c
new file mode 100644
index 0000000..70e9fae
--- /dev/null
+++ b/target-i386/helper.c
@@ -0,0 +1,3540 @@
+/*
+ * i386 helpers
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+//#define DEBUG_PCALL
+
+#if 0
+#define raise_exception_err(a, b)\
+do {\
+ if (logfile)\
+ fprintf(logfile, "raise_exception line=%d\n", __LINE__);\
+ (raise_exception_err)(a, b);\
+} while (0)
+#endif
+
+const uint8_t parity_table[256] = {
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+};
+
+/* modulo 17 table */
+const uint8_t rclw_table[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 16, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9,10,11,12,13,14,
+};
+
+/* modulo 9 table */
+const uint8_t rclb_table[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 0, 1, 2, 3, 4,
+};
+
+const CPU86_LDouble f15rk[7] =
+{
+ 0.00000000000000000000L,
+ 1.00000000000000000000L,
+ 3.14159265358979323851L, /*pi*/
+ 0.30102999566398119523L, /*lg2*/
+ 0.69314718055994530943L, /*ln2*/
+ 1.44269504088896340739L, /*l2e*/
+ 3.32192809488736234781L, /*l2t*/
+};
+
+/* thread support */
+
+spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+
+void cpu_lock(void)
+{
+ spin_lock(&global_cpu_lock);
+}
+
+void cpu_unlock(void)
+{
+ spin_unlock(&global_cpu_lock);
+}
+
+void cpu_loop_exit(void)
+{
+ /* NOTE: the register at this point must be saved by hand because
+ longjmp restore them */
+ regs_to_env();
+ longjmp(env->jmp_env, 1);
+}
+
+/* return non zero if error */
+static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
+ int selector)
+{
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ if (selector & 0x4)
+ dt = &env->ldt;
+ else
+ dt = &env->gdt;
+ index = selector & ~7;
+ if ((index + 7) > dt->limit)
+ return -1;
+ ptr = dt->base + index;
+ *e1_ptr = ldl_kernel(ptr);
+ *e2_ptr = ldl_kernel(ptr + 4);
+ return 0;
+}
+
+static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
+{
+ unsigned int limit;
+ limit = (e1 & 0xffff) | (e2 & 0x000f0000);
+ if (e2 & DESC_G_MASK)
+ limit = (limit << 12) | 0xfff;
+ return limit;
+}
+
+static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
+{
+ return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
+}
+
+static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
+{
+ sc->base = get_seg_base(e1, e2);
+ sc->limit = get_seg_limit(e1, e2);
+ sc->flags = e2;
+}
+
+/* init the segment cache in vm86 mode. */
+static inline void load_seg_vm(int seg, int selector)
+{
+ selector &= 0xffff;
+ cpu_x86_load_seg_cache(env, seg, selector,
+ (selector << 4), 0xffff, 0);
+}
+
+static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
+ uint32_t *esp_ptr, int dpl)
+{
+ int type, index, shift;
+
+#if 0
+ {
+ int i;
+ printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
+ for(i=0;i<env->tr.limit;i++) {
+ printf("%02x ", env->tr.base[i]);
+ if ((i & 7) == 7) printf("\n");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (!(env->tr.flags & DESC_P_MASK))
+ cpu_abort(env, "invalid tss");
+ type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ if ((type & 7) != 1)
+ cpu_abort(env, "invalid tss type");
+ shift = type >> 3;
+ index = (dpl * 4 + 2) << shift;
+ if (index + (4 << shift) - 1 > env->tr.limit)
+ raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
+ if (shift == 0) {
+ *esp_ptr = lduw_kernel(env->tr.base + index);
+ *ss_ptr = lduw_kernel(env->tr.base + index + 2);
+ } else {
+ *esp_ptr = ldl_kernel(env->tr.base + index);
+ *ss_ptr = lduw_kernel(env->tr.base + index + 4);
+ }
+}
+
+/* XXX: merge with load_seg() */
+static void tss_load_seg(int seg_reg, int selector)
+{
+ uint32_t e1, e2;
+ int rpl, dpl, cpl;
+
+ if ((selector & 0xfffc) != 0) {
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (seg_reg == R_CS) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ /* XXX: is it correct ? */
+ if (dpl != rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if ((e2 & DESC_C_MASK) && dpl > rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ } else if (seg_reg == R_SS) {
+ /* SS must be writable data */
+ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if (dpl != cpl || dpl != rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ } else {
+ /* not readable code */
+ if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ /* if data or non conforming code, checks the rights */
+ if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ }
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ } else {
+ if (seg_reg == R_SS || seg_reg == R_CS)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ }
+}
+
+#define SWITCH_TSS_JMP 0
+#define SWITCH_TSS_IRET 1
+#define SWITCH_TSS_CALL 2
+
+/* XXX: restore CPU state in registers (PowerPC case) */
+static void switch_tss(int tss_selector,
+ uint32_t e1, uint32_t e2, int source,
+ uint32_t next_eip)
+{
+ int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
+ target_ulong tss_base;
+ uint32_t new_regs[8], new_segs[6];
+ uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap;
+ uint32_t old_eflags, eflags_mask;
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL)
+ fprintf(logfile, "switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
+#endif
+
+ /* if task gate, we read the TSS segment and we load it */
+ if (type == 5) {
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
+ tss_selector = e1 >> 16;
+ if (tss_selector & 4)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ if (load_segment(&e1, &e2, tss_selector) != 0)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ if (e2 & DESC_S_MASK)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ if ((type & 7) != 1)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ }
+
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
+
+ if (type & 8)
+ tss_limit_max = 103;
+ else
+ tss_limit_max = 43;
+ tss_limit = get_seg_limit(e1, e2);
+ tss_base = get_seg_base(e1, e2);
+ if ((tss_selector & 4) != 0 ||
+ tss_limit < tss_limit_max)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ if (old_type & 8)
+ old_tss_limit_max = 103;
+ else
+ old_tss_limit_max = 43;
+
+ /* read all the registers from the new TSS */
+ if (type & 8) {
+ /* 32 bit */
+ new_cr3 = ldl_kernel(tss_base + 0x1c);
+ new_eip = ldl_kernel(tss_base + 0x20);
+ new_eflags = ldl_kernel(tss_base + 0x24);
+ for(i = 0; i < 8; i++)
+ new_regs[i] = ldl_kernel(tss_base + (0x28 + i * 4));
+ for(i = 0; i < 6; i++)
+ new_segs[i] = lduw_kernel(tss_base + (0x48 + i * 4));
+ new_ldt = lduw_kernel(tss_base + 0x60);
+ new_trap = ldl_kernel(tss_base + 0x64);
+ } else {
+ /* 16 bit */
+ new_cr3 = 0;
+ new_eip = lduw_kernel(tss_base + 0x0e);
+ new_eflags = lduw_kernel(tss_base + 0x10);
+ for(i = 0; i < 8; i++)
+ new_regs[i] = lduw_kernel(tss_base + (0x12 + i * 2)) | 0xffff0000;
+ for(i = 0; i < 4; i++)
+ new_segs[i] = lduw_kernel(tss_base + (0x22 + i * 4));
+ new_ldt = lduw_kernel(tss_base + 0x2a);
+ new_segs[R_FS] = 0;
+ new_segs[R_GS] = 0;
+ new_trap = 0;
+ }
+
+ /* NOTE: we must avoid memory exceptions during the task switch,
+ so we make dummy accesses before */
+ /* XXX: it can still fail in some cases, so a bigger hack is
+ necessary to valid the TLB after having done the accesses */
+
+ v1 = ldub_kernel(env->tr.base);
+ v2 = ldub_kernel(env->tr.base + old_tss_limit_max);
+ stb_kernel(env->tr.base, v1);
+ stb_kernel(env->tr.base + old_tss_limit_max, v2);
+
+ /* clear busy bit (it is restartable) */
+ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
+ target_ulong ptr;
+ uint32_t e2;
+ ptr = env->gdt.base + (env->tr.selector & ~7);
+ e2 = ldl_kernel(ptr + 4);
+ e2 &= ~DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+ old_eflags = compute_eflags();
+ if (source == SWITCH_TSS_IRET)
+ old_eflags &= ~NT_MASK;
+
+ /* save the current state in the old TSS */
+ if (type & 8) {
+ /* 32 bit */
+ stl_kernel(env->tr.base + 0x20, next_eip);
+ stl_kernel(env->tr.base + 0x24, old_eflags);
+ stl_kernel(env->tr.base + (0x28 + 0 * 4), EAX);
+ stl_kernel(env->tr.base + (0x28 + 1 * 4), ECX);
+ stl_kernel(env->tr.base + (0x28 + 2 * 4), EDX);
+ stl_kernel(env->tr.base + (0x28 + 3 * 4), EBX);
+ stl_kernel(env->tr.base + (0x28 + 4 * 4), ESP);
+ stl_kernel(env->tr.base + (0x28 + 5 * 4), EBP);
+ stl_kernel(env->tr.base + (0x28 + 6 * 4), ESI);
+ stl_kernel(env->tr.base + (0x28 + 7 * 4), EDI);
+ for(i = 0; i < 6; i++)
+ stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector);
+ } else {
+ /* 16 bit */
+ stw_kernel(env->tr.base + 0x0e, next_eip);
+ stw_kernel(env->tr.base + 0x10, old_eflags);
+ stw_kernel(env->tr.base + (0x12 + 0 * 2), EAX);
+ stw_kernel(env->tr.base + (0x12 + 1 * 2), ECX);
+ stw_kernel(env->tr.base + (0x12 + 2 * 2), EDX);
+ stw_kernel(env->tr.base + (0x12 + 3 * 2), EBX);
+ stw_kernel(env->tr.base + (0x12 + 4 * 2), ESP);
+ stw_kernel(env->tr.base + (0x12 + 5 * 2), EBP);
+ stw_kernel(env->tr.base + (0x12 + 6 * 2), ESI);
+ stw_kernel(env->tr.base + (0x12 + 7 * 2), EDI);
+ for(i = 0; i < 4; i++)
+ stw_kernel(env->tr.base + (0x22 + i * 4), env->segs[i].selector);
+ }
+
+ /* now if an exception occurs, it will occurs in the next task
+ context */
+
+ if (source == SWITCH_TSS_CALL) {
+ stw_kernel(tss_base, env->tr.selector);
+ new_eflags |= NT_MASK;
+ }
+
+ /* set busy bit */
+ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
+ target_ulong ptr;
+ uint32_t e2;
+ ptr = env->gdt.base + (tss_selector & ~7);
+ e2 = ldl_kernel(ptr + 4);
+ e2 |= DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+
+ /* set the new CPU state */
+ /* from this point, any exception which occurs can give problems */
+ env->cr[0] |= CR0_TS_MASK;
+ env->hflags |= HF_TS_MASK;
+ env->tr.selector = tss_selector;
+ env->tr.base = tss_base;
+ env->tr.limit = tss_limit;
+ env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
+
+ if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
+ cpu_x86_update_cr3(env, new_cr3);
+ }
+
+ /* load all registers without an exception, then reload them with
+ possible exception */
+ env->eip = new_eip;
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
+ if (!(type & 8))
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+ /* XXX: what to do in 16 bit case ? */
+ EAX = new_regs[0];
+ ECX = new_regs[1];
+ EDX = new_regs[2];
+ EBX = new_regs[3];
+ ESP = new_regs[4];
+ EBP = new_regs[5];
+ ESI = new_regs[6];
+ EDI = new_regs[7];
+ if (new_eflags & VM_MASK) {
+ for(i = 0; i < 6; i++)
+ load_seg_vm(i, new_segs[i]);
+ /* in vm86, CPL is always 3 */
+ cpu_x86_set_cpl(env, 3);
+ } else {
+ /* CPL is set the RPL of CS */
+ cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
+ /* first just selectors as the rest may trigger exceptions */
+ for(i = 0; i < 6; i++)
+ cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
+ }
+
+ env->ldt.selector = new_ldt & ~4;
+ env->ldt.base = 0;
+ env->ldt.limit = 0;
+ env->ldt.flags = 0;
+
+ /* load the LDT */
+ if (new_ldt & 4)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+
+ if ((new_ldt & 0xfffc) != 0) {
+ dt = &env->gdt;
+ index = new_ldt & ~7;
+ if ((index + 7) > dt->limit)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
+
+ /* load the segments */
+ if (!(new_eflags & VM_MASK)) {
+ tss_load_seg(R_CS, new_segs[R_CS]);
+ tss_load_seg(R_SS, new_segs[R_SS]);
+ tss_load_seg(R_ES, new_segs[R_ES]);
+ tss_load_seg(R_DS, new_segs[R_DS]);
+ tss_load_seg(R_FS, new_segs[R_FS]);
+ tss_load_seg(R_GS, new_segs[R_GS]);
+ }
+
+ /* check that EIP is in the CS segment limits */
+ if (new_eip > env->segs[R_CS].limit) {
+ /* XXX: different exception if CALL ? */
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+}
+
+/* check if Port I/O is allowed in TSS */
+static inline void check_io(int addr, int size)
+{
+ int io_offset, val, mask;
+
+ /* TSS must be a valid 32 bit one */
+ if (!(env->tr.flags & DESC_P_MASK) ||
+ ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
+ env->tr.limit < 103)
+ goto fail;
+ io_offset = lduw_kernel(env->tr.base + 0x66);
+ io_offset += (addr >> 3);
+ /* Note: the check needs two bytes */
+ if ((io_offset + 1) > env->tr.limit)
+ goto fail;
+ val = lduw_kernel(env->tr.base + io_offset);
+ val >>= (addr & 7);
+ mask = (1 << size) - 1;
+ /* all bits must be zero to allow the I/O */
+ if ((val & mask) != 0) {
+ fail:
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+}
+
+void check_iob_T0(void)
+{
+ check_io(T0, 1);
+}
+
+void check_iow_T0(void)
+{
+ check_io(T0, 2);
+}
+
+void check_iol_T0(void)
+{
+ check_io(T0, 4);
+}
+
+void check_iob_DX(void)
+{
+ check_io(EDX & 0xffff, 1);
+}
+
+void check_iow_DX(void)
+{
+ check_io(EDX & 0xffff, 2);
+}
+
+void check_iol_DX(void)
+{
+ check_io(EDX & 0xffff, 4);
+}
+
+static inline unsigned int get_sp_mask(unsigned int e2)
+{
+ if (e2 & DESC_B_MASK)
+ return 0xffffffff;
+ else
+ return 0xffff;
+}
+
+/* XXX: add a is_user flag to have proper security support */
+#define PUSHW(ssp, sp, sp_mask, val)\
+{\
+ sp -= 2;\
+ stw_kernel((ssp) + (sp & (sp_mask)), (val));\
+}
+
+#define PUSHL(ssp, sp, sp_mask, val)\
+{\
+ sp -= 4;\
+ stl_kernel((ssp) + (sp & (sp_mask)), (val));\
+}
+
+#define POPW(ssp, sp, sp_mask, val)\
+{\
+ val = lduw_kernel((ssp) + (sp & (sp_mask)));\
+ sp += 2;\
+}
+
+#define POPL(ssp, sp, sp_mask, val)\
+{\
+ val = (uint32_t)ldl_kernel((ssp) + (sp & (sp_mask)));\
+ sp += 4;\
+}
+
+/* protected mode interrupt */
+static void do_interrupt_protected(int intno, int is_int, int error_code,
+ unsigned int next_eip, int is_hw)
+{
+ SegmentCache *dt;
+ target_ulong ptr, ssp;
+ int type, dpl, selector, ss_dpl, cpl, sp_mask;
+ int has_error_code, new_stack, shift;
+ uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2;
+ uint32_t old_eip;
+
+ has_error_code = 0;
+ if (!is_int && !is_hw) {
+ switch(intno) {
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 17:
+ has_error_code = 1;
+ break;
+ }
+ }
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+
+ dt = &env->idt;
+ if (intno * 8 + 7 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ ptr = dt->base + intno * 8;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 5: /* task gate */
+ /* must do that check here to return the correct error code */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
+ switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
+ if (has_error_code) {
+ int mask, type;
+ /* push the error code */
+ type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ shift = type >> 3;
+ if (env->segs[R_SS].flags & DESC_B_MASK)
+ mask = 0xffffffff;
+ else
+ mask = 0xffff;
+ esp = (ESP - (2 << shift)) & mask;
+ ssp = env->segs[R_SS].base + esp;
+ if (shift)
+ stl_kernel(ssp, error_code);
+ else
+ stw_kernel(ssp, error_code);
+ ESP = (esp & mask) | (ESP & ~mask);
+ }
+ return;
+ case 6: /* 286 interrupt gate */
+ case 7: /* 286 trap gate */
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
+ selector = e1 >> 16;
+ offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ if (!(e2 & DESC_C_MASK) && dpl < cpl) {
+ /* to inner priviledge */
+ get_ss_esp_from_tss(&ss, &esp, dpl);
+ if ((ss & 0xfffc) == 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if ((ss & 3) != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, ss) != 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (ss_dpl != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ new_stack = 1;
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
+ } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
+ /* to same priviledge */
+ if (env->eflags & VM_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ esp = ESP;
+ dpl = cpl;
+ } else {
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0; /* avoid warning */
+ sp_mask = 0; /* avoid warning */
+ ssp = 0; /* avoid warning */
+ esp = 0; /* avoid warning */
+ }
+
+ shift = type >> 3;
+
+#if 0
+ /* XXX: check that enough room is available */
+ push_size = 6 + (new_stack << 2) + (has_error_code << 1);
+ if (env->eflags & VM_MASK)
+ push_size += 8;
+ push_size <<= shift;
+#endif
+ if (shift == 1) {
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
+ }
+ PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, esp, sp_mask, ESP);
+ }
+ PUSHL(ssp, esp, sp_mask, compute_eflags());
+ PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, sp_mask, old_eip);
+ if (has_error_code) {
+ PUSHL(ssp, esp, sp_mask, error_code);
+ }
+ } else {
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
+ }
+ PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, esp, sp_mask, ESP);
+ }
+ PUSHW(ssp, esp, sp_mask, compute_eflags());
+ PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, sp_mask, old_eip);
+ if (has_error_code) {
+ PUSHW(ssp, esp, sp_mask, error_code);
+ }
+ }
+
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
+ }
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
+ }
+ ESP = (ESP & ~sp_mask) | (esp & sp_mask);
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ env->eip = offset;
+
+ /* interrupt gate clear IF mask */
+ if ((type & 1) == 0) {
+ env->eflags &= ~IF_MASK;
+ }
+ env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
+}
+
+#ifdef TARGET_X86_64
+
+#define PUSHQ(sp, val)\
+{\
+ sp -= 8;\
+ stq_kernel(sp, (val));\
+}
+
+#define POPQ(sp, val)\
+{\
+ val = ldq_kernel(sp);\
+ sp += 8;\
+}
+
+static inline target_ulong get_rsp_from_tss(int level)
+{
+ int index;
+
+#if 0
+ printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
+ env->tr.base, env->tr.limit);
+#endif
+
+ if (!(env->tr.flags & DESC_P_MASK))
+ cpu_abort(env, "invalid tss");
+ index = 8 * level + 4;
+ if ((index + 7) > env->tr.limit)
+ raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
+ return ldq_kernel(env->tr.base + index);
+}
+
+/* 64 bit interrupt */
+static void do_interrupt64(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ int type, dpl, selector, cpl, ist;
+ int has_error_code, new_stack;
+ uint32_t e1, e2, e3, ss;
+ target_ulong old_eip, esp, offset;
+
+ has_error_code = 0;
+ if (!is_int && !is_hw) {
+ switch(intno) {
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 17:
+ has_error_code = 1;
+ break;
+ }
+ }
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+
+ dt = &env->idt;
+ if (intno * 16 + 15 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ ptr = dt->base + intno * 16;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ e3 = ldl_kernel(ptr + 8);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
+ selector = e1 >> 16;
+ offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ ist = e2 & 7;
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
+ /* to inner priviledge */
+ if (ist != 0)
+ esp = get_rsp_from_tss(ist + 3);
+ else
+ esp = get_rsp_from_tss(dpl);
+ esp &= ~0xfLL; /* align stack */
+ ss = 0;
+ new_stack = 1;
+ } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
+ /* to same priviledge */
+ if (env->eflags & VM_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0;
+ if (ist != 0)
+ esp = get_rsp_from_tss(ist + 3);
+ else
+ esp = ESP;
+ esp &= ~0xfLL; /* align stack */
+ dpl = cpl;
+ } else {
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0; /* avoid warning */
+ esp = 0; /* avoid warning */
+ }
+
+ PUSHQ(esp, env->segs[R_SS].selector);
+ PUSHQ(esp, ESP);
+ PUSHQ(esp, compute_eflags());
+ PUSHQ(esp, env->segs[R_CS].selector);
+ PUSHQ(esp, old_eip);
+ if (has_error_code) {
+ PUSHQ(esp, error_code);
+ }
+
+ if (new_stack) {
+ ss = 0 | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
+ }
+ ESP = esp;
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ env->eip = offset;
+
+ /* interrupt gate clear IF mask */
+ if ((type & 1) == 0) {
+ env->eflags &= ~IF_MASK;
+ }
+ env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
+}
+#endif
+
+void helper_syscall(int next_eip_addend)
+{
+ int selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ selector = (env->star >> 32) & 0xffff;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ int code64;
+
+ ECX = env->eip + next_eip_addend;
+ env->regs[11] = compute_eflags();
+
+ code64 = env->hflags & HF_CS64_MASK;
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~env->fmask;
+ if (code64)
+ env->eip = env->lstar;
+ else
+ env->eip = env->cstar;
+ } else
+#endif
+ {
+ ECX = (uint32_t)(env->eip + next_eip_addend);
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
+ env->eip = (uint32_t)env->star;
+ }
+}
+
+void helper_sysret(int dflag)
+{
+ int cpl, selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ cpl = env->hflags & HF_CPL_MASK;
+ if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ selector = (env->star >> 48) & 0xffff;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ if (dflag == 2) {
+ cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
+ DESC_L_MASK);
+ env->eip = ECX;
+ } else {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ }
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
+ cpu_x86_set_cpl(env, 3);
+ } else
+#endif
+ {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags |= IF_MASK;
+ cpu_x86_set_cpl(env, 3);
+ }
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ if (env->hflags & HF_LMA_MASK)
+ CC_OP = CC_OP_EFLAGS;
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+/* real mode interrupt */
+static void do_interrupt_real(int intno, int is_int, int error_code,
+ unsigned int next_eip)
+{
+ SegmentCache *dt;
+ target_ulong ptr, ssp;
+ int selector;
+ uint32_t offset, esp;
+ uint32_t old_cs, old_eip;
+
+ /* real mode (simpler !) */
+ dt = &env->idt;
+ if (intno * 4 + 3 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ ptr = dt->base + intno * 4;
+ offset = lduw_kernel(ptr);
+ selector = lduw_kernel(ptr + 2);
+ esp = ESP;
+ ssp = env->segs[R_SS].base;
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+ old_cs = env->segs[R_CS].selector;
+ /* XXX: use SS segment size ? */
+ PUSHW(ssp, esp, 0xffff, compute_eflags());
+ PUSHW(ssp, esp, 0xffff, old_cs);
+ PUSHW(ssp, esp, 0xffff, old_eip);
+
+ /* update processor state */
+ ESP = (ESP & ~0xffff) | (esp & 0xffff);
+ env->eip = offset;
+ env->segs[R_CS].selector = selector;
+ env->segs[R_CS].base = (selector << 4);
+ env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
+}
+
+/* fake user mode interrupt */
+void do_interrupt_user(int intno, int is_int, int error_code,
+ target_ulong next_eip)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ int dpl, cpl;
+ uint32_t e2;
+
+ dt = &env->idt;
+ ptr = dt->base + (intno * 8);
+ e2 = ldl_kernel(ptr + 4);
+
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+
+ /* Since we emulate only user space, we cannot do more than
+ exiting the emulation with the suitable exception and error
+ code */
+ if (is_int)
+ EIP = next_eip;
+}
+
+/*
+ * Begin execution of an interruption. is_int is TRUE if coming from
+ * the int instruction. next_eip is the EIP value AFTER the interrupt
+ * instruction. It is only relevant if is_int is TRUE.
+ */
+void do_interrupt(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw)
+{
+ if (loglevel & CPU_LOG_INT) {
+ if ((env->cr[0] & CR0_PE_MASK)) {
+ static int count;
+ fprintf(logfile, "%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
+ count, intno, error_code, is_int,
+ env->hflags & HF_CPL_MASK,
+ env->segs[R_CS].selector, EIP,
+ (int)env->segs[R_CS].base + EIP,
+ env->segs[R_SS].selector, ESP);
+ if (intno == 0x0e) {
+ fprintf(logfile, " CR2=" TARGET_FMT_lx, env->cr[2]);
+ } else {
+ fprintf(logfile, " EAX=" TARGET_FMT_lx, EAX);
+ }
+ fprintf(logfile, "\n");
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+#if 0
+ {
+ int i;
+ uint8_t *ptr;
+ fprintf(logfile, " code=");
+ ptr = env->segs[R_CS].base + env->eip;
+ for(i = 0; i < 16; i++) {
+ fprintf(logfile, " %02x", ldub(ptr + i));
+ }
+ fprintf(logfile, "\n");
+ }
+#endif
+ count++;
+ }
+ }
+ if (env->cr[0] & CR0_PE_MASK) {
+#if TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
+ } else
+#endif
+ {
+ do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
+ }
+ } else {
+ do_interrupt_real(intno, is_int, error_code, next_eip);
+ }
+}
+
+/*
+ * Signal an interruption. It is executed in the main CPU loop.
+ * is_int is TRUE if coming from the int instruction. next_eip is the
+ * EIP value AFTER the interrupt instruction. It is only relevant if
+ * is_int is TRUE.
+ */
+void raise_interrupt(int intno, int is_int, int error_code,
+ int next_eip_addend)
+{
+ env->exception_index = intno;
+ env->error_code = error_code;
+ env->exception_is_int = is_int;
+ env->exception_next_eip = env->eip + next_eip_addend;
+ cpu_loop_exit();
+}
+
+/* same as raise_exception_err, but do not restore global registers */
+static void raise_exception_err_norestore(int exception_index, int error_code)
+{
+ env->exception_index = exception_index;
+ env->error_code = error_code;
+ env->exception_is_int = 0;
+ env->exception_next_eip = 0;
+ longjmp(env->jmp_env, 1);
+}
+
+/* shortcuts to generate exceptions */
+
+void (raise_exception_err)(int exception_index, int error_code)
+{
+ raise_interrupt(exception_index, 0, error_code, 0);
+}
+
+void raise_exception(int exception_index)
+{
+ raise_interrupt(exception_index, 0, 0, 0);
+}
+
+#ifdef BUGGY_GCC_DIV64
+/* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we
+ call it from another function */
+uint32_t div32(uint64_t *q_ptr, uint64_t num, uint32_t den)
+{
+ *q_ptr = num / den;
+ return num % den;
+}
+
+int32_t idiv32(int64_t *q_ptr, int64_t num, int32_t den)
+{
+ *q_ptr = num / den;
+ return num % den;
+}
+#endif
+
+void helper_divl_EAX_T0(void)
+{
+ unsigned int den, r;
+ uint64_t num, q;
+
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+ den = T0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+#ifdef BUGGY_GCC_DIV64
+ r = div32(&q, num, den);
+#else
+ q = (num / den);
+ r = (num % den);
+#endif
+ if (q > 0xffffffff)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
+}
+
+void helper_idivl_EAX_T0(void)
+{
+ int den, r;
+ int64_t num, q;
+
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+ den = T0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+#ifdef BUGGY_GCC_DIV64
+ r = idiv32(&q, num, den);
+#else
+ q = (num / den);
+ r = (num % den);
+#endif
+ if (q != (int32_t)q)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
+}
+
+void helper_cmpxchg8b(void)
+{
+ uint64_t d;
+ int eflags;
+
+ eflags = cc_table[CC_OP].compute_all();
+ d = ldq(A0);
+ if (d == (((uint64_t)EDX << 32) | EAX)) {
+ stq(A0, ((uint64_t)ECX << 32) | EBX);
+ eflags |= CC_Z;
+ } else {
+ EDX = d >> 32;
+ EAX = d;
+ eflags &= ~CC_Z;
+ }
+ CC_SRC = eflags;
+}
+
+void helper_cpuid(void)
+{
+ uint32_t index;
+ index = (uint32_t)EAX;
+
+ /* test if maximum index reached */
+ if (index & 0x80000000) {
+ if (index > env->cpuid_xlevel)
+ index = env->cpuid_level;
+ } else {
+ if (index > env->cpuid_level)
+ index = env->cpuid_level;
+ }
+
+ switch(index) {
+ case 0:
+ EAX = env->cpuid_level;
+ EBX = env->cpuid_vendor1;
+ EDX = env->cpuid_vendor2;
+ ECX = env->cpuid_vendor3;
+ break;
+ case 1:
+ EAX = env->cpuid_version;
+ EBX = 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */
+ ECX = env->cpuid_ext_features;
+ EDX = env->cpuid_features;
+ break;
+ case 2:
+ /* cache info: needed for Pentium Pro compatibility */
+ EAX = 0x410601;
+ EBX = 0;
+ ECX = 0;
+ EDX = 0;
+ break;
+ case 0x80000000:
+ EAX = env->cpuid_xlevel;
+ EBX = env->cpuid_vendor1;
+ EDX = env->cpuid_vendor2;
+ ECX = env->cpuid_vendor3;
+ break;
+ case 0x80000001:
+ EAX = env->cpuid_features;
+ EBX = 0;
+ ECX = 0;
+ EDX = env->cpuid_ext2_features;
+ break;
+ case 0x80000002:
+ case 0x80000003:
+ case 0x80000004:
+ EAX = env->cpuid_model[(index - 0x80000002) * 4 + 0];
+ EBX = env->cpuid_model[(index - 0x80000002) * 4 + 1];
+ ECX = env->cpuid_model[(index - 0x80000002) * 4 + 2];
+ EDX = env->cpuid_model[(index - 0x80000002) * 4 + 3];
+ break;
+ case 0x80000005:
+ /* cache info (L1 cache) */
+ EAX = 0x01ff01ff;
+ EBX = 0x01ff01ff;
+ ECX = 0x40020140;
+ EDX = 0x40020140;
+ break;
+ case 0x80000006:
+ /* cache info (L2 cache) */
+ EAX = 0;
+ EBX = 0x42004200;
+ ECX = 0x02008140;
+ EDX = 0;
+ break;
+ case 0x80000008:
+ /* virtual & phys address size in low 2 bytes. */
+ EAX = 0x00003028;
+ EBX = 0;
+ ECX = 0;
+ EDX = 0;
+ break;
+ default:
+ /* reserved values: zero */
+ EAX = 0;
+ EBX = 0;
+ ECX = 0;
+ EDX = 0;
+ break;
+ }
+}
+
+void helper_enter_level(int level, int data32)
+{
+ target_ulong ssp;
+ uint32_t esp_mask, esp, ebp;
+
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ ebp = EBP;
+ esp = ESP;
+ if (data32) {
+ /* 32 bit */
+ esp -= 4;
+ while (--level) {
+ esp -= 4;
+ ebp -= 4;
+ stl(ssp + (esp & esp_mask), ldl(ssp + (ebp & esp_mask)));
+ }
+ esp -= 4;
+ stl(ssp + (esp & esp_mask), T1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(ssp + (esp & esp_mask), lduw(ssp + (ebp & esp_mask)));
+ }
+ esp -= 2;
+ stw(ssp + (esp & esp_mask), T1);
+ }
+}
+
+#ifdef TARGET_X86_64
+void helper_enter64_level(int level, int data64)
+{
+ target_ulong esp, ebp;
+ ebp = EBP;
+ esp = ESP;
+
+ if (data64) {
+ /* 64 bit */
+ esp -= 8;
+ while (--level) {
+ esp -= 8;
+ ebp -= 8;
+ stq(esp, ldq(ebp));
+ }
+ esp -= 8;
+ stq(esp, T1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(esp, lduw(ebp));
+ }
+ esp -= 2;
+ stw(esp, T1);
+ }
+}
+#endif
+
+void helper_lldt_T0(void)
+{
+ int selector;
+ SegmentCache *dt;
+ uint32_t e1, e2;
+ int index, entry_limit;
+ target_ulong ptr;
+
+ selector = T0 & 0xffff;
+ if ((selector & 0xfffc) == 0) {
+ /* XXX: NULL selector case: invalid LDT */
+ env->ldt.base = 0;
+ env->ldt.limit = 0;
+ } else {
+ if (selector & 0x4)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dt = &env->gdt;
+ index = selector & ~7;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3;
+ e3 = ldl_kernel(ptr + 8);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ env->ldt.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
+ }
+ env->ldt.selector = selector;
+}
+
+void helper_ltr_T0(void)
+{
+ int selector;
+ SegmentCache *dt;
+ uint32_t e1, e2;
+ int index, type, entry_limit;
+ target_ulong ptr;
+
+ selector = T0 & 0xffff;
+ if ((selector & 0xfffc) == 0) {
+ /* NULL selector case: invalid TR */
+ env->tr.base = 0;
+ env->tr.limit = 0;
+ env->tr.flags = 0;
+ } else {
+ if (selector & 0x4)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dt = &env->gdt;
+ index = selector & ~7;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ if ((e2 & DESC_S_MASK) ||
+ (type != 1 && type != 9))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3;
+ e3 = ldl_kernel(ptr + 8);
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ env->tr.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ }
+ e2 |= DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+ env->tr.selector = selector;
+}
+
+/* only works if protected mode and not VM86. seg_reg must be != R_CS */
+void load_seg(int seg_reg, int selector)
+{
+ uint32_t e1, e2;
+ int cpl, dpl, rpl;
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ selector &= 0xffff;
+ cpl = env->hflags & HF_CPL_MASK;
+ if ((selector & 0xfffc) == 0) {
+ /* null selector case */
+ if (seg_reg == R_SS
+#ifdef TARGET_X86_64
+ && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
+#endif
+ )
+ raise_exception_err(EXCP0D_GPF, 0);
+ cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
+ } else {
+
+ if (selector & 0x4)
+ dt = &env->ldt;
+ else
+ dt = &env->gdt;
+ index = selector & ~7;
+ if ((index + 7) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+
+ if (!(e2 & DESC_S_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (seg_reg == R_SS) {
+ /* must be writable segment */
+ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (rpl != cpl || dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ } else {
+ /* must be readable segment */
+ if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+
+ if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
+ /* if not conforming code, test rights */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
+ }
+
+ if (!(e2 & DESC_P_MASK)) {
+ if (seg_reg == R_SS)
+ raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
+ else
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ }
+
+ /* set the access bit if not already set */
+ if (!(e2 & DESC_A_MASK)) {
+ e2 |= DESC_A_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+#if 0
+ fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
+ selector, (unsigned long)sc->base, sc->limit, sc->flags);
+#endif
+ }
+}
+
+/* protected mode jump */
+void helper_ljmp_protected_T0_T1(int next_eip_addend)
+{
+ int new_cs, gate_cs, type;
+ uint32_t e1, e2, cpl, dpl, rpl, limit;
+ target_ulong new_eip, next_eip;
+
+ new_cs = T0;
+ new_eip = T1;
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ /* conforming code segment */
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ /* non conforming code segment */
+ rpl = new_cs & 3;
+ if (rpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit &&
+ !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ } else {
+ /* jump to call or task gate */
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ rpl = new_cs & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1: /* 286 TSS */
+ case 9: /* 386 TSS */
+ case 5: /* task gate */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ next_eip = env->eip + next_eip_addend;
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
+ CC_OP = CC_OP_EFLAGS;
+ break;
+ case 4: /* 286 call gate */
+ case 12: /* 386 call gate */
+ if ((dpl < cpl) || (dpl < rpl))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ gate_cs = e1 >> 16;
+ new_eip = (e1 & 0xffff);
+ if (type == 12)
+ new_eip |= (e2 & 0xffff0000);
+ if (load_segment(&e1, &e2, gate_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ /* must be code segment */
+ if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
+ (DESC_S_MASK | DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
+ (!(e2 & DESC_C_MASK) && (dpl != cpl)))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit)
+ raise_exception_err(EXCP0D_GPF, 0);
+ cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ break;
+ }
+ }
+}
+
+/* real mode call */
+void helper_lcall_real_T0_T1(int shift, int next_eip)
+{
+ int new_cs, new_eip;
+ uint32_t esp, esp_mask;
+ target_ulong ssp;
+
+ new_cs = T0;
+ new_eip = T1;
+ esp = ESP;
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ if (shift) {
+ PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, esp_mask, next_eip);
+ } else {
+ PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, esp_mask, next_eip);
+ }
+
+ ESP = (ESP & ~esp_mask) | (esp & esp_mask);
+ env->eip = new_eip;
+ env->segs[R_CS].selector = new_cs;
+ env->segs[R_CS].base = (new_cs << 4);
+}
+
+/* protected mode call */
+void helper_lcall_protected_T0_T1(int shift, int next_eip_addend)
+{
+ int new_cs, new_stack, i;
+ uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
+ uint32_t ss, ss_e1, ss_e2, sp, type, ss_dpl, sp_mask;
+ uint32_t val, limit, old_sp_mask;
+ target_ulong ssp, old_ssp, next_eip, new_eip;
+
+ new_cs = T0;
+ new_eip = T1;
+ next_eip = env->eip + next_eip_addend;
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "lcall %04x:%08x s=%d\n",
+ new_cs, (uint32_t)new_eip, shift);
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+ }
+#endif
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "desc=%08x:%08x\n", e1, e2);
+ }
+#endif
+ if (e2 & DESC_S_MASK) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ /* conforming code segment */
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ /* non conforming code segment */
+ rpl = new_cs & 3;
+ if (rpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+
+#ifdef TARGET_X86_64
+ /* XXX: check 16/32 bit cases in long mode */
+ if (shift == 2) {
+ target_ulong rsp;
+ /* 64 bit case */
+ rsp = ESP;
+ PUSHQ(rsp, env->segs[R_CS].selector);
+ PUSHQ(rsp, next_eip);
+ /* from this point, not restartable */
+ ESP = rsp;
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2), e2);
+ EIP = new_eip;
+ } else
+#endif
+ {
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ /* from this point, not restartable */
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ }
+ } else {
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ rpl = new_cs & 3;
+ switch(type) {
+ case 1: /* available 286 TSS */
+ case 9: /* available 386 TSS */
+ case 5: /* task gate */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
+ CC_OP = CC_OP_EFLAGS;
+ return;
+ case 4: /* 286 call gate */
+ case 12: /* 386 call gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ break;
+ }
+ shift = type >> 3;
+
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ selector = e1 >> 16;
+ offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ param_count = e2 & 0x1f;
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+
+ if (!(e2 & DESC_C_MASK) && dpl < cpl) {
+ /* to inner priviledge */
+ get_ss_esp_from_tss(&ss, &sp, dpl);
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL)
+ fprintf(logfile, "new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
+ ss, sp, param_count, ESP);
+#endif
+ if ((ss & 0xfffc) == 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if ((ss & 3) != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, ss) != 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (ss_dpl != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+
+ // push_size = ((param_count * 2) + 8) << shift;
+
+ old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ old_ssp = env->segs[R_SS].base;
+
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = ldl_kernel(old_ssp + ((ESP + i * 4) & old_sp_mask));
+ PUSHL(ssp, sp, sp_mask, val);
+ }
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = lduw_kernel(old_ssp + ((ESP + i * 2) & old_sp_mask));
+ PUSHW(ssp, sp, sp_mask, val);
+ }
+ }
+ new_stack = 1;
+ } else {
+ /* to same priviledge */
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ // push_size = (4 << shift);
+ new_stack = 0;
+ }
+
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ /* from this point, not restartable */
+
+ if (new_stack) {
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp,
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
+ }
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ EIP = offset;
+ }
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+/* real and vm86 mode iret */
+void helper_iret_real(int shift)
+{
+ uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
+ target_ulong ssp;
+ int eflags_mask;
+
+ sp_mask = 0xffff; /* XXXX: use SS segment size ? */
+ sp = ESP;
+ ssp = env->segs[R_SS].base;
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ POPL(ssp, sp, sp_mask, new_eflags);
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
+ POPW(ssp, sp, sp_mask, new_eflags);
+ }
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ load_seg_vm(R_CS, new_cs);
+ env->eip = new_eip;
+ if (env->eflags & VM_MASK)
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
+ else
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
+ if (shift == 0)
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+}
+
+static inline void validate_seg(int seg_reg, int cpl)
+{
+ int dpl;
+ uint32_t e2;
+
+ /* XXX: on x86_64, we do not want to nullify FS and GS because
+ they may still contain a valid base. I would be interested to
+ know how a real x86_64 CPU behaves */
+ if ((seg_reg == R_FS || seg_reg == R_GS) &&
+ (env->segs[seg_reg].selector & 0xfffc) == 0)
+ return;
+
+ e2 = env->segs[seg_reg].flags;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
+ /* data or non conforming code segment */
+ if (dpl < cpl) {
+ cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
+ }
+ }
+}
+
+/* protected mode iret */
+static inline void helper_ret_protected(int shift, int is_iret, int addend)
+{
+ uint32_t new_cs, new_eflags, new_ss;
+ uint32_t new_es, new_ds, new_fs, new_gs;
+ uint32_t e1, e2, ss_e1, ss_e2;
+ int cpl, dpl, rpl, eflags_mask, iopl;
+ target_ulong ssp, sp, new_eip, new_esp, sp_mask;
+
+#ifdef TARGET_X86_64
+ if (shift == 2)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ sp = ESP;
+ ssp = env->segs[R_SS].base;
+ new_eflags = 0; /* avoid warning */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_eip);
+ POPQ(sp, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPQ(sp, new_eflags);
+ }
+ } else
+#endif
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPL(ssp, sp, sp_mask, new_eflags);
+ if (new_eflags & VM_MASK)
+ goto return_to_vm86;
+ }
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
+ if (is_iret)
+ POPW(ssp, sp, sp_mask, new_eflags);
+ }
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
+ new_cs, new_eip, shift, addend);
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+ }
+#endif
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (!(e2 & DESC_S_MASK) ||
+ !(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+ rpl = new_cs & 3;
+ if (rpl < cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ if (dpl > rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ if (dpl != rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+
+ sp += addend;
+ if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
+ ((env->hflags & HF_CS64_MASK) && !is_iret))) {
+ /* return to same priledge level */
+ cpu_x86_load_seg_cache(env, R_CS, new_cs,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ } else {
+ /* return to different priviledge level */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_esp);
+ POPQ(sp, new_ss);
+ new_ss &= 0xffff;
+ } else
+#endif
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ new_ss &= 0xffff;
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_esp);
+ POPW(ssp, sp, sp_mask, new_ss);
+ }
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_PCALL) {
+ fprintf(logfile, "new ss:esp=%04x:" TARGET_FMT_lx "\n",
+ new_ss, new_esp);
+ }
+#endif
+ if ((new_ss & 0xfffc) == 0) {
+#ifdef TARGET_X86_64
+ /* NULL ss is allowed in long mode if cpl != 3*/
+ if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ } else
+#endif
+ {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ } else {
+ if ((new_ss & 3) != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ get_seg_base(ss_e1, ss_e2),
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
+ }
+
+ cpu_x86_load_seg_cache(env, R_CS, new_cs,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, rpl);
+ sp = new_esp;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(ss_e2);
+
+ /* validate data segments */
+ validate_seg(R_ES, rpl);
+ validate_seg(R_DS, rpl);
+ validate_seg(R_FS, rpl);
+ validate_seg(R_GS, rpl);
+
+ sp += addend;
+ }
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ env->eip = new_eip;
+ if (is_iret) {
+ /* NOTE: 'cpl' is the _old_ CPL */
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
+ if (cpl == 0)
+ eflags_mask |= IOPL_MASK;
+ iopl = (env->eflags >> IOPL_SHIFT) & 3;
+ if (cpl <= iopl)
+ eflags_mask |= IF_MASK;
+ if (shift == 0)
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+ }
+ return;
+
+ return_to_vm86:
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ POPL(ssp, sp, sp_mask, new_es);
+ POPL(ssp, sp, sp_mask, new_ds);
+ POPL(ssp, sp, sp_mask, new_fs);
+ POPL(ssp, sp, sp_mask, new_gs);
+
+ /* modify processor state */
+ load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
+ load_seg_vm(R_CS, new_cs & 0xffff);
+ cpu_x86_set_cpl(env, 3);
+ load_seg_vm(R_SS, new_ss & 0xffff);
+ load_seg_vm(R_ES, new_es & 0xffff);
+ load_seg_vm(R_DS, new_ds & 0xffff);
+ load_seg_vm(R_FS, new_fs & 0xffff);
+ load_seg_vm(R_GS, new_gs & 0xffff);
+
+ env->eip = new_eip & 0xffff;
+ ESP = new_esp;
+}
+
+void helper_iret_protected(int shift, int next_eip)
+{
+ int tss_selector, type;
+ uint32_t e1, e2;
+
+ /* specific case for TSS */
+ if (env->eflags & NT_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ raise_exception_err(EXCP0D_GPF, 0);
+#endif
+ tss_selector = lduw_kernel(env->tr.base + 0);
+ if (tss_selector & 4)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ if (load_segment(&e1, &e2, tss_selector) != 0)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
+ /* NOTE: we check both segment and busy TSS */
+ if (type != 3)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
+ } else {
+ helper_ret_protected(shift, 1, 0);
+ }
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ CC_OP = CC_OP_EFLAGS;
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+void helper_lret_protected(int shift, int addend)
+{
+ helper_ret_protected(shift, 0, addend);
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+void helper_sysenter(void)
+{
+ if (env->sysenter_cs == 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ ESP = env->sysenter_esp;
+ EIP = env->sysenter_eip;
+}
+
+void helper_sysexit(void)
+{
+ int cpl;
+
+ cpl = env->hflags & HF_CPL_MASK;
+ if (env->sysenter_cs == 0 || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ cpu_x86_set_cpl(env, 3);
+ cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ ESP = ECX;
+ EIP = EDX;
+#ifdef USE_KQEMU
+ if (kqemu_is_ok(env)) {
+ env->exception_index = -1;
+ cpu_loop_exit();
+ }
+#endif
+}
+
+void helper_movl_crN_T0(int reg)
+{
+#if !defined(CONFIG_USER_ONLY)
+ switch(reg) {
+ case 0:
+ cpu_x86_update_cr0(env, T0);
+ break;
+ case 3:
+ cpu_x86_update_cr3(env, T0);
+ break;
+ case 4:
+ cpu_x86_update_cr4(env, T0);
+ break;
+ case 8:
+ cpu_set_apic_tpr(env, T0);
+ break;
+ default:
+ env->cr[reg] = T0;
+ break;
+ }
+#endif
+}
+
+/* XXX: do more */
+void helper_movl_drN_T0(int reg)
+{
+ env->dr[reg] = T0;
+}
+
+void helper_invlpg(target_ulong addr)
+{
+ cpu_x86_flush_tlb(env, addr);
+}
+
+void helper_rdtsc(void)
+{
+ uint64_t val;
+
+ if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
+ raise_exception(EXCP0D_GPF);
+ }
+ val = cpu_get_tsc(env);
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
+}
+
+#if defined(CONFIG_USER_ONLY)
+void helper_wrmsr(void)
+{
+}
+
+void helper_rdmsr(void)
+{
+}
+#else
+void helper_wrmsr(void)
+{
+ uint64_t val;
+
+ val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+
+ switch((uint32_t)ECX) {
+ case MSR_IA32_SYSENTER_CS:
+ env->sysenter_cs = val & 0xffff;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ env->sysenter_esp = val;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ env->sysenter_eip = val;
+ break;
+ case MSR_IA32_APICBASE:
+ cpu_set_apic_base(env, val);
+ break;
+ case MSR_EFER:
+ {
+ uint64_t update_mask;
+ update_mask = 0;
+ if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
+ update_mask |= MSR_EFER_SCE;
+ if (env->cpuid_ext2_features & CPUID_EXT2_LM)
+ update_mask |= MSR_EFER_LME;
+ if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
+ update_mask |= MSR_EFER_FFXSR;
+ if (env->cpuid_ext2_features & CPUID_EXT2_NX)
+ update_mask |= MSR_EFER_NXE;
+ env->efer = (env->efer & ~update_mask) |
+ (val & update_mask);
+ }
+ break;
+ case MSR_STAR:
+ env->star = val;
+ break;
+ case MSR_PAT:
+ env->pat = val;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ env->lstar = val;
+ break;
+ case MSR_CSTAR:
+ env->cstar = val;
+ break;
+ case MSR_FMASK:
+ env->fmask = val;
+ break;
+ case MSR_FSBASE:
+ env->segs[R_FS].base = val;
+ break;
+ case MSR_GSBASE:
+ env->segs[R_GS].base = val;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = val;
+ break;
+#endif
+ default:
+ /* XXX: exception ? */
+ break;
+ }
+}
+
+void helper_rdmsr(void)
+{
+ uint64_t val;
+ switch((uint32_t)ECX) {
+ case MSR_IA32_SYSENTER_CS:
+ val = env->sysenter_cs;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ val = env->sysenter_esp;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ val = env->sysenter_eip;
+ break;
+ case MSR_IA32_APICBASE:
+ val = cpu_get_apic_base(env);
+ break;
+ case MSR_EFER:
+ val = env->efer;
+ break;
+ case MSR_STAR:
+ val = env->star;
+ break;
+ case MSR_PAT:
+ val = env->pat;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ val = env->lstar;
+ break;
+ case MSR_CSTAR:
+ val = env->cstar;
+ break;
+ case MSR_FMASK:
+ val = env->fmask;
+ break;
+ case MSR_FSBASE:
+ val = env->segs[R_FS].base;
+ break;
+ case MSR_GSBASE:
+ val = env->segs[R_GS].base;
+ break;
+ case MSR_KERNELGSBASE:
+ val = env->kernelgsbase;
+ break;
+#endif
+ default:
+ /* XXX: exception ? */
+ val = 0;
+ break;
+ }
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
+}
+#endif
+
+void helper_lsl(void)
+{
+ unsigned int selector, limit;
+ uint32_t e1, e2, eflags;
+ int rpl, dpl, cpl, type;
+
+ eflags = cc_table[CC_OP].compute_all();
+ selector = T0 & 0xffff;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+ /* conforming */
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1:
+ case 2:
+ case 3:
+ case 9:
+ case 11:
+ break;
+ default:
+ goto fail;
+ }
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ limit = get_seg_limit(e1, e2);
+ T1 = limit;
+ CC_SRC = eflags | CC_Z;
+}
+
+void helper_lar(void)
+{
+ unsigned int selector;
+ uint32_t e1, e2, eflags;
+ int rpl, dpl, cpl, type;
+
+ eflags = cc_table[CC_OP].compute_all();
+ selector = T0 & 0xffff;
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+ /* conforming */
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 9:
+ case 11:
+ case 12:
+ break;
+ default:
+ goto fail;
+ }
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ T1 = e2 & 0x00f0ff00;
+ CC_SRC = eflags | CC_Z;
+}
+
+void helper_verr(void)
+{
+ unsigned int selector;
+ uint32_t e1, e2, eflags;
+ int rpl, dpl, cpl;
+
+ eflags = cc_table[CC_OP].compute_all();
+ selector = T0 & 0xffff;
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ if (!(e2 & DESC_S_MASK))
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_CS_MASK) {
+ if (!(e2 & DESC_R_MASK))
+ goto fail;
+ if (!(e2 & DESC_C_MASK)) {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ CC_SRC = eflags | CC_Z;
+}
+
+void helper_verw(void)
+{
+ unsigned int selector;
+ uint32_t e1, e2, eflags;
+ int rpl, dpl, cpl;
+
+ eflags = cc_table[CC_OP].compute_all();
+ selector = T0 & 0xffff;
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ if (!(e2 & DESC_S_MASK))
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_CS_MASK) {
+ goto fail;
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ if (!(e2 & DESC_W_MASK)) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ CC_SRC = eflags | CC_Z;
+}
+
+/* FPU helpers */
+
+void helper_fldt_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+ env->fpregs[new_fpstt].d = helper_fldt(A0);
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void helper_fstt_ST0_A0(void)
+{
+ helper_fstt(ST0, A0);
+}
+
+void fpu_set_exception(int mask)
+{
+ env->fpus |= mask;
+ if (env->fpus & (~env->fpuc & FPUC_EM))
+ env->fpus |= FPUS_SE | FPUS_B;
+}
+
+CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b)
+{
+ if (b == 0.0)
+ fpu_set_exception(FPUS_ZE);
+ return a / b;
+}
+
+void fpu_raise_exception(void)
+{
+ if (env->cr[0] & CR0_NE_MASK) {
+ raise_exception(EXCP10_COPR);
+ }
+#if !defined(CONFIG_USER_ONLY)
+ else {
+ cpu_set_ferr(env);
+ }
+#endif
+}
+
+/* BCD ops */
+
+void helper_fbld_ST0_A0(void)
+{
+ CPU86_LDouble tmp;
+ uint64_t val;
+ unsigned int v;
+ int i;
+
+ val = 0;
+ for(i = 8; i >= 0; i--) {
+ v = ldub(A0 + i);
+ val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
+ }
+ tmp = val;
+ if (ldub(A0 + 9) & 0x80)
+ tmp = -tmp;
+ fpush();
+ ST0 = tmp;
+}
+
+void helper_fbst_ST0_A0(void)
+{
+ int v;
+ target_ulong mem_ref, mem_end;
+ int64_t val;
+
+ val = floatx_to_int64(ST0, &env->fp_status);
+ mem_ref = A0;
+ mem_end = mem_ref + 9;
+ if (val < 0) {
+ stb(mem_end, 0x80);
+ val = -val;
+ } else {
+ stb(mem_end, 0x00);
+ }
+ while (mem_ref < mem_end) {
+ if (val == 0)
+ break;
+ v = val % 100;
+ val = val / 100;
+ v = ((v / 10) << 4) | (v % 10);
+ stb(mem_ref++, v);
+ }
+ while (mem_ref < mem_end) {
+ stb(mem_ref++, 0);
+ }
+}
+
+void helper_f2xm1(void)
+{
+ ST0 = pow(2.0,ST0) - 1.0;
+}
+
+void helper_fyl2x(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if (fptemp>0.0){
+ fptemp = log(fptemp)/log(2.0); /* log2(ST) */
+ ST1 *= fptemp;
+ fpop();
+ } else {
+ env->fpus &= (~0x4700);
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fptan(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = tan(fptemp);
+ fpush();
+ ST0 = 1.0;
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**52 only */
+ }
+}
+
+void helper_fpatan(void)
+{
+ CPU86_LDouble fptemp, fpsrcop;
+
+ fpsrcop = ST1;
+ fptemp = ST0;
+ ST1 = atan2(fpsrcop,fptemp);
+ fpop();
+}
+
+void helper_fxtract(void)
+{
+ CPU86_LDoubleU temp;
+ unsigned int expdif;
+
+ temp.d = ST0;
+ expdif = EXPD(temp) - EXPBIAS;
+ /*DP exponent bias*/
+ ST0 = expdif;
+ fpush();
+ BIASEXPONENT(temp);
+ ST0 = temp.d;
+}
+
+void helper_fprem1(void)
+{
+ CPU86_LDouble dblq, fpsrcop, fptemp;
+ CPU86_LDoubleU fpsrcop1, fptemp1;
+ int expdif;
+ int q;
+
+ fpsrcop = ST0;
+ fptemp = ST1;
+ fpsrcop1.d = fpsrcop;
+ fptemp1.d = fptemp;
+ expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
+ if (expdif < 53) {
+ dblq = fpsrcop / fptemp;
+ dblq = (dblq < 0.0)? ceil(dblq): floor(dblq);
+ ST0 = fpsrcop - fptemp*dblq;
+ q = (int)dblq; /* cutting off top bits is assumed here */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* (C0,C1,C3) <-- (q2,q1,q0) */
+ env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */
+ env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */
+ env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */
+ } else {
+ env->fpus |= 0x400; /* C2 <-- 1 */
+ fptemp = pow(2.0, expdif-50);
+ fpsrcop = (ST0 / ST1) / fptemp;
+ /* fpsrcop = integer obtained by rounding to the nearest */
+ fpsrcop = (fpsrcop-floor(fpsrcop) < ceil(fpsrcop)-fpsrcop)?
+ floor(fpsrcop): ceil(fpsrcop);
+ ST0 -= (ST1 * fpsrcop * fptemp);
+ }
+}
+
+void helper_fprem(void)
+{
+ CPU86_LDouble dblq, fpsrcop, fptemp;
+ CPU86_LDoubleU fpsrcop1, fptemp1;
+ int expdif;
+ int q;
+
+ fpsrcop = ST0;
+ fptemp = ST1;
+ fpsrcop1.d = fpsrcop;
+ fptemp1.d = fptemp;
+ expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
+ if ( expdif < 53 ) {
+ dblq = fpsrcop / fptemp;
+ dblq = (dblq < 0.0)? ceil(dblq): floor(dblq);
+ ST0 = fpsrcop - fptemp*dblq;
+ q = (int)dblq; /* cutting off top bits is assumed here */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* (C0,C1,C3) <-- (q2,q1,q0) */
+ env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */
+ env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */
+ env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */
+ } else {
+ env->fpus |= 0x400; /* C2 <-- 1 */
+ fptemp = pow(2.0, expdif-50);
+ fpsrcop = (ST0 / ST1) / fptemp;
+ /* fpsrcop = integer obtained by chopping */
+ fpsrcop = (fpsrcop < 0.0)?
+ -(floor(fabs(fpsrcop))): floor(fpsrcop);
+ ST0 -= (ST1 * fpsrcop * fptemp);
+ }
+}
+
+void helper_fyl2xp1(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp+1.0)>0.0) {
+ fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
+ ST1 *= fptemp;
+ fpop();
+ } else {
+ env->fpus &= (~0x4700);
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fsqrt(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if (fptemp<0.0) {
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ env->fpus |= 0x400;
+ }
+ ST0 = sqrt(fptemp);
+}
+
+void helper_fsincos(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = sin(fptemp);
+ fpush();
+ ST0 = cos(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**63 only */
+ }
+}
+
+void helper_frndint(void)
+{
+ ST0 = floatx_round_to_int(ST0, &env->fp_status);
+}
+
+void helper_fscale(void)
+{
+ ST0 = ldexp (ST0, (int)(ST1));
+}
+
+void helper_fsin(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = sin(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**53 only */
+ }
+}
+
+void helper_fcos(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = cos(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg5 < 2**63 only */
+ }
+}
+
+void helper_fxam_ST0(void)
+{
+ CPU86_LDoubleU temp;
+ int expdif;
+
+ temp.d = ST0;
+
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ if (SIGND(temp))
+ env->fpus |= 0x200; /* C1 <-- 1 */
+
+ /* XXX: test fptags too */
+ expdif = EXPD(temp);
+ if (expdif == MAXEXPD) {
+#ifdef USE_X86LDOUBLE
+ if (MANTD(temp) == 0x8000000000000000ULL)
+#else
+ if (MANTD(temp) == 0)
+#endif
+ env->fpus |= 0x500 /*Infinity*/;
+ else
+ env->fpus |= 0x100 /*NaN*/;
+ } else if (expdif == 0) {
+ if (MANTD(temp) == 0)
+ env->fpus |= 0x4000 /*Zero*/;
+ else
+ env->fpus |= 0x4400 /*Denormal*/;
+ } else {
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fstenv(target_ulong ptr, int data32)
+{
+ int fpus, fptag, exp, i;
+ uint64_t mant;
+ CPU86_LDoubleU tmp;
+
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for (i=7; i>=0; i--) {
+ fptag <<= 2;
+ if (env->fptags[i]) {
+ fptag |= 3;
+ } else {
+ tmp.d = env->fpregs[i].d;
+ exp = EXPD(tmp);
+ mant = MANTD(tmp);
+ if (exp == 0 && mant == 0) {
+ /* zero */
+ fptag |= 1;
+ } else if (exp == 0 || exp == MAXEXPD
+#ifdef USE_X86LDOUBLE
+ || (mant & (1LL << 63)) == 0
+#endif
+ ) {
+ /* NaNs, infinity, denormal */
+ fptag |= 2;
+ }
+ }
+ }
+ if (data32) {
+ /* 32 bit */
+ stl(ptr, env->fpuc);
+ stl(ptr + 4, fpus);
+ stl(ptr + 8, fptag);
+ stl(ptr + 12, 0); /* fpip */
+ stl(ptr + 16, 0); /* fpcs */
+ stl(ptr + 20, 0); /* fpoo */
+ stl(ptr + 24, 0); /* fpos */
+ } else {
+ /* 16 bit */
+ stw(ptr, env->fpuc);
+ stw(ptr + 2, fpus);
+ stw(ptr + 4, fptag);
+ stw(ptr + 6, 0);
+ stw(ptr + 8, 0);
+ stw(ptr + 10, 0);
+ stw(ptr + 12, 0);
+ }
+}
+
+void helper_fldenv(target_ulong ptr, int data32)
+{
+ int i, fpus, fptag;
+
+ if (data32) {
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 4);
+ fptag = lduw(ptr + 8);
+ }
+ else {
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 2);
+ fptag = lduw(ptr + 4);
+ }
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag & 3) == 3);
+ fptag >>= 2;
+ }
+}
+
+void helper_fsave(target_ulong ptr, int data32)
+{
+ CPU86_LDouble tmp;
+ int i;
+
+ helper_fstenv(ptr, data32);
+
+ ptr += (14 << data32);
+ for(i = 0;i < 8; i++) {
+ tmp = ST(i);
+ helper_fstt(tmp, ptr);
+ ptr += 10;
+ }
+
+ /* fninit */
+ env->fpus = 0;
+ env->fpstt = 0;
+ env->fpuc = 0x37f;
+ env->fptags[0] = 1;
+ env->fptags[1] = 1;
+ env->fptags[2] = 1;
+ env->fptags[3] = 1;
+ env->fptags[4] = 1;
+ env->fptags[5] = 1;
+ env->fptags[6] = 1;
+ env->fptags[7] = 1;
+}
+
+void helper_frstor(target_ulong ptr, int data32)
+{
+ CPU86_LDouble tmp;
+ int i;
+
+ helper_fldenv(ptr, data32);
+ ptr += (14 << data32);
+
+ for(i = 0;i < 8; i++) {
+ tmp = helper_fldt(ptr);
+ ST(i) = tmp;
+ ptr += 10;
+ }
+}
+
+void helper_fxsave(target_ulong ptr, int data64)
+{
+ int fpus, fptag, i, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= (env->fptags[i] << i);
+ }
+ stw(ptr, env->fpuc);
+ stw(ptr + 2, fpus);
+ stw(ptr + 4, fptag ^ 0xff);
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = ST(i);
+ helper_fstt(tmp, addr);
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ stl(ptr + 0x18, env->mxcsr); /* mxcsr */
+ stl(ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
+ nb_xmm_regs = 8 << data64;
+ addr = ptr + 0xa0;
+ for(i = 0; i < nb_xmm_regs; i++) {
+ stq(addr, env->xmm_regs[i].XMM_Q(0));
+ stq(addr + 8, env->xmm_regs[i].XMM_Q(1));
+ addr += 16;
+ }
+ }
+}
+
+void helper_fxrstor(target_ulong ptr, int data64)
+{
+ int i, fpus, fptag, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 2);
+ fptag = lduw(ptr + 4);
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ fptag ^= 0xff;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag >> i) & 1);
+ }
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = helper_fldt(addr);
+ ST(i) = tmp;
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ env->mxcsr = ldl(ptr + 0x18);
+ //ldl(ptr + 0x1c);
+ nb_xmm_regs = 8 << data64;
+ addr = ptr + 0xa0;
+ for(i = 0; i < nb_xmm_regs; i++) {
+ env->xmm_regs[i].XMM_Q(0) = ldq(addr);
+ env->xmm_regs[i].XMM_Q(1) = ldq(addr + 8);
+ addr += 16;
+ }
+ }
+}
+
+#ifndef USE_X86LDOUBLE
+
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
+{
+ CPU86_LDoubleU temp;
+ int e;
+
+ temp.d = f;
+ /* mantissa */
+ *pmant = (MANTD(temp) << 11) | (1LL << 63);
+ /* exponent + sign */
+ e = EXPD(temp) - EXPBIAS + 16383;
+ e |= SIGND(temp) >> 16;
+ *pexp = e;
+}
+
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+{
+ CPU86_LDoubleU temp;
+ int e;
+ uint64_t ll;
+
+ /* XXX: handle overflow ? */
+ e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
+ e |= (upper >> 4) & 0x800; /* sign */
+ ll = (mant >> 11) & ((1LL << 52) - 1);
+#ifdef __arm__
+ temp.l.upper = (e << 20) | (ll >> 32);
+ temp.l.lower = ll;
+#else
+ temp.ll = ll | ((uint64_t)e << 52);
+#endif
+ return temp.d;
+}
+
+#else
+
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
+{
+ CPU86_LDoubleU temp;
+
+ temp.d = f;
+ *pmant = temp.l.lower;
+ *pexp = temp.l.upper;
+}
+
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+{
+ CPU86_LDoubleU temp;
+
+ temp.l.upper = upper;
+ temp.l.lower = mant;
+ return temp.d;
+}
+#endif
+
+#ifdef TARGET_X86_64
+
+//#define DEBUG_MULDIV
+
+static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ *plow += a;
+ /* carry test */
+ if (*plow < a)
+ (*phigh)++;
+ *phigh += b;
+}
+
+static void neg128(uint64_t *plow, uint64_t *phigh)
+{
+ *plow = ~ *plow;
+ *phigh = ~ *phigh;
+ add128(plow, phigh, 1, 0);
+}
+
+static void mul64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ uint32_t a0, a1, b0, b1;
+ uint64_t v;
+
+ a0 = a;
+ a1 = a >> 32;
+
+ b0 = b;
+ b1 = b >> 32;
+
+ v = (uint64_t)a0 * (uint64_t)b0;
+ *plow = v;
+ *phigh = 0;
+
+ v = (uint64_t)a0 * (uint64_t)b1;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b0;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b1;
+ *phigh += v;
+#ifdef DEBUG_MULDIV
+ printf("mul: 0x%016" PRIx64 " * 0x%016" PRIx64 " = 0x%016" PRIx64 "%016" PRIx64 "\n",
+ a, b, *phigh, *plow);
+#endif
+}
+
+static void imul64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
+{
+ int sa, sb;
+ sa = (a < 0);
+ if (sa)
+ a = -a;
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ mul64(plow, phigh, a, b);
+ if (sa ^ sb) {
+ neg128(plow, phigh);
+ }
+}
+
+/* return TRUE if overflow */
+static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
+{
+ uint64_t q, r, a1, a0;
+ int i, qb, ab;
+
+ a0 = *plow;
+ a1 = *phigh;
+ if (a1 == 0) {
+ q = a0 / b;
+ r = a0 % b;
+ *plow = q;
+ *phigh = r;
+ } else {
+ if (a1 >= b)
+ return 1;
+ /* XXX: use a better algorithm */
+ for(i = 0; i < 64; i++) {
+ ab = a1 >> 63;
+ a1 = (a1 << 1) | (a0 >> 63);
+ if (ab || a1 >= b) {
+ a1 -= b;
+ qb = 1;
+ } else {
+ qb = 0;
+ }
+ a0 = (a0 << 1) | qb;
+ }
+#if defined(DEBUG_MULDIV)
+ printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
+ *phigh, *plow, b, a0, a1);
+#endif
+ *plow = a0;
+ *phigh = a1;
+ }
+ return 0;
+}
+
+/* return TRUE if overflow */
+static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
+{
+ int sa, sb;
+ sa = ((int64_t)*phigh < 0);
+ if (sa)
+ neg128(plow, phigh);
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ if (div64(plow, phigh, b) != 0)
+ return 1;
+ if (sa ^ sb) {
+ if (*plow > (1ULL << 63))
+ return 1;
+ *plow = - *plow;
+ } else {
+ if (*plow >= (1ULL << 63))
+ return 1;
+ }
+ if (sa)
+ *phigh = - *phigh;
+ return 0;
+}
+
+void helper_mulq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+
+ mul64(&r0, &r1, EAX, T0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = r1;
+}
+
+void helper_imulq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+
+ imul64(&r0, &r1, EAX, T0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+}
+
+void helper_imulq_T0_T1(void)
+{
+ uint64_t r0, r1;
+
+ imul64(&r0, &r1, T0, T1);
+ T0 = r0;
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+}
+
+void helper_divq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+ if (T0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (div64(&r0, &r1, T0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+
+void helper_idivq_EAX_T0(void)
+{
+ uint64_t r0, r1;
+ if (T0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (idiv64(&r0, &r1, T0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+
+void helper_bswapq_T0(void)
+{
+ T0 = bswap64(T0);
+}
+#endif
+
+void helper_hlt(void)
+{
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
+ env->hflags |= HF_HALTED_MASK;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+}
+
+void helper_monitor(void)
+{
+ if (ECX != 0)
+ raise_exception(EXCP0D_GPF);
+ /* XXX: store address ? */
+}
+
+void helper_mwait(void)
+{
+ if (ECX != 0)
+ raise_exception(EXCP0D_GPF);
+ /* XXX: not complete but not completely erroneous */
+ if (env->cpu_index != 0 || env->next_cpu != NULL) {
+ /* more than one CPU: do not sleep because another CPU may
+ wake this one */
+ } else {
+ helper_hlt();
+ }
+}
+
+float approx_rsqrt(float a)
+{
+ return 1.0 / sqrt(a);
+}
+
+float approx_rcp(float a)
+{
+ return 1.0 / a;
+}
+
+void update_fp_status(void)
+{
+ int rnd_type;
+
+ /* set rounding mode */
+ switch(env->fpuc & RC_MASK) {
+ default:
+ case RC_NEAR:
+ rnd_type = float_round_nearest_even;
+ break;
+ case RC_DOWN:
+ rnd_type = float_round_down;
+ break;
+ case RC_UP:
+ rnd_type = float_round_up;
+ break;
+ case RC_CHOP:
+ rnd_type = float_round_to_zero;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+#ifdef FLOATX80
+ switch((env->fpuc >> 8) & 3) {
+ case 0:
+ rnd_type = 32;
+ break;
+ case 2:
+ rnd_type = 64;
+ break;
+ case 3:
+ default:
+ rnd_type = 80;
+ break;
+ }
+ set_floatx80_rounding_precision(rnd_type, &env->fp_status);
+#endif
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#endif
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ int ret;
+ unsigned long pc;
+ CPUX86State *saved_env;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ ret = cpu_x86_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ if (retaddr)
+ raise_exception_err(env->exception_index, env->error_code);
+ else
+ raise_exception_err_norestore(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
diff --git a/target-i386/helper2.c b/target-i386/helper2.c
new file mode 100644
index 0000000..19af159
--- /dev/null
+++ b/target-i386/helper2.c
@@ -0,0 +1,1031 @@
+/*
+ * i386 helpers (without register variable usage)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+//#define DEBUG_MMU
+
+#ifdef USE_CODE_COPY
+#include <asm/ldt.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+
+int modify_ldt(int func, void *ptr, unsigned long bytecount)
+{
+ return syscall(__NR_modify_ldt, func, ptr, bytecount);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66)
+#define modify_ldt_ldt_s user_desc
+#endif
+#endif /* USE_CODE_COPY */
+
+CPUX86State *cpu_x86_init(void)
+{
+ CPUX86State *env;
+ static int inited;
+
+ env = qemu_mallocz(sizeof(CPUX86State));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+
+ /* init various static tables */
+ if (!inited) {
+ inited = 1;
+ optimize_flags_init();
+ }
+#ifdef USE_CODE_COPY
+ /* testing code for code copy case */
+ {
+ struct modify_ldt_ldt_s ldt;
+
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)env;
+ ldt.limit = (sizeof(CPUState) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ asm volatile ("movl %0, %%fs" : : "r" ((1 << 3) | 7));
+ }
+#endif
+ {
+ int family, model, stepping;
+#ifdef TARGET_X86_64
+ env->cpuid_vendor1 = 0x68747541; /* "Auth" */
+ env->cpuid_vendor2 = 0x69746e65; /* "enti" */
+ env->cpuid_vendor3 = 0x444d4163; /* "cAMD" */
+ family = 6;
+ model = 2;
+ stepping = 3;
+#else
+ env->cpuid_vendor1 = 0x756e6547; /* "Genu" */
+ env->cpuid_vendor2 = 0x49656e69; /* "ineI" */
+ env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */
+#if 0
+ /* pentium 75-200 */
+ family = 5;
+ model = 2;
+ stepping = 11;
+#else
+ /* pentium pro */
+ family = 6;
+ model = 3;
+ stepping = 3;
+#endif
+#endif
+ env->cpuid_level = 2;
+ env->cpuid_version = (family << 8) | (model << 4) | stepping;
+ env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE |
+ CPUID_TSC | CPUID_MSR | CPUID_MCE |
+ CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
+ CPUID_PAT);
+ env->pat = 0x0007040600070406ULL;
+ env->cpuid_ext_features = CPUID_EXT_SSE3;
+ env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
+ env->cpuid_features |= CPUID_APIC;
+ env->cpuid_xlevel = 0;
+ {
+ const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
+ int c, len, i;
+ len = strlen(model_id);
+ for(i = 0; i < 48; i++) {
+ if (i >= len)
+ c = '\0';
+ else
+ c = model_id[i];
+ env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
+ }
+ }
+#ifdef TARGET_X86_64
+ /* currently not enabled for std i386 because not fully tested */
+ env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
+ env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
+ env->cpuid_xlevel = 0x80000008;
+
+ /* these features are needed for Win64 and aren't fully implemented */
+ env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
+#endif
+ }
+ cpu_reset(env);
+#ifdef USE_KQEMU
+ kqemu_init(env);
+#endif
+ return env;
+}
+
+/* NOTE: must be called outside the CPU execute loop */
+void cpu_reset(CPUX86State *env)
+{
+ int i;
+
+ memset(env, 0, offsetof(CPUX86State, breakpoints));
+
+ tlb_flush(env, 1);
+
+ /* init to reset state */
+
+#ifdef CONFIG_SOFTMMU
+ env->hflags |= HF_SOFTMMU_MASK;
+#endif
+
+ cpu_x86_update_cr0(env, 0x60000010);
+ env->a20_mask = 0xffffffff;
+
+ env->idt.limit = 0xffff;
+ env->gdt.limit = 0xffff;
+ env->ldt.limit = 0xffff;
+ env->ldt.flags = DESC_P_MASK;
+ env->tr.limit = 0xffff;
+ env->tr.flags = DESC_P_MASK;
+
+ cpu_x86_load_seg_cache(env, R_CS, 0xf000, 0xffff0000, 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffff, 0);
+
+ env->eip = 0xfff0;
+ env->regs[R_EDX] = 0x600; /* indicate P6 processor */
+
+ env->eflags = 0x2;
+
+ /* FPU init */
+ for(i = 0;i < 8; i++)
+ env->fptags[i] = 1;
+ env->fpuc = 0x37f;
+
+ env->mxcsr = 0x1f80;
+}
+
+void cpu_x86_close(CPUX86State *env)
+{
+ free(env);
+}
+
+/***********************************************************/
+/* x86 debug */
+
+static const char *cc_op_str[] = {
+ "DYNAMIC",
+ "EFLAGS",
+
+ "MULB",
+ "MULW",
+ "MULL",
+ "MULQ",
+
+ "ADDB",
+ "ADDW",
+ "ADDL",
+ "ADDQ",
+
+ "ADCB",
+ "ADCW",
+ "ADCL",
+ "ADCQ",
+
+ "SUBB",
+ "SUBW",
+ "SUBL",
+ "SUBQ",
+
+ "SBBB",
+ "SBBW",
+ "SBBL",
+ "SBBQ",
+
+ "LOGICB",
+ "LOGICW",
+ "LOGICL",
+ "LOGICQ",
+
+ "INCB",
+ "INCW",
+ "INCL",
+ "INCQ",
+
+ "DECB",
+ "DECW",
+ "DECL",
+ "DECQ",
+
+ "SHLB",
+ "SHLW",
+ "SHLL",
+ "SHLQ",
+
+ "SARB",
+ "SARW",
+ "SARL",
+ "SARQ",
+};
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ int eflags, i, nb;
+ char cc_op_name[32];
+ static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+ eflags = env->eflags;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK) {
+ cpu_fprintf(f,
+ "RAX=%016" PRIx64 " RBX=%016" PRIx64 " RCX=%016" PRIx64 " RDX=%016" PRIx64 "\n"
+ "RSI=%016" PRIx64 " RDI=%016" PRIx64 " RBP=%016" PRIx64 " RSP=%016" PRIx64 "\n"
+ "R8 =%016" PRIx64 " R9 =%016" PRIx64 " R10=%016" PRIx64 " R11=%016" PRIx64 "\n"
+ "R12=%016" PRIx64 " R13=%016" PRIx64 " R14=%016" PRIx64 " R15=%016" PRIx64 "\n"
+ "RIP=%016" PRIx64 " RFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d HLT=%d\n",
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP],
+ env->regs[R_ESP],
+ env->regs[8],
+ env->regs[9],
+ env->regs[10],
+ env->regs[11],
+ env->regs[12],
+ env->regs[13],
+ env->regs[14],
+ env->regs[15],
+ env->eip, eflags,
+ eflags & DF_MASK ? 'D' : '-',
+ eflags & CC_O ? 'O' : '-',
+ eflags & CC_S ? 'S' : '-',
+ eflags & CC_Z ? 'Z' : '-',
+ eflags & CC_A ? 'A' : '-',
+ eflags & CC_P ? 'P' : '-',
+ eflags & CC_C ? 'C' : '-',
+ env->hflags & HF_CPL_MASK,
+ (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1,
+ (env->a20_mask >> 20) & 1,
+ (env->hflags >> HF_HALTED_SHIFT) & 1);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
+ "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
+ "EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d HLT=%d\n",
+ (uint32_t)env->regs[R_EAX],
+ (uint32_t)env->regs[R_EBX],
+ (uint32_t)env->regs[R_ECX],
+ (uint32_t)env->regs[R_EDX],
+ (uint32_t)env->regs[R_ESI],
+ (uint32_t)env->regs[R_EDI],
+ (uint32_t)env->regs[R_EBP],
+ (uint32_t)env->regs[R_ESP],
+ (uint32_t)env->eip, eflags,
+ eflags & DF_MASK ? 'D' : '-',
+ eflags & CC_O ? 'O' : '-',
+ eflags & CC_S ? 'S' : '-',
+ eflags & CC_Z ? 'Z' : '-',
+ eflags & CC_A ? 'A' : '-',
+ eflags & CC_P ? 'P' : '-',
+ eflags & CC_C ? 'C' : '-',
+ env->hflags & HF_CPL_MASK,
+ (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1,
+ (env->a20_mask >> 20) & 1,
+ (env->hflags >> HF_HALTED_SHIFT) & 1);
+ }
+
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ for(i = 0; i < 6; i++) {
+ SegmentCache *sc = &env->segs[i];
+ cpu_fprintf(f, "%s =%04x %016" PRIx64 " %08x %08x\n",
+ seg_name[i],
+ sc->selector,
+ sc->base,
+ sc->limit,
+ sc->flags);
+ }
+ cpu_fprintf(f, "LDT=%04x %016" PRIx64 " %08x %08x\n",
+ env->ldt.selector,
+ env->ldt.base,
+ env->ldt.limit,
+ env->ldt.flags);
+ cpu_fprintf(f, "TR =%04x %016" PRIx64 " %08x %08x\n",
+ env->tr.selector,
+ env->tr.base,
+ env->tr.limit,
+ env->tr.flags);
+ cpu_fprintf(f, "GDT= %016" PRIx64 " %08x\n",
+ env->gdt.base, env->gdt.limit);
+ cpu_fprintf(f, "IDT= %016" PRIx64 " %08x\n",
+ env->idt.base, env->idt.limit);
+ cpu_fprintf(f, "CR0=%08x CR2=%016" PRIx64 " CR3=%016" PRIx64 " CR4=%08x\n",
+ (uint32_t)env->cr[0],
+ env->cr[2],
+ env->cr[3],
+ (uint32_t)env->cr[4]);
+ } else
+#endif
+ {
+ for(i = 0; i < 6; i++) {
+ SegmentCache *sc = &env->segs[i];
+ cpu_fprintf(f, "%s =%04x %08x %08x %08x\n",
+ seg_name[i],
+ sc->selector,
+ (uint32_t)sc->base,
+ sc->limit,
+ sc->flags);
+ }
+ cpu_fprintf(f, "LDT=%04x %08x %08x %08x\n",
+ env->ldt.selector,
+ (uint32_t)env->ldt.base,
+ env->ldt.limit,
+ env->ldt.flags);
+ cpu_fprintf(f, "TR =%04x %08x %08x %08x\n",
+ env->tr.selector,
+ (uint32_t)env->tr.base,
+ env->tr.limit,
+ env->tr.flags);
+ cpu_fprintf(f, "GDT= %08x %08x\n",
+ (uint32_t)env->gdt.base, env->gdt.limit);
+ cpu_fprintf(f, "IDT= %08x %08x\n",
+ (uint32_t)env->idt.base, env->idt.limit);
+ cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n",
+ (uint32_t)env->cr[0],
+ (uint32_t)env->cr[2],
+ (uint32_t)env->cr[3],
+ (uint32_t)env->cr[4]);
+ }
+ if (flags & X86_DUMP_CCOP) {
+ if ((unsigned)env->cc_op < CC_OP_NB)
+ snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]);
+ else
+ snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK) {
+ cpu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%-8s\n",
+ env->cc_src, env->cc_dst,
+ cc_op_name);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n",
+ (uint32_t)env->cc_src, (uint32_t)env->cc_dst,
+ cc_op_name);
+ }
+ }
+ if (flags & X86_DUMP_FPU) {
+ int fptag;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= ((!env->fptags[i]) << i);
+ }
+ cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
+ env->fpuc,
+ (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11,
+ env->fpstt,
+ fptag,
+ env->mxcsr);
+ for(i=0;i<8;i++) {
+#if defined(USE_X86LDOUBLE)
+ union {
+ long double d;
+ struct {
+ uint64_t lower;
+ uint16_t upper;
+ } l;
+ } tmp;
+ tmp.d = env->fpregs[i].d;
+ cpu_fprintf(f, "FPR%d=%016" PRIx64 " %04x",
+ i, tmp.l.lower, tmp.l.upper);
+#else
+ cpu_fprintf(f, "FPR%d=%016" PRIx64,
+ i, env->fpregs[i].mmx.q);
+#endif
+ if ((i & 1) == 1)
+ cpu_fprintf(f, "\n");
+ else
+ cpu_fprintf(f, " ");
+ }
+ if (env->hflags & HF_CS64_MASK)
+ nb = 16;
+ else
+ nb = 8;
+ for(i=0;i<nb;i++) {
+ cpu_fprintf(f, "XMM%02d=%08x%08x%08x%08x",
+ i,
+ env->xmm_regs[i].XMM_L(3),
+ env->xmm_regs[i].XMM_L(2),
+ env->xmm_regs[i].XMM_L(1),
+ env->xmm_regs[i].XMM_L(0));
+ if ((i & 1) == 1)
+ cpu_fprintf(f, "\n");
+ else
+ cpu_fprintf(f, " ");
+ }
+ }
+}
+
+/***********************************************************/
+/* x86 mmu */
+/* XXX: add PGE support */
+
+void cpu_x86_set_a20(CPUX86State *env, int a20_state)
+{
+ a20_state = (a20_state != 0);
+ if (a20_state != ((env->a20_mask >> 20) & 1)) {
+#if defined(DEBUG_MMU)
+ printf("A20 update: a20=%d\n", a20_state);
+#endif
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ cpu_interrupt(env, CPU_INTERRUPT_EXITTB);
+
+ /* when a20 is changed, all the MMU mappings are invalid, so
+ we must flush everything */
+ tlb_flush(env, 1);
+ env->a20_mask = 0xffefffff | (a20_state << 20);
+ }
+}
+
+void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0)
+{
+ int pe_state;
+
+#if defined(DEBUG_MMU)
+ printf("CR0 update: CR0=0x%08x\n", new_cr0);
+#endif
+ if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) !=
+ (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) {
+ tlb_flush(env, 1);
+ }
+
+#ifdef TARGET_X86_64
+ if (!(env->cr[0] & CR0_PG_MASK) && (new_cr0 & CR0_PG_MASK) &&
+ (env->efer & MSR_EFER_LME)) {
+ /* enter in long mode */
+ /* XXX: generate an exception */
+ if (!(env->cr[4] & CR4_PAE_MASK))
+ return;
+ env->efer |= MSR_EFER_LMA;
+ env->hflags |= HF_LMA_MASK;
+ } else if ((env->cr[0] & CR0_PG_MASK) && !(new_cr0 & CR0_PG_MASK) &&
+ (env->efer & MSR_EFER_LMA)) {
+ /* exit long mode */
+ env->efer &= ~MSR_EFER_LMA;
+ env->hflags &= ~(HF_LMA_MASK | HF_CS64_MASK);
+ env->eip &= 0xffffffff;
+ }
+#endif
+ env->cr[0] = new_cr0 | CR0_ET_MASK;
+
+ /* update PE flag in hidden flags */
+ pe_state = (env->cr[0] & CR0_PE_MASK);
+ env->hflags = (env->hflags & ~HF_PE_MASK) | (pe_state << HF_PE_SHIFT);
+ /* ensure that ADDSEG is always set in real mode */
+ env->hflags |= ((pe_state ^ 1) << HF_ADDSEG_SHIFT);
+ /* update FPU flags */
+ env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) |
+ ((new_cr0 << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK));
+}
+
+/* XXX: in legacy PAE mode, generate a GPF if reserved bits are set in
+ the PDPT */
+void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3)
+{
+ env->cr[3] = new_cr3;
+ if (env->cr[0] & CR0_PG_MASK) {
+#if defined(DEBUG_MMU)
+ printf("CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3);
+#endif
+ tlb_flush(env, 0);
+ }
+}
+
+void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
+{
+#if defined(DEBUG_MMU)
+ printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]);
+#endif
+ if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) !=
+ (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) {
+ tlb_flush(env, 1);
+ }
+ /* SSE handling */
+ if (!(env->cpuid_features & CPUID_SSE))
+ new_cr4 &= ~CR4_OSFXSR_MASK;
+ if (new_cr4 & CR4_OSFXSR_MASK)
+ env->hflags |= HF_OSFXSR_MASK;
+ else
+ env->hflags &= ~HF_OSFXSR_MASK;
+
+ env->cr[4] = new_cr4;
+}
+
+/* XXX: also flush 4MB pages */
+void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr)
+{
+ tlb_flush_page(env, addr);
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write, int is_user, int is_softmmu)
+{
+ /* user mode only emulation */
+ is_write &= 1;
+ env->cr[2] = addr;
+ env->error_code = (is_write << PG_ERROR_W_BIT);
+ env->error_code |= PG_ERROR_U_MASK;
+ env->exception_index = EXCP0E_PAGE;
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+
+#else
+
+#define PHYS_ADDR_MASK 0xfffff000
+
+/* return value:
+ -1 = cannot handle fault
+ 0 = nothing more to do
+ 1 = generate PF fault
+ 2 = soft MMU activation required for this block
+*/
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write1, int is_user, int is_softmmu)
+{
+ uint64_t ptep, pte;
+ uint32_t pdpe_addr, pde_addr, pte_addr;
+ int error_code, is_dirty, prot, page_size, ret, is_write;
+ unsigned long paddr, page_offset;
+ target_ulong vaddr, virt_addr;
+
+#if defined(DEBUG_MMU)
+ printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d eip=" TARGET_FMT_lx "\n",
+ addr, is_write1, is_user, env->eip);
+#endif
+ is_write = is_write1 & 1;
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ pte = addr;
+ virt_addr = addr & TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ page_size = 4096;
+ goto do_mapping;
+ }
+
+ if (env->cr[4] & CR4_PAE_MASK) {
+ uint64_t pde, pdpe;
+
+ /* XXX: we only use 32 bit physical addresses */
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t pml4e_addr;
+ uint64_t pml4e;
+ int32_t sext;
+
+ /* test virtual address sign extension */
+ sext = (int64_t)addr >> 47;
+ if (sext != 0 && sext != -1) {
+ env->error_code = 0;
+ env->exception_index = EXCP0D_GPF;
+ return 1;
+ }
+
+ pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pml4e = ldq_phys(pml4e_addr);
+ if (!(pml4e & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pml4e & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ if (!(pml4e & PG_ACCESSED_MASK)) {
+ pml4e |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pml4e_addr, pml4e);
+ }
+ ptep = pml4e ^ PG_NX_MASK;
+ pdpe_addr = ((pml4e & PHYS_ADDR_MASK) + (((addr >> 30) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pdpe & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ ptep &= pdpe ^ PG_NX_MASK;
+ if (!(pdpe & PG_ACCESSED_MASK)) {
+ pdpe |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pdpe_addr, pdpe);
+ }
+ } else
+#endif
+ {
+ /* XXX: load them when cr3 is loaded ? */
+ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK;
+ }
+
+ pde_addr = ((pdpe & PHYS_ADDR_MASK) + (((addr >> 21) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pde = ldq_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pde & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ ptep &= pde ^ PG_NX_MASK;
+ if (pde & PG_PSE_MASK) {
+ /* 2 MB page */
+ page_size = 2048 * 1024;
+ ptep ^= PG_NX_MASK;
+ if ((ptep & PG_NX_MASK) && is_write1 == 2)
+ goto do_fault_protect;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pde & PG_DIRTY_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+ /* align to page_size */
+ pte = pde & ((PHYS_ADDR_MASK & ~(page_size - 1)) | 0xfff);
+ virt_addr = addr & ~(page_size - 1);
+ } else {
+ /* 4 KB page */
+ if (!(pde & PG_ACCESSED_MASK)) {
+ pde |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+ pte_addr = ((pde & PHYS_ADDR_MASK) + (((addr >> 12) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pte = ldq_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pte & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ /* combine pde and pte nx, user and rw protections */
+ ptep &= pte ^ PG_NX_MASK;
+ ptep ^= PG_NX_MASK;
+ if ((ptep & PG_NX_MASK) && is_write1 == 2)
+ goto do_fault_protect;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pte & PG_DIRTY_MASK);
+ if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
+ pte |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pte |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pte_addr, pte);
+ }
+ page_size = 4096;
+ virt_addr = addr & ~0xfff;
+ pte = pte & (PHYS_ADDR_MASK | 0xfff);
+ }
+ } else {
+ uint32_t pde;
+
+ /* page directory entry */
+ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) &
+ env->a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ /* if PSE bit is set, then we use a 4MB page */
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ page_size = 4096 * 1024;
+ if (is_user) {
+ if (!(pde & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(pde & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(pde & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pde & PG_DIRTY_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+
+ pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */
+ ptep = pte;
+ virt_addr = addr & ~(page_size - 1);
+ } else {
+ if (!(pde & PG_ACCESSED_MASK)) {
+ pde |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+
+ /* page directory entry */
+ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) &
+ env->a20_mask;
+ pte = ldl_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ /* combine pde and pte user and rw protections */
+ ptep = pte & pde;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pte & PG_DIRTY_MASK);
+ if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
+ pte |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pte |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pte_addr, pte);
+ }
+ page_size = 4096;
+ virt_addr = addr & ~0xfff;
+ }
+ }
+ /* the page can be put in the TLB */
+ prot = PAGE_READ;
+ if (!(ptep & PG_NX_MASK))
+ prot |= PAGE_EXEC;
+ if (pte & PG_DIRTY_MASK) {
+ /* only set write access if already dirty... otherwise wait
+ for dirty access */
+ if (is_user) {
+ if (ptep & PG_RW_MASK)
+ prot |= PAGE_WRITE;
+ } else {
+ if (!(env->cr[0] & CR0_WP_MASK) ||
+ (ptep & PG_RW_MASK))
+ prot |= PAGE_WRITE;
+ }
+ }
+ do_mapping:
+ pte = pte & env->a20_mask;
+
+ /* Even if 4MB pages, we map only one 4KB page in the cache to
+ avoid filling it too fast */
+ page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+ paddr = (pte & TARGET_PAGE_MASK) + page_offset;
+ vaddr = virt_addr + page_offset;
+
+ ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+ return ret;
+ do_fault_protect:
+ error_code = PG_ERROR_P_MASK;
+ do_fault:
+ env->cr[2] = addr;
+ error_code |= (is_write << PG_ERROR_W_BIT);
+ if (is_user)
+ error_code |= PG_ERROR_U_MASK;
+ if (is_write1 == 2 &&
+ (env->efer & MSR_EFER_NXE) &&
+ (env->cr[4] & CR4_PAE_MASK))
+ error_code |= PG_ERROR_I_D_MASK;
+ env->error_code = error_code;
+ env->exception_index = EXCP0E_PAGE;
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ uint32_t pde_addr, pte_addr;
+ uint32_t pde, pte, paddr, page_offset, page_size;
+
+ if (env->cr[4] & CR4_PAE_MASK) {
+ uint32_t pdpe_addr, pde_addr, pte_addr;
+ uint32_t pdpe;
+
+ /* XXX: we only use 32 bit physical addresses */
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t pml4e_addr, pml4e;
+ int32_t sext;
+
+ /* test virtual address sign extension */
+ sext = (int64_t)addr >> 47;
+ if (sext != 0 && sext != -1)
+ return -1;
+
+ pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pml4e = ldl_phys(pml4e_addr);
+ if (!(pml4e & PG_PRESENT_MASK))
+ return -1;
+
+ pdpe_addr = ((pml4e & ~0xfff) + (((addr >> 30) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pdpe = ldl_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK))
+ return -1;
+ } else
+#endif
+ {
+ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) &
+ env->a20_mask;
+ pdpe = ldl_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK))
+ return -1;
+ }
+
+ pde_addr = ((pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ return -1;
+ }
+ if (pde & PG_PSE_MASK) {
+ /* 2 MB page */
+ page_size = 2048 * 1024;
+ pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */
+ } else {
+ /* 4 KB page */
+ pte_addr = ((pde & ~0xfff) + (((addr >> 12) & 0x1ff) << 3)) &
+ env->a20_mask;
+ page_size = 4096;
+ pte = ldl_phys(pte_addr);
+ }
+ } else {
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ pte = addr;
+ page_size = 4096;
+ } else {
+ /* page directory entry */
+ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & env->a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK))
+ return -1;
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ pte = pde & ~0x003ff000; /* align to 4MB */
+ page_size = 4096 * 1024;
+ } else {
+ /* page directory entry */
+ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask;
+ pte = ldl_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK))
+ return -1;
+ page_size = 4096;
+ }
+ }
+ pte = pte & env->a20_mask;
+ }
+
+ page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+ paddr = (pte & TARGET_PAGE_MASK) + page_offset;
+ return paddr;
+}
+#endif /* !CONFIG_USER_ONLY */
+
+#if defined(USE_CODE_COPY)
+struct fpstate {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+
+ uint32_t fpip;
+ uint32_t fpcs;
+ uint32_t fpoo;
+ uint32_t fpos;
+ uint8_t fpregs1[8 * 10];
+};
+
+void restore_native_fp_state(CPUState *env)
+{
+ int fptag, i, j;
+ struct fpstate fp1, *fp = &fp1;
+
+ fp->fpuc = env->fpuc;
+ fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for (i=7; i>=0; i--) {
+ fptag <<= 2;
+ if (env->fptags[i]) {
+ fptag |= 3;
+ } else {
+ /* the FPU automatically computes it */
+ }
+ }
+ fp->fptag = fptag;
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&fp->fpregs1[i * 10], &env->fpregs[j].d, 10);
+ j = (j + 1) & 7;
+ }
+ asm volatile ("frstor %0" : "=m" (*fp));
+ env->native_fp_regs = 1;
+}
+
+void save_native_fp_state(CPUState *env)
+{
+ int fptag, i, j;
+ uint16_t fpuc;
+ struct fpstate fp1, *fp = &fp1;
+
+ asm volatile ("fsave %0" : : "m" (*fp));
+ env->fpuc = fp->fpuc;
+ env->fpstt = (fp->fpus >> 11) & 7;
+ env->fpus = fp->fpus & ~0x3800;
+ fptag = fp->fptag;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag & 3) == 3);
+ fptag >>= 2;
+ }
+ j = env->fpstt;
+ for(i = 0;i < 8; i++) {
+ memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 10], 10);
+ j = (j + 1) & 7;
+ }
+ /* we must restore the default rounding state */
+ /* XXX: we do not restore the exception state */
+ fpuc = 0x037f | (env->fpuc & (3 << 10));
+ asm volatile("fldcw %0" : : "m" (fpuc));
+ env->native_fp_regs = 0;
+}
+#endif
diff --git a/target-i386/op.c b/target-i386/op.c
new file mode 100644
index 0000000..7a3aa77
--- /dev/null
+++ b/target-i386/op.c
@@ -0,0 +1,2444 @@
+/*
+ * i386 micro operations
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define ASM_SOFTMMU
+#include "exec.h"
+
+/* n must be a constant to be efficient */
+static inline target_long lshift(target_long x, int n)
+{
+ if (n >= 0)
+ return x << n;
+ else
+ return x >> (-n);
+}
+
+/* we define the various pieces of code used by the JIT */
+
+#define REG EAX
+#define REGNAME _EAX
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG ECX
+#define REGNAME _ECX
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG EDX
+#define REGNAME _EDX
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG EBX
+#define REGNAME _EBX
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG ESP
+#define REGNAME _ESP
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG EBP
+#define REGNAME _EBP
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG ESI
+#define REGNAME _ESI
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG EDI
+#define REGNAME _EDI
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#ifdef TARGET_X86_64
+
+#define REG (env->regs[8])
+#define REGNAME _R8
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[9])
+#define REGNAME _R9
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[10])
+#define REGNAME _R10
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[11])
+#define REGNAME _R11
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[12])
+#define REGNAME _R12
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[13])
+#define REGNAME _R13
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[14])
+#define REGNAME _R14
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#define REG (env->regs[15])
+#define REGNAME _R15
+#include "opreg_template.h"
+#undef REG
+#undef REGNAME
+
+#endif
+
+/* operations with flags */
+
+/* update flags with T0 and T1 (add/sub case) */
+void OPPROTO op_update2_cc(void)
+{
+ CC_SRC = T1;
+ CC_DST = T0;
+}
+
+/* update flags with T0 (logic operation case) */
+void OPPROTO op_update1_cc(void)
+{
+ CC_DST = T0;
+}
+
+void OPPROTO op_update_neg_cc(void)
+{
+ CC_SRC = -T0;
+ CC_DST = T0;
+}
+
+void OPPROTO op_cmpl_T0_T1_cc(void)
+{
+ CC_SRC = T1;
+ CC_DST = T0 - T1;
+}
+
+void OPPROTO op_update_inc_cc(void)
+{
+ CC_SRC = cc_table[CC_OP].compute_c();
+ CC_DST = T0;
+}
+
+void OPPROTO op_testl_T0_T1_cc(void)
+{
+ CC_DST = T0 & T1;
+}
+
+/* operations without flags */
+
+void OPPROTO op_addl_T0_T1(void)
+{
+ T0 += T1;
+}
+
+void OPPROTO op_orl_T0_T1(void)
+{
+ T0 |= T1;
+}
+
+void OPPROTO op_andl_T0_T1(void)
+{
+ T0 &= T1;
+}
+
+void OPPROTO op_subl_T0_T1(void)
+{
+ T0 -= T1;
+}
+
+void OPPROTO op_xorl_T0_T1(void)
+{
+ T0 ^= T1;
+}
+
+void OPPROTO op_negl_T0(void)
+{
+ T0 = -T0;
+}
+
+void OPPROTO op_incl_T0(void)
+{
+ T0++;
+}
+
+void OPPROTO op_decl_T0(void)
+{
+ T0--;
+}
+
+void OPPROTO op_notl_T0(void)
+{
+ T0 = ~T0;
+}
+
+void OPPROTO op_bswapl_T0(void)
+{
+ T0 = bswap32(T0);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_bswapq_T0(void)
+{
+ helper_bswapq_T0();
+}
+#endif
+
+/* multiply/divide */
+
+/* XXX: add eflags optimizations */
+/* XXX: add non P4 style flags */
+
+void OPPROTO op_mulb_AL_T0(void)
+{
+ unsigned int res;
+ res = (uint8_t)EAX * (uint8_t)T0;
+ EAX = (EAX & ~0xffff) | res;
+ CC_DST = res;
+ CC_SRC = (res & 0xff00);
+}
+
+void OPPROTO op_imulb_AL_T0(void)
+{
+ int res;
+ res = (int8_t)EAX * (int8_t)T0;
+ EAX = (EAX & ~0xffff) | (res & 0xffff);
+ CC_DST = res;
+ CC_SRC = (res != (int8_t)res);
+}
+
+void OPPROTO op_mulw_AX_T0(void)
+{
+ unsigned int res;
+ res = (uint16_t)EAX * (uint16_t)T0;
+ EAX = (EAX & ~0xffff) | (res & 0xffff);
+ EDX = (EDX & ~0xffff) | ((res >> 16) & 0xffff);
+ CC_DST = res;
+ CC_SRC = res >> 16;
+}
+
+void OPPROTO op_imulw_AX_T0(void)
+{
+ int res;
+ res = (int16_t)EAX * (int16_t)T0;
+ EAX = (EAX & ~0xffff) | (res & 0xffff);
+ EDX = (EDX & ~0xffff) | ((res >> 16) & 0xffff);
+ CC_DST = res;
+ CC_SRC = (res != (int16_t)res);
+}
+
+void OPPROTO op_mull_EAX_T0(void)
+{
+ uint64_t res;
+ res = (uint64_t)((uint32_t)EAX) * (uint64_t)((uint32_t)T0);
+ EAX = (uint32_t)res;
+ EDX = (uint32_t)(res >> 32);
+ CC_DST = (uint32_t)res;
+ CC_SRC = (uint32_t)(res >> 32);
+}
+
+void OPPROTO op_imull_EAX_T0(void)
+{
+ int64_t res;
+ res = (int64_t)((int32_t)EAX) * (int64_t)((int32_t)T0);
+ EAX = (uint32_t)(res);
+ EDX = (uint32_t)(res >> 32);
+ CC_DST = res;
+ CC_SRC = (res != (int32_t)res);
+}
+
+void OPPROTO op_imulw_T0_T1(void)
+{
+ int res;
+ res = (int16_t)T0 * (int16_t)T1;
+ T0 = res;
+ CC_DST = res;
+ CC_SRC = (res != (int16_t)res);
+}
+
+void OPPROTO op_imull_T0_T1(void)
+{
+ int64_t res;
+ res = (int64_t)((int32_t)T0) * (int64_t)((int32_t)T1);
+ T0 = res;
+ CC_DST = res;
+ CC_SRC = (res != (int32_t)res);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_mulq_EAX_T0(void)
+{
+ helper_mulq_EAX_T0();
+}
+
+void OPPROTO op_imulq_EAX_T0(void)
+{
+ helper_imulq_EAX_T0();
+}
+
+void OPPROTO op_imulq_T0_T1(void)
+{
+ helper_imulq_T0_T1();
+}
+#endif
+
+/* division, flags are undefined */
+
+void OPPROTO op_divb_AL_T0(void)
+{
+ unsigned int num, den, q, r;
+
+ num = (EAX & 0xffff);
+ den = (T0 & 0xff);
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q > 0xff)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xff;
+ r = (num % den) & 0xff;
+ EAX = (EAX & ~0xffff) | (r << 8) | q;
+}
+
+void OPPROTO op_idivb_AL_T0(void)
+{
+ int num, den, q, r;
+
+ num = (int16_t)EAX;
+ den = (int8_t)T0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q != (int8_t)q)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xff;
+ r = (num % den) & 0xff;
+ EAX = (EAX & ~0xffff) | (r << 8) | q;
+}
+
+void OPPROTO op_divw_AX_T0(void)
+{
+ unsigned int num, den, q, r;
+
+ num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
+ den = (T0 & 0xffff);
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q > 0xffff)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xffff;
+ r = (num % den) & 0xffff;
+ EAX = (EAX & ~0xffff) | q;
+ EDX = (EDX & ~0xffff) | r;
+}
+
+void OPPROTO op_idivw_AX_T0(void)
+{
+ int num, den, q, r;
+
+ num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
+ den = (int16_t)T0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q != (int16_t)q)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xffff;
+ r = (num % den) & 0xffff;
+ EAX = (EAX & ~0xffff) | q;
+ EDX = (EDX & ~0xffff) | r;
+}
+
+void OPPROTO op_divl_EAX_T0(void)
+{
+ helper_divl_EAX_T0();
+}
+
+void OPPROTO op_idivl_EAX_T0(void)
+{
+ helper_idivl_EAX_T0();
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_divq_EAX_T0(void)
+{
+ helper_divq_EAX_T0();
+}
+
+void OPPROTO op_idivq_EAX_T0(void)
+{
+ helper_idivq_EAX_T0();
+}
+#endif
+
+/* constant load & misc op */
+
+/* XXX: consistent names */
+void OPPROTO op_movl_T0_imu(void)
+{
+ T0 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T0_im(void)
+{
+ T0 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_addl_T0_im(void)
+{
+ T0 += PARAM1;
+}
+
+void OPPROTO op_andl_T0_ffff(void)
+{
+ T0 = T0 & 0xffff;
+}
+
+void OPPROTO op_andl_T0_im(void)
+{
+ T0 = T0 & PARAM1;
+}
+
+void OPPROTO op_movl_T0_T1(void)
+{
+ T0 = T1;
+}
+
+void OPPROTO op_movl_T1_imu(void)
+{
+ T1 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T1_im(void)
+{
+ T1 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_addl_T1_im(void)
+{
+ T1 += PARAM1;
+}
+
+void OPPROTO op_movl_T1_A0(void)
+{
+ T1 = A0;
+}
+
+void OPPROTO op_movl_A0_im(void)
+{
+ A0 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_addl_A0_im(void)
+{
+ A0 = (uint32_t)(A0 + PARAM1);
+}
+
+void OPPROTO op_movl_A0_seg(void)
+{
+ A0 = (uint32_t)*(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_addl_A0_seg(void)
+{
+ A0 = (uint32_t)(A0 + *(target_ulong *)((char *)env + PARAM1));
+}
+
+void OPPROTO op_addl_A0_AL(void)
+{
+ A0 = (uint32_t)(A0 + (EAX & 0xff));
+}
+
+#ifdef WORDS_BIGENDIAN
+typedef union UREG64 {
+ struct { uint16_t v3, v2, v1, v0; } w;
+ struct { uint32_t v1, v0; } l;
+ uint64_t q;
+} UREG64;
+#else
+typedef union UREG64 {
+ struct { uint16_t v0, v1, v2, v3; } w;
+ struct { uint32_t v0, v1; } l;
+ uint64_t q;
+} UREG64;
+#endif
+
+#ifdef TARGET_X86_64
+
+#define PARAMQ1 \
+({\
+ UREG64 __p;\
+ __p.l.v1 = PARAM1;\
+ __p.l.v0 = PARAM2;\
+ __p.q;\
+})
+
+void OPPROTO op_movq_T0_im64(void)
+{
+ T0 = PARAMQ1;
+}
+
+void OPPROTO op_movq_T1_im64(void)
+{
+ T1 = PARAMQ1;
+}
+
+void OPPROTO op_movq_A0_im(void)
+{
+ A0 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_movq_A0_im64(void)
+{
+ A0 = PARAMQ1;
+}
+
+void OPPROTO op_addq_A0_im(void)
+{
+ A0 = (A0 + (int32_t)PARAM1);
+}
+
+void OPPROTO op_addq_A0_im64(void)
+{
+ A0 = (A0 + PARAMQ1);
+}
+
+void OPPROTO op_movq_A0_seg(void)
+{
+ A0 = *(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_addq_A0_seg(void)
+{
+ A0 += *(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_addq_A0_AL(void)
+{
+ A0 = (A0 + (EAX & 0xff));
+}
+
+#endif
+
+void OPPROTO op_andl_A0_ffff(void)
+{
+ A0 = A0 & 0xffff;
+}
+
+/* memory access */
+
+#define MEMSUFFIX _raw
+#include "ops_mem.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _kernel
+#include "ops_mem.h"
+
+#define MEMSUFFIX _user
+#include "ops_mem.h"
+#endif
+
+/* indirect jump */
+
+void OPPROTO op_jmp_T0(void)
+{
+ EIP = T0;
+}
+
+void OPPROTO op_movl_eip_im(void)
+{
+ EIP = (uint32_t)PARAM1;
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_movq_eip_im(void)
+{
+ EIP = (int32_t)PARAM1;
+}
+
+void OPPROTO op_movq_eip_im64(void)
+{
+ EIP = PARAMQ1;
+}
+#endif
+
+void OPPROTO op_hlt(void)
+{
+ helper_hlt();
+}
+
+void OPPROTO op_monitor(void)
+{
+ helper_monitor();
+}
+
+void OPPROTO op_mwait(void)
+{
+ helper_mwait();
+}
+
+void OPPROTO op_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_raise_interrupt(void)
+{
+ int intno, next_eip_addend;
+ intno = PARAM1;
+ next_eip_addend = PARAM2;
+ raise_interrupt(intno, 1, 0, next_eip_addend);
+}
+
+void OPPROTO op_raise_exception(void)
+{
+ int exception_index;
+ exception_index = PARAM1;
+ raise_exception(exception_index);
+}
+
+void OPPROTO op_into(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ if (eflags & CC_O) {
+ raise_interrupt(EXCP04_INTO, 1, 0, PARAM1);
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_cli(void)
+{
+ env->eflags &= ~IF_MASK;
+}
+
+void OPPROTO op_sti(void)
+{
+ env->eflags |= IF_MASK;
+}
+
+void OPPROTO op_set_inhibit_irq(void)
+{
+ env->hflags |= HF_INHIBIT_IRQ_MASK;
+}
+
+void OPPROTO op_reset_inhibit_irq(void)
+{
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+}
+
+#if 0
+/* vm86plus instructions */
+void OPPROTO op_cli_vm(void)
+{
+ env->eflags &= ~VIF_MASK;
+}
+
+void OPPROTO op_sti_vm(void)
+{
+ env->eflags |= VIF_MASK;
+ if (env->eflags & VIP_MASK) {
+ EIP = PARAM1;
+ raise_exception(EXCP0D_GPF);
+ }
+ FORCE_RET();
+}
+#endif
+
+void OPPROTO op_boundw(void)
+{
+ int low, high, v;
+ low = ldsw(A0);
+ high = ldsw(A0 + 2);
+ v = (int16_t)T0;
+ if (v < low || v > high) {
+ raise_exception(EXCP05_BOUND);
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_boundl(void)
+{
+ int low, high, v;
+ low = ldl(A0);
+ high = ldl(A0 + 4);
+ v = T0;
+ if (v < low || v > high) {
+ raise_exception(EXCP05_BOUND);
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_cmpxchg8b(void)
+{
+ helper_cmpxchg8b();
+}
+
+void OPPROTO op_movl_T0_0(void)
+{
+ T0 = 0;
+}
+
+void OPPROTO op_exit_tb(void)
+{
+ EXIT_TB();
+}
+
+/* multiple size ops */
+
+#define ldul ldl
+
+#define SHIFT 0
+#include "ops_template.h"
+#undef SHIFT
+
+#define SHIFT 1
+#include "ops_template.h"
+#undef SHIFT
+
+#define SHIFT 2
+#include "ops_template.h"
+#undef SHIFT
+
+#ifdef TARGET_X86_64
+
+#define SHIFT 3
+#include "ops_template.h"
+#undef SHIFT
+
+#endif
+
+/* sign extend */
+
+void OPPROTO op_movsbl_T0_T0(void)
+{
+ T0 = (int8_t)T0;
+}
+
+void OPPROTO op_movzbl_T0_T0(void)
+{
+ T0 = (uint8_t)T0;
+}
+
+void OPPROTO op_movswl_T0_T0(void)
+{
+ T0 = (int16_t)T0;
+}
+
+void OPPROTO op_movzwl_T0_T0(void)
+{
+ T0 = (uint16_t)T0;
+}
+
+void OPPROTO op_movswl_EAX_AX(void)
+{
+ EAX = (int16_t)EAX;
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_movslq_T0_T0(void)
+{
+ T0 = (int32_t)T0;
+}
+
+void OPPROTO op_movslq_RAX_EAX(void)
+{
+ EAX = (int32_t)EAX;
+}
+#endif
+
+void OPPROTO op_movsbw_AX_AL(void)
+{
+ EAX = (EAX & ~0xffff) | ((int8_t)EAX & 0xffff);
+}
+
+void OPPROTO op_movslq_EDX_EAX(void)
+{
+ EDX = (int32_t)EAX >> 31;
+}
+
+void OPPROTO op_movswl_DX_AX(void)
+{
+ EDX = (EDX & ~0xffff) | (((int16_t)EAX >> 15) & 0xffff);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_movsqo_RDX_RAX(void)
+{
+ EDX = (int64_t)EAX >> 63;
+}
+#endif
+
+/* string ops helpers */
+
+void OPPROTO op_addl_ESI_T0(void)
+{
+ ESI = (uint32_t)(ESI + T0);
+}
+
+void OPPROTO op_addw_ESI_T0(void)
+{
+ ESI = (ESI & ~0xffff) | ((ESI + T0) & 0xffff);
+}
+
+void OPPROTO op_addl_EDI_T0(void)
+{
+ EDI = (uint32_t)(EDI + T0);
+}
+
+void OPPROTO op_addw_EDI_T0(void)
+{
+ EDI = (EDI & ~0xffff) | ((EDI + T0) & 0xffff);
+}
+
+void OPPROTO op_decl_ECX(void)
+{
+ ECX = (uint32_t)(ECX - 1);
+}
+
+void OPPROTO op_decw_ECX(void)
+{
+ ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_addq_ESI_T0(void)
+{
+ ESI = (ESI + T0);
+}
+
+void OPPROTO op_addq_EDI_T0(void)
+{
+ EDI = (EDI + T0);
+}
+
+void OPPROTO op_decq_ECX(void)
+{
+ ECX--;
+}
+#endif
+
+/* push/pop utils */
+
+void op_addl_A0_SS(void)
+{
+ A0 = (uint32_t)(A0 + env->segs[R_SS].base);
+}
+
+void op_subl_A0_2(void)
+{
+ A0 = (uint32_t)(A0 - 2);
+}
+
+void op_subl_A0_4(void)
+{
+ A0 = (uint32_t)(A0 - 4);
+}
+
+void op_addl_ESP_4(void)
+{
+ ESP = (uint32_t)(ESP + 4);
+}
+
+void op_addl_ESP_2(void)
+{
+ ESP = (uint32_t)(ESP + 2);
+}
+
+void op_addw_ESP_4(void)
+{
+ ESP = (ESP & ~0xffff) | ((ESP + 4) & 0xffff);
+}
+
+void op_addw_ESP_2(void)
+{
+ ESP = (ESP & ~0xffff) | ((ESP + 2) & 0xffff);
+}
+
+void op_addl_ESP_im(void)
+{
+ ESP = (uint32_t)(ESP + PARAM1);
+}
+
+void op_addw_ESP_im(void)
+{
+ ESP = (ESP & ~0xffff) | ((ESP + PARAM1) & 0xffff);
+}
+
+#ifdef TARGET_X86_64
+void op_subq_A0_2(void)
+{
+ A0 -= 2;
+}
+
+void op_subq_A0_8(void)
+{
+ A0 -= 8;
+}
+
+void op_addq_ESP_8(void)
+{
+ ESP += 8;
+}
+
+void op_addq_ESP_im(void)
+{
+ ESP += PARAM1;
+}
+#endif
+
+void OPPROTO op_rdtsc(void)
+{
+ helper_rdtsc();
+}
+
+void OPPROTO op_cpuid(void)
+{
+ helper_cpuid();
+}
+
+void OPPROTO op_enter_level(void)
+{
+ helper_enter_level(PARAM1, PARAM2);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_enter64_level(void)
+{
+ helper_enter64_level(PARAM1, PARAM2);
+}
+#endif
+
+void OPPROTO op_sysenter(void)
+{
+ helper_sysenter();
+}
+
+void OPPROTO op_sysexit(void)
+{
+ helper_sysexit();
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_syscall(void)
+{
+ helper_syscall(PARAM1);
+}
+
+void OPPROTO op_sysret(void)
+{
+ helper_sysret(PARAM1);
+}
+#endif
+
+void OPPROTO op_rdmsr(void)
+{
+ helper_rdmsr();
+}
+
+void OPPROTO op_wrmsr(void)
+{
+ helper_wrmsr();
+}
+
+/* bcd */
+
+/* XXX: exception */
+void OPPROTO op_aam(void)
+{
+ int base = PARAM1;
+ int al, ah;
+ al = EAX & 0xff;
+ ah = al / base;
+ al = al % base;
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_DST = al;
+}
+
+void OPPROTO op_aad(void)
+{
+ int base = PARAM1;
+ int al, ah;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+ al = ((ah * base) + al) & 0xff;
+ EAX = (EAX & ~0xffff) | al;
+ CC_DST = al;
+}
+
+void OPPROTO op_aaa(void)
+{
+ int icarry;
+ int al, ah, af;
+ int eflags;
+
+ eflags = cc_table[CC_OP].compute_all();
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+
+ icarry = (al > 0xf9);
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al + 6) & 0x0f;
+ ah = (ah + 1 + icarry) & 0xff;
+ eflags |= CC_C | CC_A;
+ } else {
+ eflags &= ~(CC_C | CC_A);
+ al &= 0x0f;
+ }
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+void OPPROTO op_aas(void)
+{
+ int icarry;
+ int al, ah, af;
+ int eflags;
+
+ eflags = cc_table[CC_OP].compute_all();
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+
+ icarry = (al < 6);
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al - 6) & 0x0f;
+ ah = (ah - 1 - icarry) & 0xff;
+ eflags |= CC_C | CC_A;
+ } else {
+ eflags &= ~(CC_C | CC_A);
+ al &= 0x0f;
+ }
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+void OPPROTO op_daa(void)
+{
+ int al, af, cf;
+ int eflags;
+
+ eflags = cc_table[CC_OP].compute_all();
+ cf = eflags & CC_C;
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+
+ eflags = 0;
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al + 6) & 0xff;
+ eflags |= CC_A;
+ }
+ if ((al > 0x9f) || cf) {
+ al = (al + 0x60) & 0xff;
+ eflags |= CC_C;
+ }
+ EAX = (EAX & ~0xff) | al;
+ /* well, speed is not an issue here, so we compute the flags by hand */
+ eflags |= (al == 0) << 6; /* zf */
+ eflags |= parity_table[al]; /* pf */
+ eflags |= (al & 0x80); /* sf */
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+void OPPROTO op_das(void)
+{
+ int al, al1, af, cf;
+ int eflags;
+
+ eflags = cc_table[CC_OP].compute_all();
+ cf = eflags & CC_C;
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+
+ eflags = 0;
+ al1 = al;
+ if (((al & 0x0f) > 9 ) || af) {
+ eflags |= CC_A;
+ if (al < 6 || cf)
+ eflags |= CC_C;
+ al = (al - 6) & 0xff;
+ }
+ if ((al1 > 0x99) || cf) {
+ al = (al - 0x60) & 0xff;
+ eflags |= CC_C;
+ }
+ EAX = (EAX & ~0xff) | al;
+ /* well, speed is not an issue here, so we compute the flags by hand */
+ eflags |= (al == 0) << 6; /* zf */
+ eflags |= parity_table[al]; /* pf */
+ eflags |= (al & 0x80); /* sf */
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+/* segment handling */
+
+/* never use it with R_CS */
+void OPPROTO op_movl_seg_T0(void)
+{
+ load_seg(PARAM1, T0);
+}
+
+/* faster VM86 version */
+void OPPROTO op_movl_seg_T0_vm(void)
+{
+ int selector;
+ SegmentCache *sc;
+
+ selector = T0 & 0xffff;
+ /* env->segs[] access */
+ sc = (SegmentCache *)((char *)env + PARAM1);
+ sc->selector = selector;
+ sc->base = (selector << 4);
+}
+
+void OPPROTO op_movl_T0_seg(void)
+{
+ T0 = env->segs[PARAM1].selector;
+}
+
+void OPPROTO op_lsl(void)
+{
+ helper_lsl();
+}
+
+void OPPROTO op_lar(void)
+{
+ helper_lar();
+}
+
+void OPPROTO op_verr(void)
+{
+ helper_verr();
+}
+
+void OPPROTO op_verw(void)
+{
+ helper_verw();
+}
+
+void OPPROTO op_arpl(void)
+{
+ if ((T0 & 3) < (T1 & 3)) {
+ /* XXX: emulate bug or 0xff3f0000 oring as in bochs ? */
+ T0 = (T0 & ~3) | (T1 & 3);
+ T1 = CC_Z;
+ } else {
+ T1 = 0;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_arpl_update(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ CC_SRC = (eflags & ~CC_Z) | T1;
+}
+
+/* T0: segment, T1:eip */
+void OPPROTO op_ljmp_protected_T0_T1(void)
+{
+ helper_ljmp_protected_T0_T1(PARAM1);
+}
+
+void OPPROTO op_lcall_real_T0_T1(void)
+{
+ helper_lcall_real_T0_T1(PARAM1, PARAM2);
+}
+
+void OPPROTO op_lcall_protected_T0_T1(void)
+{
+ helper_lcall_protected_T0_T1(PARAM1, PARAM2);
+}
+
+void OPPROTO op_iret_real(void)
+{
+ helper_iret_real(PARAM1);
+}
+
+void OPPROTO op_iret_protected(void)
+{
+ helper_iret_protected(PARAM1, PARAM2);
+}
+
+void OPPROTO op_lret_protected(void)
+{
+ helper_lret_protected(PARAM1, PARAM2);
+}
+
+void OPPROTO op_lldt_T0(void)
+{
+ helper_lldt_T0();
+}
+
+void OPPROTO op_ltr_T0(void)
+{
+ helper_ltr_T0();
+}
+
+/* CR registers access */
+void OPPROTO op_movl_crN_T0(void)
+{
+ helper_movl_crN_T0(PARAM1);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void OPPROTO op_movtl_T0_cr8(void)
+{
+ T0 = cpu_get_apic_tpr(env);
+}
+#endif
+
+/* DR registers access */
+void OPPROTO op_movl_drN_T0(void)
+{
+ helper_movl_drN_T0(PARAM1);
+}
+
+void OPPROTO op_lmsw_T0(void)
+{
+ /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
+ if already set to one. */
+ T0 = (env->cr[0] & ~0xe) | (T0 & 0xf);
+ helper_movl_crN_T0(0);
+}
+
+void OPPROTO op_invlpg_A0(void)
+{
+ helper_invlpg(A0);
+}
+
+void OPPROTO op_movl_T0_env(void)
+{
+ T0 = *(uint32_t *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movl_env_T0(void)
+{
+ *(uint32_t *)((char *)env + PARAM1) = T0;
+}
+
+void OPPROTO op_movl_env_T1(void)
+{
+ *(uint32_t *)((char *)env + PARAM1) = T1;
+}
+
+void OPPROTO op_movtl_T0_env(void)
+{
+ T0 = *(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movtl_env_T0(void)
+{
+ *(target_ulong *)((char *)env + PARAM1) = T0;
+}
+
+void OPPROTO op_movtl_T1_env(void)
+{
+ T1 = *(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movtl_env_T1(void)
+{
+ *(target_ulong *)((char *)env + PARAM1) = T1;
+}
+
+void OPPROTO op_clts(void)
+{
+ env->cr[0] &= ~CR0_TS_MASK;
+ env->hflags &= ~HF_TS_MASK;
+}
+
+/* flags handling */
+
+void OPPROTO op_goto_tb0(void)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+void OPPROTO op_goto_tb1(void)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+void OPPROTO op_jmp_label(void)
+{
+ GOTO_LABEL_PARAM(1);
+}
+
+void OPPROTO op_jnz_T0_label(void)
+{
+ if (T0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_jz_T0_label(void)
+{
+ if (!T0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+/* slow set cases (compute x86 flags) */
+void OPPROTO op_seto_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (eflags >> 11) & 1;
+}
+
+void OPPROTO op_setb_T0_cc(void)
+{
+ T0 = cc_table[CC_OP].compute_c();
+}
+
+void OPPROTO op_setz_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (eflags >> 6) & 1;
+}
+
+void OPPROTO op_setbe_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (eflags & (CC_Z | CC_C)) != 0;
+}
+
+void OPPROTO op_sets_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (eflags >> 7) & 1;
+}
+
+void OPPROTO op_setp_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (eflags >> 2) & 1;
+}
+
+void OPPROTO op_setl_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = ((eflags ^ (eflags >> 4)) >> 7) & 1;
+}
+
+void OPPROTO op_setle_T0_cc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ T0 = (((eflags ^ (eflags >> 4)) & 0x80) || (eflags & CC_Z)) != 0;
+}
+
+void OPPROTO op_xor_T0_1(void)
+{
+ T0 ^= 1;
+}
+
+void OPPROTO op_set_cc_op(void)
+{
+ CC_OP = PARAM1;
+}
+
+void OPPROTO op_mov_T0_cc(void)
+{
+ T0 = cc_table[CC_OP].compute_all();
+}
+
+/* XXX: clear VIF/VIP in all ops ? */
+
+void OPPROTO op_movl_eflags_T0(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK));
+}
+
+void OPPROTO op_movw_eflags_T0(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff);
+}
+
+void OPPROTO op_movl_eflags_T0_io(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK));
+}
+
+void OPPROTO op_movw_eflags_T0_io(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK) & 0xffff);
+}
+
+void OPPROTO op_movl_eflags_T0_cpl0(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK));
+}
+
+void OPPROTO op_movw_eflags_T0_cpl0(void)
+{
+ load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK) & 0xffff);
+}
+
+#if 0
+/* vm86plus version */
+void OPPROTO op_movw_eflags_T0_vm(void)
+{
+ int eflags;
+ eflags = T0;
+ CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((eflags >> 10) & 1));
+ /* we also update some system flags as in user mode */
+ env->eflags = (env->eflags & ~(FL_UPDATE_MASK16 | VIF_MASK)) |
+ (eflags & FL_UPDATE_MASK16);
+ if (eflags & IF_MASK) {
+ env->eflags |= VIF_MASK;
+ if (env->eflags & VIP_MASK) {
+ EIP = PARAM1;
+ raise_exception(EXCP0D_GPF);
+ }
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_movl_eflags_T0_vm(void)
+{
+ int eflags;
+ eflags = T0;
+ CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((eflags >> 10) & 1));
+ /* we also update some system flags as in user mode */
+ env->eflags = (env->eflags & ~(FL_UPDATE_MASK32 | VIF_MASK)) |
+ (eflags & FL_UPDATE_MASK32);
+ if (eflags & IF_MASK) {
+ env->eflags |= VIF_MASK;
+ if (env->eflags & VIP_MASK) {
+ EIP = PARAM1;
+ raise_exception(EXCP0D_GPF);
+ }
+ }
+ FORCE_RET();
+}
+#endif
+
+/* XXX: compute only O flag */
+void OPPROTO op_movb_eflags_T0(void)
+{
+ int of;
+ of = cc_table[CC_OP].compute_all() & CC_O;
+ CC_SRC = (T0 & (CC_S | CC_Z | CC_A | CC_P | CC_C)) | of;
+}
+
+void OPPROTO op_movl_T0_eflags(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags |= (DF & DF_MASK);
+ eflags |= env->eflags & ~(VM_MASK | RF_MASK);
+ T0 = eflags;
+}
+
+/* vm86plus version */
+#if 0
+void OPPROTO op_movl_T0_eflags_vm(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags |= (DF & DF_MASK);
+ eflags |= env->eflags & ~(VM_MASK | RF_MASK | IF_MASK);
+ if (env->eflags & VIF_MASK)
+ eflags |= IF_MASK;
+ T0 = eflags;
+}
+#endif
+
+void OPPROTO op_cld(void)
+{
+ DF = 1;
+}
+
+void OPPROTO op_std(void)
+{
+ DF = -1;
+}
+
+void OPPROTO op_clc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags &= ~CC_C;
+ CC_SRC = eflags;
+}
+
+void OPPROTO op_stc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags |= CC_C;
+ CC_SRC = eflags;
+}
+
+void OPPROTO op_cmc(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags ^= CC_C;
+ CC_SRC = eflags;
+}
+
+void OPPROTO op_salc(void)
+{
+ int cf;
+ cf = cc_table[CC_OP].compute_c();
+ EAX = (EAX & ~0xff) | ((-cf) & 0xff);
+}
+
+static int compute_all_eflags(void)
+{
+ return CC_SRC;
+}
+
+static int compute_c_eflags(void)
+{
+ return CC_SRC & CC_C;
+}
+
+CCTable cc_table[CC_OP_NB] = {
+ [CC_OP_DYNAMIC] = { /* should never happen */ },
+
+ [CC_OP_EFLAGS] = { compute_all_eflags, compute_c_eflags },
+
+ [CC_OP_MULB] = { compute_all_mulb, compute_c_mull },
+ [CC_OP_MULW] = { compute_all_mulw, compute_c_mull },
+ [CC_OP_MULL] = { compute_all_mull, compute_c_mull },
+
+ [CC_OP_ADDB] = { compute_all_addb, compute_c_addb },
+ [CC_OP_ADDW] = { compute_all_addw, compute_c_addw },
+ [CC_OP_ADDL] = { compute_all_addl, compute_c_addl },
+
+ [CC_OP_ADCB] = { compute_all_adcb, compute_c_adcb },
+ [CC_OP_ADCW] = { compute_all_adcw, compute_c_adcw },
+ [CC_OP_ADCL] = { compute_all_adcl, compute_c_adcl },
+
+ [CC_OP_SUBB] = { compute_all_subb, compute_c_subb },
+ [CC_OP_SUBW] = { compute_all_subw, compute_c_subw },
+ [CC_OP_SUBL] = { compute_all_subl, compute_c_subl },
+
+ [CC_OP_SBBB] = { compute_all_sbbb, compute_c_sbbb },
+ [CC_OP_SBBW] = { compute_all_sbbw, compute_c_sbbw },
+ [CC_OP_SBBL] = { compute_all_sbbl, compute_c_sbbl },
+
+ [CC_OP_LOGICB] = { compute_all_logicb, compute_c_logicb },
+ [CC_OP_LOGICW] = { compute_all_logicw, compute_c_logicw },
+ [CC_OP_LOGICL] = { compute_all_logicl, compute_c_logicl },
+
+ [CC_OP_INCB] = { compute_all_incb, compute_c_incl },
+ [CC_OP_INCW] = { compute_all_incw, compute_c_incl },
+ [CC_OP_INCL] = { compute_all_incl, compute_c_incl },
+
+ [CC_OP_DECB] = { compute_all_decb, compute_c_incl },
+ [CC_OP_DECW] = { compute_all_decw, compute_c_incl },
+ [CC_OP_DECL] = { compute_all_decl, compute_c_incl },
+
+ [CC_OP_SHLB] = { compute_all_shlb, compute_c_shlb },
+ [CC_OP_SHLW] = { compute_all_shlw, compute_c_shlw },
+ [CC_OP_SHLL] = { compute_all_shll, compute_c_shll },
+
+ [CC_OP_SARB] = { compute_all_sarb, compute_c_sarl },
+ [CC_OP_SARW] = { compute_all_sarw, compute_c_sarl },
+ [CC_OP_SARL] = { compute_all_sarl, compute_c_sarl },
+
+#ifdef TARGET_X86_64
+ [CC_OP_MULQ] = { compute_all_mulq, compute_c_mull },
+
+ [CC_OP_ADDQ] = { compute_all_addq, compute_c_addq },
+
+ [CC_OP_ADCQ] = { compute_all_adcq, compute_c_adcq },
+
+ [CC_OP_SUBQ] = { compute_all_subq, compute_c_subq },
+
+ [CC_OP_SBBQ] = { compute_all_sbbq, compute_c_sbbq },
+
+ [CC_OP_LOGICQ] = { compute_all_logicq, compute_c_logicq },
+
+ [CC_OP_INCQ] = { compute_all_incq, compute_c_incl },
+
+ [CC_OP_DECQ] = { compute_all_decq, compute_c_incl },
+
+ [CC_OP_SHLQ] = { compute_all_shlq, compute_c_shlq },
+
+ [CC_OP_SARQ] = { compute_all_sarq, compute_c_sarl },
+#endif
+};
+
+/* floating point support. Some of the code for complicated x87
+ functions comes from the LGPL'ed x86 emulator found in the Willows
+ TWIN windows emulator. */
+
+/* fp load FT0 */
+
+void OPPROTO op_flds_FT0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = ldl(A0);
+ FT0 = FP_CONVERT.f;
+#else
+ FT0 = ldfl(A0);
+#endif
+}
+
+void OPPROTO op_fldl_FT0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i64 = ldq(A0);
+ FT0 = FP_CONVERT.d;
+#else
+ FT0 = ldfq(A0);
+#endif
+}
+
+/* helpers are needed to avoid static constant reference. XXX: find a better way */
+#ifdef USE_INT_TO_FLOAT_HELPERS
+
+void helper_fild_FT0_A0(void)
+{
+ FT0 = (CPU86_LDouble)ldsw(A0);
+}
+
+void helper_fildl_FT0_A0(void)
+{
+ FT0 = (CPU86_LDouble)((int32_t)ldl(A0));
+}
+
+void helper_fildll_FT0_A0(void)
+{
+ FT0 = (CPU86_LDouble)((int64_t)ldq(A0));
+}
+
+void OPPROTO op_fild_FT0_A0(void)
+{
+ helper_fild_FT0_A0();
+}
+
+void OPPROTO op_fildl_FT0_A0(void)
+{
+ helper_fildl_FT0_A0();
+}
+
+void OPPROTO op_fildll_FT0_A0(void)
+{
+ helper_fildll_FT0_A0();
+}
+
+#else
+
+void OPPROTO op_fild_FT0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = ldsw(A0);
+ FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
+ FT0 = (CPU86_LDouble)ldsw(A0);
+#endif
+}
+
+void OPPROTO op_fildl_FT0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = (int32_t) ldl(A0);
+ FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
+ FT0 = (CPU86_LDouble)((int32_t)ldl(A0));
+#endif
+}
+
+void OPPROTO op_fildll_FT0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i64 = (int64_t) ldq(A0);
+ FT0 = (CPU86_LDouble)FP_CONVERT.i64;
+#else
+ FT0 = (CPU86_LDouble)((int64_t)ldq(A0));
+#endif
+}
+#endif
+
+/* fp load ST0 */
+
+void OPPROTO op_flds_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = ldl(A0);
+ env->fpregs[new_fpstt].d = FP_CONVERT.f;
+#else
+ env->fpregs[new_fpstt].d = ldfl(A0);
+#endif
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void OPPROTO op_fldl_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i64 = ldq(A0);
+ env->fpregs[new_fpstt].d = FP_CONVERT.d;
+#else
+ env->fpregs[new_fpstt].d = ldfq(A0);
+#endif
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void OPPROTO op_fldt_ST0_A0(void)
+{
+ helper_fldt_ST0_A0();
+}
+
+/* helpers are needed to avoid static constant reference. XXX: find a better way */
+#ifdef USE_INT_TO_FLOAT_HELPERS
+
+void helper_fild_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0);
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void helper_fildl_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0));
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void helper_fildll_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0));
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void OPPROTO op_fild_ST0_A0(void)
+{
+ helper_fild_ST0_A0();
+}
+
+void OPPROTO op_fildl_ST0_A0(void)
+{
+ helper_fildl_ST0_A0();
+}
+
+void OPPROTO op_fildll_ST0_A0(void)
+{
+ helper_fildll_ST0_A0();
+}
+
+#else
+
+void OPPROTO op_fild_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = ldsw(A0);
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32;
+#else
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0);
+#endif
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void OPPROTO op_fildl_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i32 = (int32_t) ldl(A0);
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32;
+#else
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0));
+#endif
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void OPPROTO op_fildll_ST0_A0(void)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.i64 = (int64_t) ldq(A0);
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i64;
+#else
+ env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0));
+#endif
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+#endif
+
+/* fp store */
+
+void OPPROTO op_fsts_ST0_A0(void)
+{
+#ifdef USE_FP_CONVERT
+ FP_CONVERT.f = (float)ST0;
+ stfl(A0, FP_CONVERT.f);
+#else
+ stfl(A0, (float)ST0);
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_fstl_ST0_A0(void)
+{
+ stfq(A0, (double)ST0);
+ FORCE_RET();
+}
+
+void OPPROTO op_fstt_ST0_A0(void)
+{
+ helper_fstt_ST0_A0();
+}
+
+void OPPROTO op_fist_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int val;
+
+ d = ST0;
+ val = floatx_to_int32(d, &env->fp_status);
+ if (val != (int16_t)val)
+ val = -32768;
+ stw(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fistl_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int val;
+
+ d = ST0;
+ val = floatx_to_int32(d, &env->fp_status);
+ stl(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fistll_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int64_t val;
+
+ d = ST0;
+ val = floatx_to_int64(d, &env->fp_status);
+ stq(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fistt_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int val;
+
+ d = ST0;
+ val = floatx_to_int32_round_to_zero(d, &env->fp_status);
+ if (val != (int16_t)val)
+ val = -32768;
+ stw(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fisttl_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int val;
+
+ d = ST0;
+ val = floatx_to_int32_round_to_zero(d, &env->fp_status);
+ stl(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fisttll_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+ register CPU86_LDouble d asm("o0");
+#else
+ CPU86_LDouble d;
+#endif
+ int64_t val;
+
+ d = ST0;
+ val = floatx_to_int64_round_to_zero(d, &env->fp_status);
+ stq(A0, val);
+ FORCE_RET();
+}
+
+void OPPROTO op_fbld_ST0_A0(void)
+{
+ helper_fbld_ST0_A0();
+}
+
+void OPPROTO op_fbst_ST0_A0(void)
+{
+ helper_fbst_ST0_A0();
+}
+
+/* FPU move */
+
+void OPPROTO op_fpush(void)
+{
+ fpush();
+}
+
+void OPPROTO op_fpop(void)
+{
+ fpop();
+}
+
+void OPPROTO op_fdecstp(void)
+{
+ env->fpstt = (env->fpstt - 1) & 7;
+ env->fpus &= (~0x4700);
+}
+
+void OPPROTO op_fincstp(void)
+{
+ env->fpstt = (env->fpstt + 1) & 7;
+ env->fpus &= (~0x4700);
+}
+
+void OPPROTO op_ffree_STN(void)
+{
+ env->fptags[(env->fpstt + PARAM1) & 7] = 1;
+}
+
+void OPPROTO op_fmov_ST0_FT0(void)
+{
+ ST0 = FT0;
+}
+
+void OPPROTO op_fmov_FT0_STN(void)
+{
+ FT0 = ST(PARAM1);
+}
+
+void OPPROTO op_fmov_ST0_STN(void)
+{
+ ST0 = ST(PARAM1);
+}
+
+void OPPROTO op_fmov_STN_ST0(void)
+{
+ ST(PARAM1) = ST0;
+}
+
+void OPPROTO op_fxchg_ST0_STN(void)
+{
+ CPU86_LDouble tmp;
+ tmp = ST(PARAM1);
+ ST(PARAM1) = ST0;
+ ST0 = tmp;
+}
+
+/* FPU operations */
+
+const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
+
+void OPPROTO op_fcom_ST0_FT0(void)
+{
+ int ret;
+
+ ret = floatx_compare(ST0, FT0, &env->fp_status);
+ env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
+ FORCE_RET();
+}
+
+void OPPROTO op_fucom_ST0_FT0(void)
+{
+ int ret;
+
+ ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+ env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
+ FORCE_RET();
+}
+
+const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
+
+void OPPROTO op_fcomi_ST0_FT0(void)
+{
+ int eflags;
+ int ret;
+
+ ret = floatx_compare(ST0, FT0, &env->fp_status);
+ eflags = cc_table[CC_OP].compute_all();
+ eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+void OPPROTO op_fucomi_ST0_FT0(void)
+{
+ int eflags;
+ int ret;
+
+ ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+ eflags = cc_table[CC_OP].compute_all();
+ eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
+ CC_SRC = eflags;
+ FORCE_RET();
+}
+
+void OPPROTO op_fcmov_ST0_STN_T0(void)
+{
+ if (T0) {
+ ST0 = ST(PARAM1);
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_fadd_ST0_FT0(void)
+{
+ ST0 += FT0;
+}
+
+void OPPROTO op_fmul_ST0_FT0(void)
+{
+ ST0 *= FT0;
+}
+
+void OPPROTO op_fsub_ST0_FT0(void)
+{
+ ST0 -= FT0;
+}
+
+void OPPROTO op_fsubr_ST0_FT0(void)
+{
+ ST0 = FT0 - ST0;
+}
+
+void OPPROTO op_fdiv_ST0_FT0(void)
+{
+ ST0 = helper_fdiv(ST0, FT0);
+}
+
+void OPPROTO op_fdivr_ST0_FT0(void)
+{
+ ST0 = helper_fdiv(FT0, ST0);
+}
+
+/* fp operations between STN and ST0 */
+
+void OPPROTO op_fadd_STN_ST0(void)
+{
+ ST(PARAM1) += ST0;
+}
+
+void OPPROTO op_fmul_STN_ST0(void)
+{
+ ST(PARAM1) *= ST0;
+}
+
+void OPPROTO op_fsub_STN_ST0(void)
+{
+ ST(PARAM1) -= ST0;
+}
+
+void OPPROTO op_fsubr_STN_ST0(void)
+{
+ CPU86_LDouble *p;
+ p = &ST(PARAM1);
+ *p = ST0 - *p;
+}
+
+void OPPROTO op_fdiv_STN_ST0(void)
+{
+ CPU86_LDouble *p;
+ p = &ST(PARAM1);
+ *p = helper_fdiv(*p, ST0);
+}
+
+void OPPROTO op_fdivr_STN_ST0(void)
+{
+ CPU86_LDouble *p;
+ p = &ST(PARAM1);
+ *p = helper_fdiv(ST0, *p);
+}
+
+/* misc FPU operations */
+void OPPROTO op_fchs_ST0(void)
+{
+ ST0 = floatx_chs(ST0);
+}
+
+void OPPROTO op_fabs_ST0(void)
+{
+ ST0 = floatx_abs(ST0);
+}
+
+void OPPROTO op_fxam_ST0(void)
+{
+ helper_fxam_ST0();
+}
+
+void OPPROTO op_fld1_ST0(void)
+{
+ ST0 = f15rk[1];
+}
+
+void OPPROTO op_fldl2t_ST0(void)
+{
+ ST0 = f15rk[6];
+}
+
+void OPPROTO op_fldl2e_ST0(void)
+{
+ ST0 = f15rk[5];
+}
+
+void OPPROTO op_fldpi_ST0(void)
+{
+ ST0 = f15rk[2];
+}
+
+void OPPROTO op_fldlg2_ST0(void)
+{
+ ST0 = f15rk[3];
+}
+
+void OPPROTO op_fldln2_ST0(void)
+{
+ ST0 = f15rk[4];
+}
+
+void OPPROTO op_fldz_ST0(void)
+{
+ ST0 = f15rk[0];
+}
+
+void OPPROTO op_fldz_FT0(void)
+{
+ FT0 = f15rk[0];
+}
+
+/* associated heplers to reduce generated code length and to simplify
+ relocation (FP constants are usually stored in .rodata section) */
+
+void OPPROTO op_f2xm1(void)
+{
+ helper_f2xm1();
+}
+
+void OPPROTO op_fyl2x(void)
+{
+ helper_fyl2x();
+}
+
+void OPPROTO op_fptan(void)
+{
+ helper_fptan();
+}
+
+void OPPROTO op_fpatan(void)
+{
+ helper_fpatan();
+}
+
+void OPPROTO op_fxtract(void)
+{
+ helper_fxtract();
+}
+
+void OPPROTO op_fprem1(void)
+{
+ helper_fprem1();
+}
+
+
+void OPPROTO op_fprem(void)
+{
+ helper_fprem();
+}
+
+void OPPROTO op_fyl2xp1(void)
+{
+ helper_fyl2xp1();
+}
+
+void OPPROTO op_fsqrt(void)
+{
+ helper_fsqrt();
+}
+
+void OPPROTO op_fsincos(void)
+{
+ helper_fsincos();
+}
+
+void OPPROTO op_frndint(void)
+{
+ helper_frndint();
+}
+
+void OPPROTO op_fscale(void)
+{
+ helper_fscale();
+}
+
+void OPPROTO op_fsin(void)
+{
+ helper_fsin();
+}
+
+void OPPROTO op_fcos(void)
+{
+ helper_fcos();
+}
+
+void OPPROTO op_fnstsw_A0(void)
+{
+ int fpus;
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ stw(A0, fpus);
+ FORCE_RET();
+}
+
+void OPPROTO op_fnstsw_EAX(void)
+{
+ int fpus;
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ EAX = (EAX & ~0xffff) | fpus;
+}
+
+void OPPROTO op_fnstcw_A0(void)
+{
+ stw(A0, env->fpuc);
+ FORCE_RET();
+}
+
+void OPPROTO op_fldcw_A0(void)
+{
+ env->fpuc = lduw(A0);
+ update_fp_status();
+}
+
+void OPPROTO op_fclex(void)
+{
+ env->fpus &= 0x7f00;
+}
+
+void OPPROTO op_fwait(void)
+{
+ if (env->fpus & FPUS_SE)
+ fpu_raise_exception();
+ FORCE_RET();
+}
+
+void OPPROTO op_fninit(void)
+{
+ env->fpus = 0;
+ env->fpstt = 0;
+ env->fpuc = 0x37f;
+ env->fptags[0] = 1;
+ env->fptags[1] = 1;
+ env->fptags[2] = 1;
+ env->fptags[3] = 1;
+ env->fptags[4] = 1;
+ env->fptags[5] = 1;
+ env->fptags[6] = 1;
+ env->fptags[7] = 1;
+}
+
+void OPPROTO op_fnstenv_A0(void)
+{
+ helper_fstenv(A0, PARAM1);
+}
+
+void OPPROTO op_fldenv_A0(void)
+{
+ helper_fldenv(A0, PARAM1);
+}
+
+void OPPROTO op_fnsave_A0(void)
+{
+ helper_fsave(A0, PARAM1);
+}
+
+void OPPROTO op_frstor_A0(void)
+{
+ helper_frstor(A0, PARAM1);
+}
+
+/* threading support */
+void OPPROTO op_lock(void)
+{
+ cpu_lock();
+}
+
+void OPPROTO op_unlock(void)
+{
+ cpu_unlock();
+}
+
+/* SSE support */
+static inline void memcpy16(void *d, void *s)
+{
+ ((uint32_t *)d)[0] = ((uint32_t *)s)[0];
+ ((uint32_t *)d)[1] = ((uint32_t *)s)[1];
+ ((uint32_t *)d)[2] = ((uint32_t *)s)[2];
+ ((uint32_t *)d)[3] = ((uint32_t *)s)[3];
+}
+
+void OPPROTO op_movo(void)
+{
+ /* XXX: badly generated code */
+ XMMReg *d, *s;
+ d = (XMMReg *)((char *)env + PARAM1);
+ s = (XMMReg *)((char *)env + PARAM2);
+ memcpy16(d, s);
+}
+
+void OPPROTO op_movq(void)
+{
+ uint64_t *d, *s;
+ d = (uint64_t *)((char *)env + PARAM1);
+ s = (uint64_t *)((char *)env + PARAM2);
+ *d = *s;
+}
+
+void OPPROTO op_movl(void)
+{
+ uint32_t *d, *s;
+ d = (uint32_t *)((char *)env + PARAM1);
+ s = (uint32_t *)((char *)env + PARAM2);
+ *d = *s;
+}
+
+void OPPROTO op_movq_env_0(void)
+{
+ uint64_t *d;
+ d = (uint64_t *)((char *)env + PARAM1);
+ *d = 0;
+}
+
+void OPPROTO op_fxsave_A0(void)
+{
+ helper_fxsave(A0, PARAM1);
+}
+
+void OPPROTO op_fxrstor_A0(void)
+{
+ helper_fxrstor(A0, PARAM1);
+}
+
+/* XXX: optimize by storing fptt and fptags in the static cpu state */
+void OPPROTO op_enter_mmx(void)
+{
+ env->fpstt = 0;
+ *(uint32_t *)(env->fptags) = 0;
+ *(uint32_t *)(env->fptags + 4) = 0;
+}
+
+void OPPROTO op_emms(void)
+{
+ /* set to empty state */
+ *(uint32_t *)(env->fptags) = 0x01010101;
+ *(uint32_t *)(env->fptags + 4) = 0x01010101;
+}
+
+#define SHIFT 0
+#include "ops_sse.h"
+
+#define SHIFT 1
+#include "ops_sse.h"
diff --git a/target-i386/opreg_template.h b/target-i386/opreg_template.h
new file mode 100644
index 0000000..6480636
--- /dev/null
+++ b/target-i386/opreg_template.h
@@ -0,0 +1,190 @@
+/*
+ * i386 micro operations (templates for various register related
+ * operations)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+void OPPROTO glue(op_movl_A0,REGNAME)(void)
+{
+ A0 = (uint32_t)REG;
+}
+
+void OPPROTO glue(op_addl_A0,REGNAME)(void)
+{
+ A0 = (uint32_t)(A0 + REG);
+}
+
+void OPPROTO glue(glue(op_addl_A0,REGNAME),_s1)(void)
+{
+ A0 = (uint32_t)(A0 + (REG << 1));
+}
+
+void OPPROTO glue(glue(op_addl_A0,REGNAME),_s2)(void)
+{
+ A0 = (uint32_t)(A0 + (REG << 2));
+}
+
+void OPPROTO glue(glue(op_addl_A0,REGNAME),_s3)(void)
+{
+ A0 = (uint32_t)(A0 + (REG << 3));
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO glue(op_movq_A0,REGNAME)(void)
+{
+ A0 = REG;
+}
+
+void OPPROTO glue(op_addq_A0,REGNAME)(void)
+{
+ A0 = (A0 + REG);
+}
+
+void OPPROTO glue(glue(op_addq_A0,REGNAME),_s1)(void)
+{
+ A0 = (A0 + (REG << 1));
+}
+
+void OPPROTO glue(glue(op_addq_A0,REGNAME),_s2)(void)
+{
+ A0 = (A0 + (REG << 2));
+}
+
+void OPPROTO glue(glue(op_addq_A0,REGNAME),_s3)(void)
+{
+ A0 = (A0 + (REG << 3));
+}
+#endif
+
+void OPPROTO glue(op_movl_T0,REGNAME)(void)
+{
+ T0 = REG;
+}
+
+void OPPROTO glue(op_movl_T1,REGNAME)(void)
+{
+ T1 = REG;
+}
+
+void OPPROTO glue(op_movh_T0,REGNAME)(void)
+{
+ T0 = REG >> 8;
+}
+
+void OPPROTO glue(op_movh_T1,REGNAME)(void)
+{
+ T1 = REG >> 8;
+}
+
+void OPPROTO glue(glue(op_movl,REGNAME),_T0)(void)
+{
+ REG = (uint32_t)T0;
+}
+
+void OPPROTO glue(glue(op_movl,REGNAME),_T1)(void)
+{
+ REG = (uint32_t)T1;
+}
+
+void OPPROTO glue(glue(op_movl,REGNAME),_A0)(void)
+{
+ REG = (uint32_t)A0;
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO glue(glue(op_movq,REGNAME),_T0)(void)
+{
+ REG = T0;
+}
+
+void OPPROTO glue(glue(op_movq,REGNAME),_T1)(void)
+{
+ REG = T1;
+}
+
+void OPPROTO glue(glue(op_movq,REGNAME),_A0)(void)
+{
+ REG = A0;
+}
+#endif
+
+/* mov T1 to REG if T0 is true */
+void OPPROTO glue(glue(op_cmovw,REGNAME),_T1_T0)(void)
+{
+ if (T0)
+ REG = (REG & ~0xffff) | (T1 & 0xffff);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_cmovl,REGNAME),_T1_T0)(void)
+{
+ if (T0)
+ REG = (uint32_t)T1;
+ FORCE_RET();
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO glue(glue(op_cmovq,REGNAME),_T1_T0)(void)
+{
+ if (T0)
+ REG = T1;
+ FORCE_RET();
+}
+#endif
+
+/* NOTE: T0 high order bits are ignored */
+void OPPROTO glue(glue(op_movw,REGNAME),_T0)(void)
+{
+ REG = (REG & ~0xffff) | (T0 & 0xffff);
+}
+
+/* NOTE: T0 high order bits are ignored */
+void OPPROTO glue(glue(op_movw,REGNAME),_T1)(void)
+{
+ REG = (REG & ~0xffff) | (T1 & 0xffff);
+}
+
+/* NOTE: A0 high order bits are ignored */
+void OPPROTO glue(glue(op_movw,REGNAME),_A0)(void)
+{
+ REG = (REG & ~0xffff) | (A0 & 0xffff);
+}
+
+/* NOTE: T0 high order bits are ignored */
+void OPPROTO glue(glue(op_movb,REGNAME),_T0)(void)
+{
+ REG = (REG & ~0xff) | (T0 & 0xff);
+}
+
+/* NOTE: T0 high order bits are ignored */
+void OPPROTO glue(glue(op_movh,REGNAME),_T0)(void)
+{
+ REG = (REG & ~0xff00) | ((T0 & 0xff) << 8);
+}
+
+/* NOTE: T1 high order bits are ignored */
+void OPPROTO glue(glue(op_movb,REGNAME),_T1)(void)
+{
+ REG = (REG & ~0xff) | (T1 & 0xff);
+}
+
+/* NOTE: T1 high order bits are ignored */
+void OPPROTO glue(glue(op_movh,REGNAME),_T1)(void)
+{
+ REG = (REG & ~0xff00) | ((T1 & 0xff) << 8);
+}
+
diff --git a/target-i386/ops_mem.h b/target-i386/ops_mem.h
new file mode 100644
index 0000000..7ec84dd
--- /dev/null
+++ b/target-i386/ops_mem.h
@@ -0,0 +1,156 @@
+void OPPROTO glue(glue(op_ldub, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = glue(ldub, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldsb, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = glue(ldsb, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_lduw, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = glue(lduw, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldsw, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = glue(ldsw, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldl, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = (uint32_t)glue(ldl, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldub, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = glue(ldub, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldsb, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = glue(ldsb, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_lduw, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = glue(lduw, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldsw, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = glue(ldsw, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldl, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = (uint32_t)glue(ldl, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_stb, MEMSUFFIX), _T0_A0)(void)
+{
+ glue(stb, MEMSUFFIX)(A0, T0);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_stw, MEMSUFFIX), _T0_A0)(void)
+{
+ glue(stw, MEMSUFFIX)(A0, T0);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_stl, MEMSUFFIX), _T0_A0)(void)
+{
+ glue(stl, MEMSUFFIX)(A0, T0);
+ FORCE_RET();
+}
+
+#if 0
+void OPPROTO glue(glue(op_stb, MEMSUFFIX), _T1_A0)(void)
+{
+ glue(stb, MEMSUFFIX)(A0, T1);
+ FORCE_RET();
+}
+#endif
+
+void OPPROTO glue(glue(op_stw, MEMSUFFIX), _T1_A0)(void)
+{
+ glue(stw, MEMSUFFIX)(A0, T1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_stl, MEMSUFFIX), _T1_A0)(void)
+{
+ glue(stl, MEMSUFFIX)(A0, T1);
+ FORCE_RET();
+}
+
+/* SSE/MMX support */
+void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _env_A0)(void)
+{
+ uint64_t *p;
+ p = (uint64_t *)((char *)env + PARAM1);
+ *p = glue(ldq, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_stq, MEMSUFFIX), _env_A0)(void)
+{
+ uint64_t *p;
+ p = (uint64_t *)((char *)env + PARAM1);
+ glue(stq, MEMSUFFIX)(A0, *p);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_ldo, MEMSUFFIX), _env_A0)(void)
+{
+ XMMReg *p;
+ p = (XMMReg *)((char *)env + PARAM1);
+ p->XMM_Q(0) = glue(ldq, MEMSUFFIX)(A0);
+ p->XMM_Q(1) = glue(ldq, MEMSUFFIX)(A0 + 8);
+}
+
+void OPPROTO glue(glue(op_sto, MEMSUFFIX), _env_A0)(void)
+{
+ XMMReg *p;
+ p = (XMMReg *)((char *)env + PARAM1);
+ glue(stq, MEMSUFFIX)(A0, p->XMM_Q(0));
+ glue(stq, MEMSUFFIX)(A0 + 8, p->XMM_Q(1));
+ FORCE_RET();
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO glue(glue(op_ldsl, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = (int32_t)glue(ldl, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldsl, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = (int32_t)glue(ldl, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _T0_A0)(void)
+{
+ T0 = glue(ldq, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _T1_A0)(void)
+{
+ T1 = glue(ldq, MEMSUFFIX)(A0);
+}
+
+void OPPROTO glue(glue(op_stq, MEMSUFFIX), _T0_A0)(void)
+{
+ glue(stq, MEMSUFFIX)(A0, T0);
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_stq, MEMSUFFIX), _T1_A0)(void)
+{
+ glue(stq, MEMSUFFIX)(A0, T1);
+ FORCE_RET();
+}
+#endif
+
+#undef MEMSUFFIX
diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h
new file mode 100644
index 0000000..cdc3801
--- /dev/null
+++ b/target-i386/ops_sse.h
@@ -0,0 +1,1374 @@
+/*
+ * MMX/SSE/SSE2/PNI support
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if SHIFT == 0
+#define Reg MMXReg
+#define XMM_ONLY(x...)
+#define B(n) MMX_B(n)
+#define W(n) MMX_W(n)
+#define L(n) MMX_L(n)
+#define Q(n) q
+#define SUFFIX _mmx
+#else
+#define Reg XMMReg
+#define XMM_ONLY(x...) x
+#define B(n) XMM_B(n)
+#define W(n) XMM_W(n)
+#define L(n) XMM_L(n)
+#define Q(n) XMM_Q(n)
+#define SUFFIX _xmm
+#endif
+
+void OPPROTO glue(op_psrlw, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 15) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->W(0) >>= shift;
+ d->W(1) >>= shift;
+ d->W(2) >>= shift;
+ d->W(3) >>= shift;
+#if SHIFT == 1
+ d->W(4) >>= shift;
+ d->W(5) >>= shift;
+ d->W(6) >>= shift;
+ d->W(7) >>= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_psraw, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 15) {
+ shift = 15;
+ } else {
+ shift = s->B(0);
+ }
+ d->W(0) = (int16_t)d->W(0) >> shift;
+ d->W(1) = (int16_t)d->W(1) >> shift;
+ d->W(2) = (int16_t)d->W(2) >> shift;
+ d->W(3) = (int16_t)d->W(3) >> shift;
+#if SHIFT == 1
+ d->W(4) = (int16_t)d->W(4) >> shift;
+ d->W(5) = (int16_t)d->W(5) >> shift;
+ d->W(6) = (int16_t)d->W(6) >> shift;
+ d->W(7) = (int16_t)d->W(7) >> shift;
+#endif
+}
+
+void OPPROTO glue(op_psllw, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 15) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->W(0) <<= shift;
+ d->W(1) <<= shift;
+ d->W(2) <<= shift;
+ d->W(3) <<= shift;
+#if SHIFT == 1
+ d->W(4) <<= shift;
+ d->W(5) <<= shift;
+ d->W(6) <<= shift;
+ d->W(7) <<= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_psrld, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 31) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->L(0) >>= shift;
+ d->L(1) >>= shift;
+#if SHIFT == 1
+ d->L(2) >>= shift;
+ d->L(3) >>= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_psrad, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 31) {
+ shift = 31;
+ } else {
+ shift = s->B(0);
+ }
+ d->L(0) = (int32_t)d->L(0) >> shift;
+ d->L(1) = (int32_t)d->L(1) >> shift;
+#if SHIFT == 1
+ d->L(2) = (int32_t)d->L(2) >> shift;
+ d->L(3) = (int32_t)d->L(3) >> shift;
+#endif
+}
+
+void OPPROTO glue(op_pslld, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 31) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->L(0) <<= shift;
+ d->L(1) <<= shift;
+#if SHIFT == 1
+ d->L(2) <<= shift;
+ d->L(3) <<= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_psrlq, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 63) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->Q(0) >>= shift;
+#if SHIFT == 1
+ d->Q(1) >>= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_psllq, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ if (s->Q(0) > 63) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->Q(0) <<= shift;
+#if SHIFT == 1
+ d->Q(1) <<= shift;
+#endif
+ }
+ FORCE_RET();
+}
+
+#if SHIFT == 1
+void OPPROTO glue(op_psrldq, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift, i;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ shift = s->L(0);
+ if (shift > 16)
+ shift = 16;
+ for(i = 0; i < 16 - shift; i++)
+ d->B(i) = d->B(i + shift);
+ for(i = 16 - shift; i < 16; i++)
+ d->B(i) = 0;
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_pslldq, SUFFIX)(void)
+{
+ Reg *d, *s;
+ int shift, i;
+
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ shift = s->L(0);
+ if (shift > 16)
+ shift = 16;
+ for(i = 15; i >= shift; i--)
+ d->B(i) = d->B(i - shift);
+ for(i = 0; i < shift; i++)
+ d->B(i) = 0;
+ FORCE_RET();
+}
+#endif
+
+#define SSE_OP_B(name, F)\
+void OPPROTO glue(name, SUFFIX) (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->B(0) = F(d->B(0), s->B(0));\
+ d->B(1) = F(d->B(1), s->B(1));\
+ d->B(2) = F(d->B(2), s->B(2));\
+ d->B(3) = F(d->B(3), s->B(3));\
+ d->B(4) = F(d->B(4), s->B(4));\
+ d->B(5) = F(d->B(5), s->B(5));\
+ d->B(6) = F(d->B(6), s->B(6));\
+ d->B(7) = F(d->B(7), s->B(7));\
+ XMM_ONLY(\
+ d->B(8) = F(d->B(8), s->B(8));\
+ d->B(9) = F(d->B(9), s->B(9));\
+ d->B(10) = F(d->B(10), s->B(10));\
+ d->B(11) = F(d->B(11), s->B(11));\
+ d->B(12) = F(d->B(12), s->B(12));\
+ d->B(13) = F(d->B(13), s->B(13));\
+ d->B(14) = F(d->B(14), s->B(14));\
+ d->B(15) = F(d->B(15), s->B(15));\
+ )\
+}
+
+#define SSE_OP_W(name, F)\
+void OPPROTO glue(name, SUFFIX) (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->W(0) = F(d->W(0), s->W(0));\
+ d->W(1) = F(d->W(1), s->W(1));\
+ d->W(2) = F(d->W(2), s->W(2));\
+ d->W(3) = F(d->W(3), s->W(3));\
+ XMM_ONLY(\
+ d->W(4) = F(d->W(4), s->W(4));\
+ d->W(5) = F(d->W(5), s->W(5));\
+ d->W(6) = F(d->W(6), s->W(6));\
+ d->W(7) = F(d->W(7), s->W(7));\
+ )\
+}
+
+#define SSE_OP_L(name, F)\
+void OPPROTO glue(name, SUFFIX) (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->L(0) = F(d->L(0), s->L(0));\
+ d->L(1) = F(d->L(1), s->L(1));\
+ XMM_ONLY(\
+ d->L(2) = F(d->L(2), s->L(2));\
+ d->L(3) = F(d->L(3), s->L(3));\
+ )\
+}
+
+#define SSE_OP_Q(name, F)\
+void OPPROTO glue(name, SUFFIX) (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->Q(0) = F(d->Q(0), s->Q(0));\
+ XMM_ONLY(\
+ d->Q(1) = F(d->Q(1), s->Q(1));\
+ )\
+}
+
+#if SHIFT == 0
+static inline int satub(int x)
+{
+ if (x < 0)
+ return 0;
+ else if (x > 255)
+ return 255;
+ else
+ return x;
+}
+
+static inline int satuw(int x)
+{
+ if (x < 0)
+ return 0;
+ else if (x > 65535)
+ return 65535;
+ else
+ return x;
+}
+
+static inline int satsb(int x)
+{
+ if (x < -128)
+ return -128;
+ else if (x > 127)
+ return 127;
+ else
+ return x;
+}
+
+static inline int satsw(int x)
+{
+ if (x < -32768)
+ return -32768;
+ else if (x > 32767)
+ return 32767;
+ else
+ return x;
+}
+
+#define FADD(a, b) ((a) + (b))
+#define FADDUB(a, b) satub((a) + (b))
+#define FADDUW(a, b) satuw((a) + (b))
+#define FADDSB(a, b) satsb((int8_t)(a) + (int8_t)(b))
+#define FADDSW(a, b) satsw((int16_t)(a) + (int16_t)(b))
+
+#define FSUB(a, b) ((a) - (b))
+#define FSUBUB(a, b) satub((a) - (b))
+#define FSUBUW(a, b) satuw((a) - (b))
+#define FSUBSB(a, b) satsb((int8_t)(a) - (int8_t)(b))
+#define FSUBSW(a, b) satsw((int16_t)(a) - (int16_t)(b))
+#define FMINUB(a, b) ((a) < (b)) ? (a) : (b)
+#define FMINSW(a, b) ((int16_t)(a) < (int16_t)(b)) ? (a) : (b)
+#define FMAXUB(a, b) ((a) > (b)) ? (a) : (b)
+#define FMAXSW(a, b) ((int16_t)(a) > (int16_t)(b)) ? (a) : (b)
+
+#define FAND(a, b) (a) & (b)
+#define FANDN(a, b) ((~(a)) & (b))
+#define FOR(a, b) (a) | (b)
+#define FXOR(a, b) (a) ^ (b)
+
+#define FCMPGTB(a, b) (int8_t)(a) > (int8_t)(b) ? -1 : 0
+#define FCMPGTW(a, b) (int16_t)(a) > (int16_t)(b) ? -1 : 0
+#define FCMPGTL(a, b) (int32_t)(a) > (int32_t)(b) ? -1 : 0
+#define FCMPEQ(a, b) (a) == (b) ? -1 : 0
+
+#define FMULLW(a, b) (a) * (b)
+#define FMULHUW(a, b) (a) * (b) >> 16
+#define FMULHW(a, b) (int16_t)(a) * (int16_t)(b) >> 16
+
+#define FAVG(a, b) ((a) + (b) + 1) >> 1
+#endif
+
+SSE_OP_B(op_paddb, FADD)
+SSE_OP_W(op_paddw, FADD)
+SSE_OP_L(op_paddl, FADD)
+SSE_OP_Q(op_paddq, FADD)
+
+SSE_OP_B(op_psubb, FSUB)
+SSE_OP_W(op_psubw, FSUB)
+SSE_OP_L(op_psubl, FSUB)
+SSE_OP_Q(op_psubq, FSUB)
+
+SSE_OP_B(op_paddusb, FADDUB)
+SSE_OP_B(op_paddsb, FADDSB)
+SSE_OP_B(op_psubusb, FSUBUB)
+SSE_OP_B(op_psubsb, FSUBSB)
+
+SSE_OP_W(op_paddusw, FADDUW)
+SSE_OP_W(op_paddsw, FADDSW)
+SSE_OP_W(op_psubusw, FSUBUW)
+SSE_OP_W(op_psubsw, FSUBSW)
+
+SSE_OP_B(op_pminub, FMINUB)
+SSE_OP_B(op_pmaxub, FMAXUB)
+
+SSE_OP_W(op_pminsw, FMINSW)
+SSE_OP_W(op_pmaxsw, FMAXSW)
+
+SSE_OP_Q(op_pand, FAND)
+SSE_OP_Q(op_pandn, FANDN)
+SSE_OP_Q(op_por, FOR)
+SSE_OP_Q(op_pxor, FXOR)
+
+SSE_OP_B(op_pcmpgtb, FCMPGTB)
+SSE_OP_W(op_pcmpgtw, FCMPGTW)
+SSE_OP_L(op_pcmpgtl, FCMPGTL)
+
+SSE_OP_B(op_pcmpeqb, FCMPEQ)
+SSE_OP_W(op_pcmpeqw, FCMPEQ)
+SSE_OP_L(op_pcmpeql, FCMPEQ)
+
+SSE_OP_W(op_pmullw, FMULLW)
+SSE_OP_W(op_pmulhuw, FMULHUW)
+SSE_OP_W(op_pmulhw, FMULHW)
+
+SSE_OP_B(op_pavgb, FAVG)
+SSE_OP_W(op_pavgw, FAVG)
+
+void OPPROTO glue(op_pmuludq, SUFFIX) (void)
+{
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ d->Q(0) = (uint64_t)s->L(0) * (uint64_t)d->L(0);
+#if SHIFT == 1
+ d->Q(1) = (uint64_t)s->L(2) * (uint64_t)d->L(2);
+#endif
+}
+
+void OPPROTO glue(op_pmaddwd, SUFFIX) (void)
+{
+ int i;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ for(i = 0; i < (2 << SHIFT); i++) {
+ d->L(i) = (int16_t)s->W(2*i) * (int16_t)d->W(2*i) +
+ (int16_t)s->W(2*i+1) * (int16_t)d->W(2*i+1);
+ }
+ FORCE_RET();
+}
+
+#if SHIFT == 0
+static inline int abs1(int a)
+{
+ if (a < 0)
+ return -a;
+ else
+ return a;
+}
+#endif
+void OPPROTO glue(op_psadbw, SUFFIX) (void)
+{
+ unsigned int val;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ val = 0;
+ val += abs1(d->B(0) - s->B(0));
+ val += abs1(d->B(1) - s->B(1));
+ val += abs1(d->B(2) - s->B(2));
+ val += abs1(d->B(3) - s->B(3));
+ val += abs1(d->B(4) - s->B(4));
+ val += abs1(d->B(5) - s->B(5));
+ val += abs1(d->B(6) - s->B(6));
+ val += abs1(d->B(7) - s->B(7));
+ d->Q(0) = val;
+#if SHIFT == 1
+ val = 0;
+ val += abs1(d->B(8) - s->B(8));
+ val += abs1(d->B(9) - s->B(9));
+ val += abs1(d->B(10) - s->B(10));
+ val += abs1(d->B(11) - s->B(11));
+ val += abs1(d->B(12) - s->B(12));
+ val += abs1(d->B(13) - s->B(13));
+ val += abs1(d->B(14) - s->B(14));
+ val += abs1(d->B(15) - s->B(15));
+ d->Q(1) = val;
+#endif
+}
+
+void OPPROTO glue(op_maskmov, SUFFIX) (void)
+{
+ int i;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ for(i = 0; i < (8 << SHIFT); i++) {
+ if (s->B(i) & 0x80)
+ stb(A0 + i, d->B(i));
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_movl_mm_T0, SUFFIX) (void)
+{
+ Reg *d;
+ d = (Reg *)((char *)env + PARAM1);
+ d->L(0) = T0;
+ d->L(1) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+}
+
+void OPPROTO glue(op_movl_T0_mm, SUFFIX) (void)
+{
+ Reg *s;
+ s = (Reg *)((char *)env + PARAM1);
+ T0 = s->L(0);
+}
+
+#if SHIFT == 0
+void OPPROTO glue(op_pshufw, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.W(0) = s->W(order & 3);
+ r.W(1) = s->W((order >> 2) & 3);
+ r.W(2) = s->W((order >> 4) & 3);
+ r.W(3) = s->W((order >> 6) & 3);
+ *d = r;
+}
+#else
+void OPPROTO op_shufps(void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.L(0) = d->L(order & 3);
+ r.L(1) = d->L((order >> 2) & 3);
+ r.L(2) = s->L((order >> 4) & 3);
+ r.L(3) = s->L((order >> 6) & 3);
+ *d = r;
+}
+
+void OPPROTO op_shufpd(void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.Q(0) = d->Q(order & 1);
+ r.Q(1) = s->Q((order >> 1) & 1);
+ *d = r;
+}
+
+void OPPROTO glue(op_pshufd, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.L(0) = s->L(order & 3);
+ r.L(1) = s->L((order >> 2) & 3);
+ r.L(2) = s->L((order >> 4) & 3);
+ r.L(3) = s->L((order >> 6) & 3);
+ *d = r;
+}
+
+void OPPROTO glue(op_pshuflw, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.W(0) = s->W(order & 3);
+ r.W(1) = s->W((order >> 2) & 3);
+ r.W(2) = s->W((order >> 4) & 3);
+ r.W(3) = s->W((order >> 6) & 3);
+ r.Q(1) = s->Q(1);
+ *d = r;
+}
+
+void OPPROTO glue(op_pshufhw, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ int order;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ order = PARAM3;
+ r.Q(0) = s->Q(0);
+ r.W(4) = s->W(4 + (order & 3));
+ r.W(5) = s->W(4 + ((order >> 2) & 3));
+ r.W(6) = s->W(4 + ((order >> 4) & 3));
+ r.W(7) = s->W(4 + ((order >> 6) & 3));
+ *d = r;
+}
+#endif
+
+#if SHIFT == 1
+/* FPU ops */
+/* XXX: not accurate */
+
+#define SSE_OP_S(name, F)\
+void OPPROTO op_ ## name ## ps (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+ d->XMM_S(1) = F(32, d->XMM_S(1), s->XMM_S(1));\
+ d->XMM_S(2) = F(32, d->XMM_S(2), s->XMM_S(2));\
+ d->XMM_S(3) = F(32, d->XMM_S(3), s->XMM_S(3));\
+}\
+\
+void OPPROTO op_ ## name ## ss (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+}\
+void OPPROTO op_ ## name ## pd (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+ d->XMM_D(1) = F(64, d->XMM_D(1), s->XMM_D(1));\
+}\
+\
+void OPPROTO op_ ## name ## sd (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+}
+
+#define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status)
+#define FPU_SUB(size, a, b) float ## size ## _sub(a, b, &env->sse_status)
+#define FPU_MUL(size, a, b) float ## size ## _mul(a, b, &env->sse_status)
+#define FPU_DIV(size, a, b) float ## size ## _div(a, b, &env->sse_status)
+#define FPU_MIN(size, a, b) (a) < (b) ? (a) : (b)
+#define FPU_MAX(size, a, b) (a) > (b) ? (a) : (b)
+#define FPU_SQRT(size, a, b) float ## size ## _sqrt(b, &env->sse_status)
+
+SSE_OP_S(add, FPU_ADD)
+SSE_OP_S(sub, FPU_SUB)
+SSE_OP_S(mul, FPU_MUL)
+SSE_OP_S(div, FPU_DIV)
+SSE_OP_S(min, FPU_MIN)
+SSE_OP_S(max, FPU_MAX)
+SSE_OP_S(sqrt, FPU_SQRT)
+
+
+/* float to float conversions */
+void OPPROTO op_cvtps2pd(void)
+{
+ float32 s0, s1;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ s0 = s->XMM_S(0);
+ s1 = s->XMM_S(1);
+ d->XMM_D(0) = float32_to_float64(s0, &env->sse_status);
+ d->XMM_D(1) = float32_to_float64(s1, &env->sse_status);
+}
+
+void OPPROTO op_cvtpd2ps(void)
+{
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status);
+ d->XMM_S(1) = float64_to_float32(s->XMM_D(1), &env->sse_status);
+ d->Q(1) = 0;
+}
+
+void OPPROTO op_cvtss2sd(void)
+{
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ d->XMM_D(0) = float32_to_float64(s->XMM_S(0), &env->sse_status);
+}
+
+void OPPROTO op_cvtsd2ss(void)
+{
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+ d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status);
+}
+
+/* integer to float */
+void OPPROTO op_cvtdq2ps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = int32_to_float32(s->XMM_L(0), &env->sse_status);
+ d->XMM_S(1) = int32_to_float32(s->XMM_L(1), &env->sse_status);
+ d->XMM_S(2) = int32_to_float32(s->XMM_L(2), &env->sse_status);
+ d->XMM_S(3) = int32_to_float32(s->XMM_L(3), &env->sse_status);
+}
+
+void OPPROTO op_cvtdq2pd(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ int32_t l0, l1;
+ l0 = (int32_t)s->XMM_L(0);
+ l1 = (int32_t)s->XMM_L(1);
+ d->XMM_D(0) = int32_to_float64(l0, &env->sse_status);
+ d->XMM_D(1) = int32_to_float64(l1, &env->sse_status);
+}
+
+void OPPROTO op_cvtpi2ps(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ MMXReg *s = (MMXReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status);
+ d->XMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status);
+}
+
+void OPPROTO op_cvtpi2pd(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ MMXReg *s = (MMXReg *)((char *)env + PARAM2);
+ d->XMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status);
+ d->XMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status);
+}
+
+void OPPROTO op_cvtsi2ss(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ d->XMM_S(0) = int32_to_float32(T0, &env->sse_status);
+}
+
+void OPPROTO op_cvtsi2sd(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ d->XMM_D(0) = int32_to_float64(T0, &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_cvtsq2ss(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ d->XMM_S(0) = int64_to_float32(T0, &env->sse_status);
+}
+
+void OPPROTO op_cvtsq2sd(void)
+{
+ XMMReg *d = (Reg *)((char *)env + PARAM1);
+ d->XMM_D(0) = int64_to_float64(T0, &env->sse_status);
+}
+#endif
+
+/* float to integer */
+void OPPROTO op_cvtps2dq(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status);
+ d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status);
+ d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status);
+ d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status);
+}
+
+void OPPROTO op_cvtpd2dq(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status);
+ d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status);
+ d->XMM_Q(1) = 0;
+}
+
+void OPPROTO op_cvtps2pi(void)
+{
+ MMXReg *d = (MMXReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status);
+ d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status);
+}
+
+void OPPROTO op_cvtpd2pi(void)
+{
+ MMXReg *d = (MMXReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status);
+ d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status);
+}
+
+void OPPROTO op_cvtss2si(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float32_to_int32(s->XMM_S(0), &env->sse_status);
+}
+
+void OPPROTO op_cvtsd2si(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float64_to_int32(s->XMM_D(0), &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_cvtss2sq(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float32_to_int64(s->XMM_S(0), &env->sse_status);
+}
+
+void OPPROTO op_cvtsd2sq(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float64_to_int64(s->XMM_D(0), &env->sse_status);
+}
+#endif
+
+/* float to integer truncated */
+void OPPROTO op_cvttps2dq(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+ d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
+ d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status);
+ d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status);
+}
+
+void OPPROTO op_cvttpd2dq(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+ d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
+ d->XMM_Q(1) = 0;
+}
+
+void OPPROTO op_cvttps2pi(void)
+{
+ MMXReg *d = (MMXReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+ d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
+}
+
+void OPPROTO op_cvttpd2pi(void)
+{
+ MMXReg *d = (MMXReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+ d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
+}
+
+void OPPROTO op_cvttss2si(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+}
+
+void OPPROTO op_cvttsd2si(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+void OPPROTO op_cvttss2sq(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status);
+}
+
+void OPPROTO op_cvttsd2sq(void)
+{
+ XMMReg *s = (XMMReg *)((char *)env + PARAM1);
+ T0 = float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status);
+}
+#endif
+
+void OPPROTO op_rsqrtps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = approx_rsqrt(s->XMM_S(0));
+ d->XMM_S(1) = approx_rsqrt(s->XMM_S(1));
+ d->XMM_S(2) = approx_rsqrt(s->XMM_S(2));
+ d->XMM_S(3) = approx_rsqrt(s->XMM_S(3));
+}
+
+void OPPROTO op_rsqrtss(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = approx_rsqrt(s->XMM_S(0));
+}
+
+void OPPROTO op_rcpps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = approx_rcp(s->XMM_S(0));
+ d->XMM_S(1) = approx_rcp(s->XMM_S(1));
+ d->XMM_S(2) = approx_rcp(s->XMM_S(2));
+ d->XMM_S(3) = approx_rcp(s->XMM_S(3));
+}
+
+void OPPROTO op_rcpss(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = approx_rcp(s->XMM_S(0));
+}
+
+void OPPROTO op_haddps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ XMMReg r;
+ r.XMM_S(0) = d->XMM_S(0) + d->XMM_S(1);
+ r.XMM_S(1) = d->XMM_S(2) + d->XMM_S(3);
+ r.XMM_S(2) = s->XMM_S(0) + s->XMM_S(1);
+ r.XMM_S(3) = s->XMM_S(2) + s->XMM_S(3);
+ *d = r;
+}
+
+void OPPROTO op_haddpd(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ XMMReg r;
+ r.XMM_D(0) = d->XMM_D(0) + d->XMM_D(1);
+ r.XMM_D(1) = s->XMM_D(0) + s->XMM_D(1);
+ *d = r;
+}
+
+void OPPROTO op_hsubps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ XMMReg r;
+ r.XMM_S(0) = d->XMM_S(0) - d->XMM_S(1);
+ r.XMM_S(1) = d->XMM_S(2) - d->XMM_S(3);
+ r.XMM_S(2) = s->XMM_S(0) - s->XMM_S(1);
+ r.XMM_S(3) = s->XMM_S(2) - s->XMM_S(3);
+ *d = r;
+}
+
+void OPPROTO op_hsubpd(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ XMMReg r;
+ r.XMM_D(0) = d->XMM_D(0) - d->XMM_D(1);
+ r.XMM_D(1) = s->XMM_D(0) - s->XMM_D(1);
+ *d = r;
+}
+
+void OPPROTO op_addsubps(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_S(0) = d->XMM_S(0) - s->XMM_S(0);
+ d->XMM_S(1) = d->XMM_S(1) + s->XMM_S(1);
+ d->XMM_S(2) = d->XMM_S(2) - s->XMM_S(2);
+ d->XMM_S(3) = d->XMM_S(3) + s->XMM_S(3);
+}
+
+void OPPROTO op_addsubpd(void)
+{
+ XMMReg *d = (XMMReg *)((char *)env + PARAM1);
+ XMMReg *s = (XMMReg *)((char *)env + PARAM2);
+ d->XMM_D(0) = d->XMM_D(0) - s->XMM_D(0);
+ d->XMM_D(1) = d->XMM_D(1) + s->XMM_D(1);
+}
+
+/* XXX: unordered */
+#define SSE_OP_CMP(name, F)\
+void OPPROTO op_ ## name ## ps (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+ d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1));\
+ d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2));\
+ d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3));\
+}\
+\
+void OPPROTO op_ ## name ## ss (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+}\
+void OPPROTO op_ ## name ## pd (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+ d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1));\
+}\
+\
+void OPPROTO op_ ## name ## sd (void)\
+{\
+ Reg *d, *s;\
+ d = (Reg *)((char *)env + PARAM1);\
+ s = (Reg *)((char *)env + PARAM2);\
+ d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+}
+
+#define FPU_CMPEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? - 1 : 0
+#define FPU_CMPNEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPNLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPNLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? 0 : -1
+
+SSE_OP_CMP(cmpeq, FPU_CMPEQ)
+SSE_OP_CMP(cmplt, FPU_CMPLT)
+SSE_OP_CMP(cmple, FPU_CMPLE)
+SSE_OP_CMP(cmpunord, FPU_CMPUNORD)
+SSE_OP_CMP(cmpneq, FPU_CMPNEQ)
+SSE_OP_CMP(cmpnlt, FPU_CMPNLT)
+SSE_OP_CMP(cmpnle, FPU_CMPNLE)
+SSE_OP_CMP(cmpord, FPU_CMPORD)
+
+const int comis_eflags[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
+
+void OPPROTO op_ucomiss(void)
+{
+ int ret;
+ float32 s0, s1;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ s0 = d->XMM_S(0);
+ s1 = s->XMM_S(0);
+ ret = float32_compare_quiet(s0, s1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+ FORCE_RET();
+}
+
+void OPPROTO op_comiss(void)
+{
+ int ret;
+ float32 s0, s1;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ s0 = d->XMM_S(0);
+ s1 = s->XMM_S(0);
+ ret = float32_compare(s0, s1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+ FORCE_RET();
+}
+
+void OPPROTO op_ucomisd(void)
+{
+ int ret;
+ float64 d0, d1;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ d0 = d->XMM_D(0);
+ d1 = s->XMM_D(0);
+ ret = float64_compare_quiet(d0, d1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+ FORCE_RET();
+}
+
+void OPPROTO op_comisd(void)
+{
+ int ret;
+ float64 d0, d1;
+ Reg *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ d0 = d->XMM_D(0);
+ d1 = s->XMM_D(0);
+ ret = float64_compare(d0, d1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+ FORCE_RET();
+}
+
+void OPPROTO op_movmskps(void)
+{
+ int b0, b1, b2, b3;
+ Reg *s;
+ s = (Reg *)((char *)env + PARAM1);
+ b0 = s->XMM_L(0) >> 31;
+ b1 = s->XMM_L(1) >> 31;
+ b2 = s->XMM_L(2) >> 31;
+ b3 = s->XMM_L(3) >> 31;
+ T0 = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
+}
+
+void OPPROTO op_movmskpd(void)
+{
+ int b0, b1;
+ Reg *s;
+ s = (Reg *)((char *)env + PARAM1);
+ b0 = s->XMM_L(1) >> 31;
+ b1 = s->XMM_L(3) >> 31;
+ T0 = b0 | (b1 << 1);
+}
+
+#endif
+
+void OPPROTO glue(op_pmovmskb, SUFFIX)(void)
+{
+ Reg *s;
+ s = (Reg *)((char *)env + PARAM1);
+ T0 = 0;
+ T0 |= (s->XMM_B(0) >> 7);
+ T0 |= (s->XMM_B(1) >> 6) & 0x02;
+ T0 |= (s->XMM_B(2) >> 5) & 0x04;
+ T0 |= (s->XMM_B(3) >> 4) & 0x08;
+ T0 |= (s->XMM_B(4) >> 3) & 0x10;
+ T0 |= (s->XMM_B(5) >> 2) & 0x20;
+ T0 |= (s->XMM_B(6) >> 1) & 0x40;
+ T0 |= (s->XMM_B(7)) & 0x80;
+#if SHIFT == 1
+ T0 |= (s->XMM_B(8) << 1) & 0x0100;
+ T0 |= (s->XMM_B(9) << 2) & 0x0200;
+ T0 |= (s->XMM_B(10) << 3) & 0x0400;
+ T0 |= (s->XMM_B(11) << 4) & 0x0800;
+ T0 |= (s->XMM_B(12) << 5) & 0x1000;
+ T0 |= (s->XMM_B(13) << 6) & 0x2000;
+ T0 |= (s->XMM_B(14) << 7) & 0x4000;
+ T0 |= (s->XMM_B(15) << 8) & 0x8000;
+#endif
+}
+
+void OPPROTO glue(op_pinsrw, SUFFIX) (void)
+{
+ Reg *d = (Reg *)((char *)env + PARAM1);
+ int pos = PARAM2;
+
+ d->W(pos) = T0;
+}
+
+void OPPROTO glue(op_pextrw, SUFFIX) (void)
+{
+ Reg *s = (Reg *)((char *)env + PARAM1);
+ int pos = PARAM2;
+
+ T0 = s->W(pos);
+}
+
+void OPPROTO glue(op_packsswb, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ r.B(0) = satsb((int16_t)d->W(0));
+ r.B(1) = satsb((int16_t)d->W(1));
+ r.B(2) = satsb((int16_t)d->W(2));
+ r.B(3) = satsb((int16_t)d->W(3));
+#if SHIFT == 1
+ r.B(4) = satsb((int16_t)d->W(4));
+ r.B(5) = satsb((int16_t)d->W(5));
+ r.B(6) = satsb((int16_t)d->W(6));
+ r.B(7) = satsb((int16_t)d->W(7));
+#endif
+ r.B((4 << SHIFT) + 0) = satsb((int16_t)s->W(0));
+ r.B((4 << SHIFT) + 1) = satsb((int16_t)s->W(1));
+ r.B((4 << SHIFT) + 2) = satsb((int16_t)s->W(2));
+ r.B((4 << SHIFT) + 3) = satsb((int16_t)s->W(3));
+#if SHIFT == 1
+ r.B(12) = satsb((int16_t)s->W(4));
+ r.B(13) = satsb((int16_t)s->W(5));
+ r.B(14) = satsb((int16_t)s->W(6));
+ r.B(15) = satsb((int16_t)s->W(7));
+#endif
+ *d = r;
+}
+
+void OPPROTO glue(op_packuswb, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ r.B(0) = satub((int16_t)d->W(0));
+ r.B(1) = satub((int16_t)d->W(1));
+ r.B(2) = satub((int16_t)d->W(2));
+ r.B(3) = satub((int16_t)d->W(3));
+#if SHIFT == 1
+ r.B(4) = satub((int16_t)d->W(4));
+ r.B(5) = satub((int16_t)d->W(5));
+ r.B(6) = satub((int16_t)d->W(6));
+ r.B(7) = satub((int16_t)d->W(7));
+#endif
+ r.B((4 << SHIFT) + 0) = satub((int16_t)s->W(0));
+ r.B((4 << SHIFT) + 1) = satub((int16_t)s->W(1));
+ r.B((4 << SHIFT) + 2) = satub((int16_t)s->W(2));
+ r.B((4 << SHIFT) + 3) = satub((int16_t)s->W(3));
+#if SHIFT == 1
+ r.B(12) = satub((int16_t)s->W(4));
+ r.B(13) = satub((int16_t)s->W(5));
+ r.B(14) = satub((int16_t)s->W(6));
+ r.B(15) = satub((int16_t)s->W(7));
+#endif
+ *d = r;
+}
+
+void OPPROTO glue(op_packssdw, SUFFIX) (void)
+{
+ Reg r, *d, *s;
+ d = (Reg *)((char *)env + PARAM1);
+ s = (Reg *)((char *)env + PARAM2);
+
+ r.W(0) = satsw(d->L(0));
+ r.W(1) = satsw(d->L(1));
+#if SHIFT == 1
+ r.W(2) = satsw(d->L(2));
+ r.W(3) = satsw(d->L(3));
+#endif
+ r.W((2 << SHIFT) + 0) = satsw(s->L(0));
+ r.W((2 << SHIFT) + 1) = satsw(s->L(1));
+#if SHIFT == 1
+ r.W(6) = satsw(s->L(2));
+ r.W(7) = satsw(s->L(3));
+#endif
+ *d = r;
+}
+
+#define UNPCK_OP(base_name, base) \
+ \
+void OPPROTO glue(op_punpck ## base_name ## bw, SUFFIX) (void) \
+{ \
+ Reg r, *d, *s; \
+ d = (Reg *)((char *)env + PARAM1); \
+ s = (Reg *)((char *)env + PARAM2); \
+ \
+ r.B(0) = d->B((base << (SHIFT + 2)) + 0); \
+ r.B(1) = s->B((base << (SHIFT + 2)) + 0); \
+ r.B(2) = d->B((base << (SHIFT + 2)) + 1); \
+ r.B(3) = s->B((base << (SHIFT + 2)) + 1); \
+ r.B(4) = d->B((base << (SHIFT + 2)) + 2); \
+ r.B(5) = s->B((base << (SHIFT + 2)) + 2); \
+ r.B(6) = d->B((base << (SHIFT + 2)) + 3); \
+ r.B(7) = s->B((base << (SHIFT + 2)) + 3); \
+XMM_ONLY( \
+ r.B(8) = d->B((base << (SHIFT + 2)) + 4); \
+ r.B(9) = s->B((base << (SHIFT + 2)) + 4); \
+ r.B(10) = d->B((base << (SHIFT + 2)) + 5); \
+ r.B(11) = s->B((base << (SHIFT + 2)) + 5); \
+ r.B(12) = d->B((base << (SHIFT + 2)) + 6); \
+ r.B(13) = s->B((base << (SHIFT + 2)) + 6); \
+ r.B(14) = d->B((base << (SHIFT + 2)) + 7); \
+ r.B(15) = s->B((base << (SHIFT + 2)) + 7); \
+) \
+ *d = r; \
+} \
+ \
+void OPPROTO glue(op_punpck ## base_name ## wd, SUFFIX) (void) \
+{ \
+ Reg r, *d, *s; \
+ d = (Reg *)((char *)env + PARAM1); \
+ s = (Reg *)((char *)env + PARAM2); \
+ \
+ r.W(0) = d->W((base << (SHIFT + 1)) + 0); \
+ r.W(1) = s->W((base << (SHIFT + 1)) + 0); \
+ r.W(2) = d->W((base << (SHIFT + 1)) + 1); \
+ r.W(3) = s->W((base << (SHIFT + 1)) + 1); \
+XMM_ONLY( \
+ r.W(4) = d->W((base << (SHIFT + 1)) + 2); \
+ r.W(5) = s->W((base << (SHIFT + 1)) + 2); \
+ r.W(6) = d->W((base << (SHIFT + 1)) + 3); \
+ r.W(7) = s->W((base << (SHIFT + 1)) + 3); \
+) \
+ *d = r; \
+} \
+ \
+void OPPROTO glue(op_punpck ## base_name ## dq, SUFFIX) (void) \
+{ \
+ Reg r, *d, *s; \
+ d = (Reg *)((char *)env + PARAM1); \
+ s = (Reg *)((char *)env + PARAM2); \
+ \
+ r.L(0) = d->L((base << SHIFT) + 0); \
+ r.L(1) = s->L((base << SHIFT) + 0); \
+XMM_ONLY( \
+ r.L(2) = d->L((base << SHIFT) + 1); \
+ r.L(3) = s->L((base << SHIFT) + 1); \
+) \
+ *d = r; \
+} \
+ \
+XMM_ONLY( \
+void OPPROTO glue(op_punpck ## base_name ## qdq, SUFFIX) (void) \
+{ \
+ Reg r, *d, *s; \
+ d = (Reg *)((char *)env + PARAM1); \
+ s = (Reg *)((char *)env + PARAM2); \
+ \
+ r.Q(0) = d->Q(base); \
+ r.Q(1) = s->Q(base); \
+ *d = r; \
+} \
+)
+
+UNPCK_OP(l, 0)
+UNPCK_OP(h, 1)
+
+#undef SHIFT
+#undef XMM_ONLY
+#undef Reg
+#undef B
+#undef W
+#undef L
+#undef Q
+#undef SUFFIX
diff --git a/target-i386/ops_template.h b/target-i386/ops_template.h
new file mode 100644
index 0000000..373b77a
--- /dev/null
+++ b/target-i386/ops_template.h
@@ -0,0 +1,597 @@
+/*
+ * i386 micro operations (included several times to generate
+ * different operand sizes)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define DATA_BITS (1 << (3 + SHIFT))
+#define SHIFT_MASK (DATA_BITS - 1)
+#define SIGN_MASK (((target_ulong)1) << (DATA_BITS - 1))
+#if DATA_BITS <= 32
+#define SHIFT1_MASK 0x1f
+#else
+#define SHIFT1_MASK 0x3f
+#endif
+
+#if DATA_BITS == 8
+#define SUFFIX b
+#define DATA_TYPE uint8_t
+#define DATA_STYPE int8_t
+#define DATA_MASK 0xff
+#elif DATA_BITS == 16
+#define SUFFIX w
+#define DATA_TYPE uint16_t
+#define DATA_STYPE int16_t
+#define DATA_MASK 0xffff
+#elif DATA_BITS == 32
+#define SUFFIX l
+#define DATA_TYPE uint32_t
+#define DATA_STYPE int32_t
+#define DATA_MASK 0xffffffff
+#elif DATA_BITS == 64
+#define SUFFIX q
+#define DATA_TYPE uint64_t
+#define DATA_STYPE int64_t
+#define DATA_MASK 0xffffffffffffffffULL
+#else
+#error unhandled operand size
+#endif
+
+/* dynamic flags computation */
+
+static int glue(compute_all_add, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_SRC;
+ src2 = CC_DST - CC_SRC;
+ cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_add, SUFFIX)(void)
+{
+ int cf;
+ target_long src1;
+ src1 = CC_SRC;
+ cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
+ return cf;
+}
+
+static int glue(compute_all_adc, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_SRC;
+ src2 = CC_DST - CC_SRC - 1;
+ cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_adc, SUFFIX)(void)
+{
+ int cf;
+ target_long src1;
+ src1 = CC_SRC;
+ cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
+ return cf;
+}
+
+static int glue(compute_all_sub, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_sub, SUFFIX)(void)
+{
+ int cf;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
+ return cf;
+}
+
+static int glue(compute_all_sbb, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC + 1;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_sbb, SUFFIX)(void)
+{
+ int cf;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC + 1;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
+ return cf;
+}
+
+static int glue(compute_all_logic, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = 0;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = 0;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_logic, SUFFIX)(void)
+{
+ return 0;
+}
+
+static int glue(compute_all_inc, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST - 1;
+ src2 = 1;
+ cf = CC_SRC;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = ((CC_DST & DATA_MASK) == SIGN_MASK) << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_inc, SUFFIX)(void)
+{
+ return CC_SRC;
+}
+#endif
+
+static int glue(compute_all_dec, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + 1;
+ src2 = 1;
+ cf = CC_SRC;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = ((CC_DST & DATA_MASK) == ((target_ulong)SIGN_MASK - 1)) << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_all_shl, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = (CC_SRC >> (DATA_BITS - 1)) & CC_C;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ /* of is defined if shift count == 1 */
+ of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_shl, SUFFIX)(void)
+{
+ return (CC_SRC >> (DATA_BITS - 1)) & CC_C;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_sar, SUFFIX)(void)
+{
+ return CC_SRC & 1;
+}
+#endif
+
+static int glue(compute_all_sar, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = CC_SRC & 1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ /* of is defined if shift count == 1 */
+ of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_mul, SUFFIX)(void)
+{
+ int cf;
+ cf = (CC_SRC != 0);
+ return cf;
+}
+#endif
+
+/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and
+ CF are modified and it is slower to do that. */
+static int glue(compute_all_mul, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = (CC_SRC != 0);
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = cf << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+/* various optimized jumps cases */
+
+void OPPROTO glue(op_jb_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ if ((DATA_TYPE)src1 < (DATA_TYPE)src2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jz_sub, SUFFIX)(void)
+{
+ if ((DATA_TYPE)CC_DST == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jnz_sub, SUFFIX)(void)
+{
+ if ((DATA_TYPE)CC_DST != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jbe_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ if ((DATA_TYPE)src1 <= (DATA_TYPE)src2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_js_sub, SUFFIX)(void)
+{
+ if (CC_DST & SIGN_MASK)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jl_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ if ((DATA_STYPE)src1 < (DATA_STYPE)src2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jle_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ if ((DATA_STYPE)src1 <= (DATA_STYPE)src2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+/* oldies */
+
+#if DATA_BITS >= 16
+
+void OPPROTO glue(op_loopnz, SUFFIX)(void)
+{
+ if ((DATA_TYPE)ECX != 0 && !(T0 & CC_Z))
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_loopz, SUFFIX)(void)
+{
+ if ((DATA_TYPE)ECX != 0 && (T0 & CC_Z))
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jz_ecx, SUFFIX)(void)
+{
+ if ((DATA_TYPE)ECX == 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_jnz_ecx, SUFFIX)(void)
+{
+ if ((DATA_TYPE)ECX != 0)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+#endif
+
+/* various optimized set cases */
+
+void OPPROTO glue(op_setb_T0_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ T0 = ((DATA_TYPE)src1 < (DATA_TYPE)src2);
+}
+
+void OPPROTO glue(op_setz_T0_sub, SUFFIX)(void)
+{
+ T0 = ((DATA_TYPE)CC_DST == 0);
+}
+
+void OPPROTO glue(op_setbe_T0_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ T0 = ((DATA_TYPE)src1 <= (DATA_TYPE)src2);
+}
+
+void OPPROTO glue(op_sets_T0_sub, SUFFIX)(void)
+{
+ T0 = lshift(CC_DST, -(DATA_BITS - 1)) & 1;
+}
+
+void OPPROTO glue(op_setl_T0_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ T0 = ((DATA_STYPE)src1 < (DATA_STYPE)src2);
+}
+
+void OPPROTO glue(op_setle_T0_sub, SUFFIX)(void)
+{
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+
+ T0 = ((DATA_STYPE)src1 <= (DATA_STYPE)src2);
+}
+
+/* shifts */
+
+void OPPROTO glue(glue(op_shl, SUFFIX), _T0_T1)(void)
+{
+ int count;
+ count = T1 & SHIFT1_MASK;
+ T0 = T0 << count;
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_shr, SUFFIX), _T0_T1)(void)
+{
+ int count;
+ count = T1 & SHIFT1_MASK;
+ T0 &= DATA_MASK;
+ T0 = T0 >> count;
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_sar, SUFFIX), _T0_T1)(void)
+{
+ int count;
+ target_long src;
+
+ count = T1 & SHIFT1_MASK;
+ src = (DATA_STYPE)T0;
+ T0 = src >> count;
+ FORCE_RET();
+}
+
+#undef MEM_WRITE
+#include "ops_template_mem.h"
+
+#define MEM_WRITE 0
+#include "ops_template_mem.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MEM_WRITE 1
+#include "ops_template_mem.h"
+
+#define MEM_WRITE 2
+#include "ops_template_mem.h"
+#endif
+
+/* bit operations */
+#if DATA_BITS >= 16
+
+void OPPROTO glue(glue(op_bt, SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ CC_SRC = T0 >> count;
+}
+
+void OPPROTO glue(glue(op_bts, SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ T1 = T0 >> count;
+ T0 |= (((target_long)1) << count);
+}
+
+void OPPROTO glue(glue(op_btr, SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ T1 = T0 >> count;
+ T0 &= ~(((target_long)1) << count);
+}
+
+void OPPROTO glue(glue(op_btc, SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ T1 = T0 >> count;
+ T0 ^= (((target_long)1) << count);
+}
+
+void OPPROTO glue(glue(op_add_bit, SUFFIX), _A0_T1)(void)
+{
+ A0 += ((DATA_STYPE)T1 >> (3 + SHIFT)) << SHIFT;
+}
+
+void OPPROTO glue(glue(op_bsf, SUFFIX), _T0_cc)(void)
+{
+ int count;
+ target_long res;
+
+ res = T0 & DATA_MASK;
+ if (res != 0) {
+ count = 0;
+ while ((res & 1) == 0) {
+ count++;
+ res >>= 1;
+ }
+ T1 = count;
+ CC_DST = 1; /* ZF = 0 */
+ } else {
+ CC_DST = 0; /* ZF = 1 */
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
+{
+ int count;
+ target_long res;
+
+ res = T0 & DATA_MASK;
+ if (res != 0) {
+ count = DATA_BITS - 1;
+ while ((res & SIGN_MASK) == 0) {
+ count--;
+ res <<= 1;
+ }
+ T1 = count;
+ CC_DST = 1; /* ZF = 0 */
+ } else {
+ CC_DST = 0; /* ZF = 1 */
+ }
+ FORCE_RET();
+}
+
+#endif
+
+#if DATA_BITS == 32
+void OPPROTO op_update_bt_cc(void)
+{
+ CC_SRC = T1;
+}
+#endif
+
+/* string operations */
+
+void OPPROTO glue(op_movl_T0_Dshift, SUFFIX)(void)
+{
+ T0 = DF << SHIFT;
+}
+
+/* port I/O */
+#if DATA_BITS <= 32
+void OPPROTO glue(glue(op_out, SUFFIX), _T0_T1)(void)
+{
+ glue(cpu_out, SUFFIX)(env, T0, T1 & DATA_MASK);
+}
+
+void OPPROTO glue(glue(op_in, SUFFIX), _T0_T1)(void)
+{
+ T1 = glue(cpu_in, SUFFIX)(env, T0);
+}
+
+void OPPROTO glue(glue(op_in, SUFFIX), _DX_T0)(void)
+{
+ T0 = glue(cpu_in, SUFFIX)(env, EDX & 0xffff);
+}
+
+void OPPROTO glue(glue(op_out, SUFFIX), _DX_T0)(void)
+{
+ glue(cpu_out, SUFFIX)(env, EDX & 0xffff, T0);
+}
+
+void OPPROTO glue(glue(op_check_io, SUFFIX), _T0)(void)
+{
+ glue(glue(check_io, SUFFIX), _T0)();
+}
+
+void OPPROTO glue(glue(op_check_io, SUFFIX), _DX)(void)
+{
+ glue(glue(check_io, SUFFIX), _DX)();
+}
+#endif
+
+#undef DATA_BITS
+#undef SHIFT_MASK
+#undef SHIFT1_MASK
+#undef SIGN_MASK
+#undef DATA_TYPE
+#undef DATA_STYPE
+#undef DATA_MASK
+#undef SUFFIX
diff --git a/target-i386/ops_template_mem.h b/target-i386/ops_template_mem.h
new file mode 100644
index 0000000..9f72a8c
--- /dev/null
+++ b/target-i386/ops_template_mem.h
@@ -0,0 +1,483 @@
+/*
+ * i386 micro operations (included several times to generate
+ * different operand sizes)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifdef MEM_WRITE
+
+#if MEM_WRITE == 0
+
+#if DATA_BITS == 8
+#define MEM_SUFFIX b_raw
+#elif DATA_BITS == 16
+#define MEM_SUFFIX w_raw
+#elif DATA_BITS == 32
+#define MEM_SUFFIX l_raw
+#elif DATA_BITS == 64
+#define MEM_SUFFIX q_raw
+#endif
+
+#elif MEM_WRITE == 1
+
+#if DATA_BITS == 8
+#define MEM_SUFFIX b_kernel
+#elif DATA_BITS == 16
+#define MEM_SUFFIX w_kernel
+#elif DATA_BITS == 32
+#define MEM_SUFFIX l_kernel
+#elif DATA_BITS == 64
+#define MEM_SUFFIX q_kernel
+#endif
+
+#elif MEM_WRITE == 2
+
+#if DATA_BITS == 8
+#define MEM_SUFFIX b_user
+#elif DATA_BITS == 16
+#define MEM_SUFFIX w_user
+#elif DATA_BITS == 32
+#define MEM_SUFFIX l_user
+#elif DATA_BITS == 64
+#define MEM_SUFFIX q_user
+#endif
+
+#else
+
+#error invalid MEM_WRITE
+
+#endif
+
+#else
+
+#define MEM_SUFFIX SUFFIX
+
+#endif
+
+void OPPROTO glue(glue(op_rol, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ target_long src;
+
+ if (T1 & SHIFT1_MASK) {
+ count = T1 & SHIFT_MASK;
+ src = T0;
+ T0 &= DATA_MASK;
+ T0 = (T0 << count) | (T0 >> (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#else
+ /* gcc 3.2 workaround. This is really a bug in gcc. */
+ asm volatile("" : : "r" (T0));
+#endif
+ CC_SRC = (cc_table[CC_OP].compute_all() & ~(CC_O | CC_C)) |
+ (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
+ (T0 & CC_C);
+ CC_OP = CC_OP_EFLAGS;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_ror, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ target_long src;
+
+ if (T1 & SHIFT1_MASK) {
+ count = T1 & SHIFT_MASK;
+ src = T0;
+ T0 &= DATA_MASK;
+ T0 = (T0 >> count) | (T0 << (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#else
+ /* gcc 3.2 workaround. This is really a bug in gcc. */
+ asm volatile("" : : "r" (T0));
+#endif
+ CC_SRC = (cc_table[CC_OP].compute_all() & ~(CC_O | CC_C)) |
+ (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
+ ((T0 >> (DATA_BITS - 1)) & CC_C);
+ CC_OP = CC_OP_EFLAGS;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_rol, MEM_SUFFIX), _T0_T1)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ if (count) {
+ T0 &= DATA_MASK;
+ T0 = (T0 << count) | (T0 >> (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_ror, MEM_SUFFIX), _T0_T1)(void)
+{
+ int count;
+ count = T1 & SHIFT_MASK;
+ if (count) {
+ T0 &= DATA_MASK;
+ T0 = (T0 >> count) | (T0 << (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_rcl, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count, eflags;
+ target_ulong src;
+ target_long res;
+
+ count = T1 & SHIFT1_MASK;
+#if DATA_BITS == 16
+ count = rclw_table[count];
+#elif DATA_BITS == 8
+ count = rclb_table[count];
+#endif
+ if (count) {
+ eflags = cc_table[CC_OP].compute_all();
+ T0 &= DATA_MASK;
+ src = T0;
+ res = (T0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1));
+ if (count > 1)
+ res |= T0 >> (DATA_BITS + 1 - count);
+ T0 = res;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = (eflags & ~(CC_C | CC_O)) |
+ (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
+ ((src >> (DATA_BITS - count)) & CC_C);
+ CC_OP = CC_OP_EFLAGS;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_rcr, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count, eflags;
+ target_ulong src;
+ target_long res;
+
+ count = T1 & SHIFT1_MASK;
+#if DATA_BITS == 16
+ count = rclw_table[count];
+#elif DATA_BITS == 8
+ count = rclb_table[count];
+#endif
+ if (count) {
+ eflags = cc_table[CC_OP].compute_all();
+ T0 &= DATA_MASK;
+ src = T0;
+ res = (T0 >> count) | ((target_ulong)(eflags & CC_C) << (DATA_BITS - count));
+ if (count > 1)
+ res |= T0 << (DATA_BITS + 1 - count);
+ T0 = res;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = (eflags & ~(CC_C | CC_O)) |
+ (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) |
+ ((src >> (count - 1)) & CC_C);
+ CC_OP = CC_OP_EFLAGS;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_shl, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ target_long src;
+
+ count = T1 & SHIFT1_MASK;
+ if (count) {
+ src = (DATA_TYPE)T0 << (count - 1);
+ T0 = T0 << count;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = src;
+ CC_DST = T0;
+ CC_OP = CC_OP_SHLB + SHIFT;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_shr, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ target_long src;
+
+ count = T1 & SHIFT1_MASK;
+ if (count) {
+ T0 &= DATA_MASK;
+ src = T0 >> (count - 1);
+ T0 = T0 >> count;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = src;
+ CC_DST = T0;
+ CC_OP = CC_OP_SARB + SHIFT;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_sar, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int count;
+ target_long src;
+
+ count = T1 & SHIFT1_MASK;
+ if (count) {
+ src = (DATA_STYPE)T0;
+ T0 = src >> count;
+ src = src >> (count - 1);
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = src;
+ CC_DST = T0;
+ CC_OP = CC_OP_SARB + SHIFT;
+ }
+ FORCE_RET();
+}
+
+#if DATA_BITS == 16
+/* XXX: overflow flag might be incorrect in some cases in shldw */
+void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_im_cc)(void)
+{
+ int count;
+ unsigned int res, tmp;
+ count = PARAM1;
+ T1 &= 0xffff;
+ res = T1 | (T0 << 16);
+ tmp = res >> (32 - count);
+ res <<= count;
+ if (count > 16)
+ res |= T1 << (count - 16);
+ T0 = res >> 16;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+}
+
+void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_ECX_cc)(void)
+{
+ int count;
+ unsigned int res, tmp;
+ count = ECX & 0x1f;
+ if (count) {
+ T1 &= 0xffff;
+ res = T1 | (T0 << 16);
+ tmp = res >> (32 - count);
+ res <<= count;
+ if (count > 16)
+ res |= T1 << (count - 16);
+ T0 = res >> 16;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+ CC_OP = CC_OP_SARB + SHIFT;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_im_cc)(void)
+{
+ int count;
+ unsigned int res, tmp;
+
+ count = PARAM1;
+ res = (T0 & 0xffff) | (T1 << 16);
+ tmp = res >> (count - 1);
+ res >>= count;
+ if (count > 16)
+ res |= T1 << (32 - count);
+ T0 = res;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+}
+
+
+void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_ECX_cc)(void)
+{
+ int count;
+ unsigned int res, tmp;
+
+ count = ECX & 0x1f;
+ if (count) {
+ res = (T0 & 0xffff) | (T1 << 16);
+ tmp = res >> (count - 1);
+ res >>= count;
+ if (count > 16)
+ res |= T1 << (32 - count);
+ T0 = res;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+ CC_OP = CC_OP_SARB + SHIFT;
+ }
+ FORCE_RET();
+}
+#endif
+
+#if DATA_BITS >= 32
+void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_im_cc)(void)
+{
+ int count;
+ target_long tmp;
+
+ count = PARAM1;
+ T0 &= DATA_MASK;
+ T1 &= DATA_MASK;
+ tmp = T0 << (count - 1);
+ T0 = (T0 << count) | (T1 >> (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+}
+
+void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_ECX_cc)(void)
+{
+ int count;
+ target_long tmp;
+
+ count = ECX & SHIFT1_MASK;
+ if (count) {
+ T0 &= DATA_MASK;
+ T1 &= DATA_MASK;
+ tmp = T0 << (count - 1);
+ T0 = (T0 << count) | (T1 >> (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+ CC_OP = CC_OP_SHLB + SHIFT;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_im_cc)(void)
+{
+ int count;
+ target_long tmp;
+
+ count = PARAM1;
+ T0 &= DATA_MASK;
+ T1 &= DATA_MASK;
+ tmp = T0 >> (count - 1);
+ T0 = (T0 >> count) | (T1 << (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+}
+
+
+void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_ECX_cc)(void)
+{
+ int count;
+ target_long tmp;
+
+ count = ECX & SHIFT1_MASK;
+ if (count) {
+ T0 &= DATA_MASK;
+ T1 &= DATA_MASK;
+ tmp = T0 >> (count - 1);
+ T0 = (T0 >> count) | (T1 << (DATA_BITS - count));
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = tmp;
+ CC_DST = T0;
+ CC_OP = CC_OP_SARB + SHIFT;
+ }
+ FORCE_RET();
+}
+#endif
+
+/* carry add/sub (we only need to set CC_OP differently) */
+
+void OPPROTO glue(glue(op_adc, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int cf;
+ cf = cc_table[CC_OP].compute_c();
+ T0 = T0 + T1 + cf;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = T1;
+ CC_DST = T0;
+ CC_OP = CC_OP_ADDB + SHIFT + cf * 4;
+}
+
+void OPPROTO glue(glue(op_sbb, MEM_SUFFIX), _T0_T1_cc)(void)
+{
+ int cf;
+ cf = cc_table[CC_OP].compute_c();
+ T0 = T0 - T1 - cf;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ CC_SRC = T1;
+ CC_DST = T0;
+ CC_OP = CC_OP_SUBB + SHIFT + cf * 4;
+}
+
+void OPPROTO glue(glue(op_cmpxchg, MEM_SUFFIX), _T0_T1_EAX_cc)(void)
+{
+ target_ulong src, dst;
+
+ src = T0;
+ dst = EAX - T0;
+ if ((DATA_TYPE)dst == 0) {
+ T0 = T1;
+#ifdef MEM_WRITE
+ glue(st, MEM_SUFFIX)(A0, T0);
+#endif
+ } else {
+ EAX = (EAX & ~DATA_MASK) | (T0 & DATA_MASK);
+ }
+ CC_SRC = src;
+ CC_DST = dst;
+ FORCE_RET();
+}
+
+#undef MEM_SUFFIX
+#undef MEM_WRITE
diff --git a/target-i386/translate-copy.c b/target-i386/translate-copy.c
new file mode 100644
index 0000000..cf8bd5a
--- /dev/null
+++ b/target-i386/translate-copy.c
@@ -0,0 +1,1323 @@
+/*
+ * i386 on i386 translation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+#ifdef USE_CODE_COPY
+
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/ucontext.h>
+
+extern char exec_loop;
+
+/* operand size */
+enum {
+ OT_BYTE = 0,
+ OT_WORD,
+ OT_LONG,
+ OT_QUAD,
+};
+
+#define PREFIX_REPZ 0x01
+#define PREFIX_REPNZ 0x02
+#define PREFIX_LOCK 0x04
+#define PREFIX_DATA 0x08
+#define PREFIX_ADR 0x10
+
+typedef struct DisasContext {
+ /* current insn context */
+ int override; /* -1 if no override */
+ int prefix;
+ int aflag, dflag;
+ target_ulong pc; /* pc = eip + cs_base */
+ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
+ static state change (stop translation) */
+ /* code output */
+ uint8_t *gen_code_ptr;
+ uint8_t *gen_code_start;
+
+ /* current block context */
+ target_ulong cs_base; /* base of CS segment */
+ int pe; /* protected mode */
+ int code32; /* 32 bit code segment */
+ int f_st; /* currently unused */
+ int vm86; /* vm86 mode */
+ int cpl;
+ int iopl;
+ int flags;
+ struct TranslationBlock *tb;
+} DisasContext;
+
+#define CPU_FIELD_OFFSET(field) offsetof(CPUState, field)
+
+#define CPU_SEG 0x64 /* fs override */
+
+static inline void gb(DisasContext *s, uint32_t val)
+{
+ *s->gen_code_ptr++ = val;
+}
+
+static inline void gw(DisasContext *s, uint32_t val)
+{
+ *s->gen_code_ptr++ = val;
+ *s->gen_code_ptr++ = val >> 8;
+}
+
+static inline void gl(DisasContext *s, uint32_t val)
+{
+ *s->gen_code_ptr++ = val;
+ *s->gen_code_ptr++ = val >> 8;
+ *s->gen_code_ptr++ = val >> 16;
+ *s->gen_code_ptr++ = val >> 24;
+}
+
+static inline void gjmp(DisasContext *s, long val)
+{
+ gb(s, 0xe9); /* jmp */
+ gl(s, val - (long)(s->gen_code_ptr + 4));
+}
+
+static inline void gen_movl_addr_im(DisasContext *s,
+ uint32_t addr, uint32_t val)
+{
+ gb(s, CPU_SEG); /* seg movl im, addr */
+ gb(s, 0xc7);
+ gb(s, 0x05);
+ gl(s, addr);
+ gl(s, val);
+}
+
+static inline void gen_movw_addr_im(DisasContext *s,
+ uint32_t addr, uint32_t val)
+{
+ gb(s, CPU_SEG); /* seg movl im, addr */
+ gb(s, 0x66);
+ gb(s, 0xc7);
+ gb(s, 0x05);
+ gl(s, addr);
+ gw(s, val);
+}
+
+
+static void gen_jmp(DisasContext *s, uint32_t target_eip)
+{
+ TranslationBlock *tb = s->tb;
+
+ gb(s, 0xe9); /* jmp */
+ tb->tb_jmp_offset[0] = s->gen_code_ptr - s->gen_code_start;
+ gl(s, 0);
+
+ tb->tb_next_offset[0] = s->gen_code_ptr - s->gen_code_start;
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), target_eip);
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb);
+ gjmp(s, (long)&exec_loop);
+
+ s->is_jmp = 1;
+}
+
+static void gen_jcc(DisasContext *s, int op,
+ uint32_t target_eip, uint32_t next_eip)
+{
+ TranslationBlock *tb = s->tb;
+
+ gb(s, 0x0f); /* jcc */
+ gb(s, 0x80 + op);
+ tb->tb_jmp_offset[0] = s->gen_code_ptr - s->gen_code_start;
+ gl(s, 0);
+ gb(s, 0xe9); /* jmp */
+ tb->tb_jmp_offset[1] = s->gen_code_ptr - s->gen_code_start;
+ gl(s, 0);
+
+ tb->tb_next_offset[0] = s->gen_code_ptr - s->gen_code_start;
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), target_eip);
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb);
+ gjmp(s, (long)&exec_loop);
+
+ tb->tb_next_offset[1] = s->gen_code_ptr - s->gen_code_start;
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), next_eip);
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb | 1);
+ gjmp(s, (long)&exec_loop);
+
+ s->is_jmp = 1;
+}
+
+static void gen_eob(DisasContext *s)
+{
+ gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), 0);
+ gjmp(s, (long)&exec_loop);
+
+ s->is_jmp = 1;
+}
+
+static inline void gen_lea_modrm(DisasContext *s, int modrm)
+{
+ int havesib;
+ int base, disp;
+ int index;
+ int scale;
+ int mod, rm, code;
+
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+
+ if (s->aflag) {
+
+ havesib = 0;
+ base = rm;
+ index = 0;
+ scale = 0;
+
+ if (base == 4) {
+ havesib = 1;
+ code = ldub_code(s->pc++);
+ scale = (code >> 6) & 3;
+ index = (code >> 3) & 7;
+ base = code & 7;
+ }
+
+ switch (mod) {
+ case 0:
+ if (base == 5) {
+ base = -1;
+ disp = ldl_code(s->pc);
+ s->pc += 4;
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+
+ } else {
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ }
+ }
+}
+
+static inline void parse_modrm(DisasContext *s, int modrm)
+{
+ if ((modrm & 0xc0) != 0xc0)
+ gen_lea_modrm(s, modrm);
+}
+
+static inline uint32_t insn_get(DisasContext *s, int ot)
+{
+ uint32_t ret;
+
+ switch(ot) {
+ case OT_BYTE:
+ ret = ldub_code(s->pc);
+ s->pc++;
+ break;
+ case OT_WORD:
+ ret = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ default:
+ case OT_LONG:
+ ret = ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+ return ret;
+}
+
+/* convert one instruction. s->is_jmp is set if the translation must
+ be stopped. */
+static int disas_insn(DisasContext *s)
+{
+ target_ulong pc_start, pc_tmp, pc_start_insn;
+ int b, prefixes, aflag, dflag, next_eip, val;
+ int ot;
+ int modrm, mod, op, rm;
+
+ pc_start = s->pc;
+ prefixes = 0;
+ aflag = s->code32;
+ dflag = s->code32;
+ s->override = -1;
+ next_byte:
+ b = ldub_code(s->pc);
+ s->pc++;
+ /* check prefixes */
+ switch (b) {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ goto next_byte;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ goto next_byte;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ goto next_byte;
+ case 0x2e:
+ s->override = R_CS;
+ goto next_byte;
+ case 0x36:
+ s->override = R_SS;
+ goto next_byte;
+ case 0x3e:
+ s->override = R_DS;
+ goto next_byte;
+ case 0x26:
+ s->override = R_ES;
+ goto next_byte;
+ case 0x64:
+ s->override = R_FS;
+ goto next_byte;
+ case 0x65:
+ s->override = R_GS;
+ goto next_byte;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ goto next_byte;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ goto next_byte;
+ }
+
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+ if (prefixes & PREFIX_ADR)
+ aflag ^= 1;
+
+ s->prefix = prefixes;
+ s->aflag = aflag;
+ s->dflag = dflag;
+
+ /* lock generation */
+ if (prefixes & PREFIX_LOCK)
+ goto unsupported_op;
+ if (s->override == R_FS || s->override == R_GS || s->override == R_CS)
+ goto unsupported_op;
+
+ pc_start_insn = s->pc - 1;
+ /* now check op code */
+ reswitch:
+ switch(b) {
+ case 0x0f:
+ /**************************/
+ /* extended op code */
+ b = ldub_code(s->pc++) | 0x100;
+ goto reswitch;
+
+ /**************************/
+ /* arith & logic */
+ case 0x00 ... 0x05:
+ case 0x08 ... 0x0d:
+ case 0x10 ... 0x15:
+ case 0x18 ... 0x1d:
+ case 0x20 ... 0x25:
+ case 0x28 ... 0x2d:
+ case 0x30 ... 0x35:
+ case 0x38 ... 0x3d:
+ {
+ int f;
+ f = (b >> 1) & 3;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+
+ switch(f) {
+ case 0: /* OP Ev, Gv */
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ break;
+ case 1: /* OP Gv, Ev */
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ break;
+ case 2: /* OP A, Iv */
+ insn_get(s, ot);
+ break;
+ }
+ }
+ break;
+
+ case 0x80: /* GRP1 */
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ {
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+
+ switch(b) {
+ default:
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ insn_get(s, ot);
+ break;
+ case 0x83:
+ insn_get(s, OT_BYTE);
+ break;
+ }
+ }
+ break;
+
+ /**************************/
+ /* inc, dec, and other misc arith */
+ case 0x40 ... 0x47: /* inc Gv */
+ break;
+ case 0x48 ... 0x4f: /* dec Gv */
+ break;
+ case 0xf6: /* GRP3 */
+ case 0xf7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ op = (modrm >> 3) & 7;
+ parse_modrm(s, modrm);
+
+ switch(op) {
+ case 0: /* test */
+ insn_get(s, ot);
+ break;
+ case 2: /* not */
+ break;
+ case 3: /* neg */
+ break;
+ case 4: /* mul */
+ break;
+ case 5: /* imul */
+ break;
+ case 6: /* div */
+ break;
+ case 7: /* idiv */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+
+ case 0xfe: /* GRP4 */
+ case 0xff: /* GRP5 */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ if (op >= 2 && b == 0xfe) {
+ goto illegal_op;
+ }
+ pc_tmp = s->pc;
+ parse_modrm(s, modrm);
+
+ switch(op) {
+ case 0: /* inc Ev */
+ break;
+ case 1: /* dec Ev */
+ break;
+ case 2: /* call Ev */
+ /* XXX: optimize and handle MEM exceptions specifically
+ fs movl %eax, regs[0]
+ movl Ev, %eax
+ pushl next_eip
+ fs movl %eax, eip
+ */
+ goto unsupported_op;
+ case 3: /* lcall Ev */
+ goto unsupported_op;
+ case 4: /* jmp Ev */
+ /* XXX: optimize and handle MEM exceptions specifically
+ fs movl %eax, regs[0]
+ movl Ev, %eax
+ fs movl %eax, eip
+ */
+ goto unsupported_op;
+ case 5: /* ljmp Ev */
+ goto unsupported_op;
+ case 6: /* push Ev */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0xa8: /* test eAX, Iv */
+ case 0xa9:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ insn_get(s, ot);
+ break;
+
+ case 0x98: /* CWDE/CBW */
+ break;
+ case 0x99: /* CDQ/CWD */
+ break;
+ case 0x1af: /* imul Gv, Ev */
+ case 0x69: /* imul Gv, Ev, I */
+ case 0x6b:
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ if (b == 0x69) {
+ insn_get(s, ot);
+ } else if (b == 0x6b) {
+ insn_get(s, OT_BYTE);
+ } else {
+ }
+ break;
+
+ case 0x84: /* test Ev, Gv */
+ case 0x85:
+
+ case 0x1c0:
+ case 0x1c1: /* xadd Ev, Gv */
+
+ case 0x1b0:
+ case 0x1b1: /* cmpxchg Ev, Gv */
+
+ case 0x8f: /* pop Ev */
+
+ case 0x88:
+ case 0x89: /* mov Gv, Ev */
+
+ case 0x8a:
+ case 0x8b: /* mov Ev, Gv */
+
+ case 0x1b6: /* movzbS Gv, Eb */
+ case 0x1b7: /* movzwS Gv, Eb */
+ case 0x1be: /* movsbS Gv, Eb */
+ case 0x1bf: /* movswS Gv, Eb */
+
+ case 0x86:
+ case 0x87: /* xchg Ev, Gv */
+
+ case 0xd0:
+ case 0xd1: /* shift Ev,1 */
+
+ case 0xd2:
+ case 0xd3: /* shift Ev,cl */
+
+ case 0x1a5: /* shld cl */
+ case 0x1ad: /* shrd cl */
+
+ case 0x190 ... 0x19f: /* setcc Gv */
+
+ /* XXX: emulate cmov if not available ? */
+ case 0x140 ... 0x14f: /* cmov Gv, Ev */
+
+ case 0x1a3: /* bt Gv, Ev */
+ case 0x1ab: /* bts */
+ case 0x1b3: /* btr */
+ case 0x1bb: /* btc */
+
+ case 0x1bc: /* bsf */
+ case 0x1bd: /* bsr */
+
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ break;
+
+ case 0x1c7: /* cmpxchg8b */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ parse_modrm(s, modrm);
+ break;
+
+ /**************************/
+ /* push/pop */
+ case 0x50 ... 0x57: /* push */
+ case 0x58 ... 0x5f: /* pop */
+ case 0x60: /* pusha */
+ case 0x61: /* popa */
+ break;
+
+ case 0x68: /* push Iv */
+ case 0x6a:
+ ot = dflag ? OT_LONG : OT_WORD;
+ if (b == 0x68)
+ insn_get(s, ot);
+ else
+ insn_get(s, OT_BYTE);
+ break;
+ case 0xc8: /* enter */
+ lduw_code(s->pc);
+ s->pc += 2;
+ ldub_code(s->pc++);
+ break;
+ case 0xc9: /* leave */
+ break;
+
+ case 0x06: /* push es */
+ case 0x0e: /* push cs */
+ case 0x16: /* push ss */
+ case 0x1e: /* push ds */
+ /* XXX: optimize:
+ push segs[n].selector
+ */
+ goto unsupported_op;
+ case 0x1a0: /* push fs */
+ case 0x1a8: /* push gs */
+ goto unsupported_op;
+ case 0x07: /* pop es */
+ case 0x17: /* pop ss */
+ case 0x1f: /* pop ds */
+ goto unsupported_op;
+ case 0x1a1: /* pop fs */
+ case 0x1a9: /* pop gs */
+ goto unsupported_op;
+ case 0x8e: /* mov seg, Gv */
+ /* XXX: optimize:
+ fs movl r, regs[]
+ movl segs[].selector, r
+ mov r, Gv
+ fs movl regs[], r
+ */
+ goto unsupported_op;
+ case 0x8c: /* mov Gv, seg */
+ goto unsupported_op;
+ case 0xc4: /* les Gv */
+ op = R_ES;
+ goto do_lxx;
+ case 0xc5: /* lds Gv */
+ op = R_DS;
+ goto do_lxx;
+ case 0x1b2: /* lss Gv */
+ op = R_SS;
+ goto do_lxx;
+ case 0x1b4: /* lfs Gv */
+ op = R_FS;
+ goto do_lxx;
+ case 0x1b5: /* lgs Gv */
+ op = R_GS;
+ do_lxx:
+ goto unsupported_op;
+ /************************/
+ /* floats */
+ case 0xd8 ... 0xdf:
+#if 1
+ /* currently not stable enough */
+ goto unsupported_op;
+#else
+ if (s->flags & (HF_EM_MASK | HF_TS_MASK))
+ goto unsupported_op;
+#endif
+#if 0
+ /* for testing FPU context switch */
+ {
+ static int count;
+ count = (count + 1) % 3;
+ if (count != 0)
+ goto unsupported_op;
+ }
+#endif
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ op = ((b & 7) << 3) | ((modrm >> 3) & 7);
+ if (mod != 3) {
+ /* memory op */
+ parse_modrm(s, modrm);
+ switch(op) {
+ case 0x00 ... 0x07: /* fxxxs */
+ case 0x10 ... 0x17: /* fixxxl */
+ case 0x20 ... 0x27: /* fxxxl */
+ case 0x30 ... 0x37: /* fixxx */
+ break;
+ case 0x08: /* flds */
+ case 0x0a: /* fsts */
+ case 0x0b: /* fstps */
+ case 0x18: /* fildl */
+ case 0x1a: /* fistl */
+ case 0x1b: /* fistpl */
+ case 0x28: /* fldl */
+ case 0x2a: /* fstl */
+ case 0x2b: /* fstpl */
+ case 0x38: /* filds */
+ case 0x3a: /* fists */
+ case 0x3b: /* fistps */
+ case 0x0c: /* fldenv mem */
+ case 0x0d: /* fldcw mem */
+ case 0x0e: /* fnstenv mem */
+ case 0x0f: /* fnstcw mem */
+ case 0x1d: /* fldt mem */
+ case 0x1f: /* fstpt mem */
+ case 0x2c: /* frstor mem */
+ case 0x2e: /* fnsave mem */
+ case 0x2f: /* fnstsw mem */
+ case 0x3c: /* fbld */
+ case 0x3e: /* fbstp */
+ case 0x3d: /* fildll */
+ case 0x3f: /* fistpll */
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ /* register float ops */
+ switch(op) {
+ case 0x08: /* fld sti */
+ case 0x09: /* fxchg sti */
+ break;
+ case 0x0a: /* grp d9/2 */
+ switch(rm) {
+ case 0: /* fnop */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0c: /* grp d9/4 */
+ switch(rm) {
+ case 0: /* fchs */
+ case 1: /* fabs */
+ case 4: /* ftst */
+ case 5: /* fxam */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0d: /* grp d9/5 */
+ switch(rm) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0e: /* grp d9/6 */
+ break;
+ case 0x0f: /* grp d9/7 */
+ break;
+ case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */
+ case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */
+ case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */
+ break;
+ case 0x02: /* fcom */
+ break;
+ case 0x03: /* fcomp */
+ break;
+ case 0x15: /* da/5 */
+ switch(rm) {
+ case 1: /* fucompp */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1c:
+ switch(rm) {
+ case 0: /* feni (287 only, just do nop here) */
+ case 1: /* fdisi (287 only, just do nop here) */
+ goto unsupported_op;
+ case 2: /* fclex */
+ case 3: /* fninit */
+ case 4: /* fsetpm (287 only, just do nop here) */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1d: /* fucomi */
+ break;
+ case 0x1e: /* fcomi */
+ break;
+ case 0x28: /* ffree sti */
+ break;
+ case 0x2a: /* fst sti */
+ break;
+ case 0x2b: /* fstp sti */
+ break;
+ case 0x2c: /* fucom st(i) */
+ break;
+ case 0x2d: /* fucomp st(i) */
+ break;
+ case 0x33: /* de/3 */
+ switch(rm) {
+ case 1: /* fcompp */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x3c: /* df/4 */
+ switch(rm) {
+ case 0:
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x3d: /* fucomip */
+ break;
+ case 0x3e: /* fcomip */
+ break;
+ case 0x10 ... 0x13: /* fcmovxx */
+ case 0x18 ... 0x1b:
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ s->tb->cflags |= CF_TB_FP_USED;
+ break;
+
+ /**************************/
+ /* mov */
+ case 0xc6:
+ case 0xc7: /* mov Ev, Iv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ insn_get(s, ot);
+ break;
+
+ case 0x8d: /* lea */
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ parse_modrm(s, modrm);
+ break;
+
+ case 0xa0: /* mov EAX, Ov */
+ case 0xa1:
+ case 0xa2: /* mov Ov, EAX */
+ case 0xa3:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ if (s->aflag)
+ insn_get(s, OT_LONG);
+ else
+ insn_get(s, OT_WORD);
+ break;
+ case 0xd7: /* xlat */
+ break;
+ case 0xb0 ... 0xb7: /* mov R, Ib */
+ insn_get(s, OT_BYTE);
+ break;
+ case 0xb8 ... 0xbf: /* mov R, Iv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ insn_get(s, ot);
+ break;
+
+ case 0x91 ... 0x97: /* xchg R, EAX */
+ break;
+
+ /************************/
+ /* shifts */
+ case 0xc0:
+ case 0xc1: /* shift Ev,imm */
+
+ case 0x1a4: /* shld imm */
+ case 0x1ac: /* shrd imm */
+ modrm = ldub_code(s->pc++);
+ parse_modrm(s, modrm);
+ ldub_code(s->pc++);
+ break;
+
+ /************************/
+ /* string ops */
+
+ case 0xa4: /* movsS */
+ case 0xa5:
+ break;
+
+ case 0xaa: /* stosS */
+ case 0xab:
+ break;
+
+ case 0xac: /* lodsS */
+ case 0xad:
+ break;
+
+ case 0xae: /* scasS */
+ case 0xaf:
+ break;
+
+ case 0xa6: /* cmpsS */
+ case 0xa7:
+ break;
+
+ case 0x6c: /* insS */
+ case 0x6d:
+ goto unsupported_op;
+
+ case 0x6e: /* outsS */
+ case 0x6f:
+ goto unsupported_op;
+
+ /************************/
+ /* port I/O */
+ case 0xe4:
+ case 0xe5:
+ goto unsupported_op;
+
+ case 0xe6:
+ case 0xe7:
+ goto unsupported_op;
+
+ case 0xec:
+ case 0xed:
+ goto unsupported_op;
+
+ case 0xee:
+ case 0xef:
+ goto unsupported_op;
+
+ /************************/
+ /* control */
+#if 0
+ case 0xc2: /* ret im */
+ val = ldsw_code(s->pc);
+ s->pc += 2;
+ gen_pop_T0(s);
+ gen_stack_update(s, val + (2 << s->dflag));
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+#endif
+
+ case 0xc3: /* ret */
+ gb(s, CPU_SEG);
+ if (!s->dflag)
+ gb(s, 0x66); /* d16 */
+ gb(s, 0x8f); /* pop addr */
+ gb(s, 0x05);
+ gl(s, CPU_FIELD_OFFSET(eip));
+ if (!s->dflag) {
+ /* reset high bits of EIP */
+ gen_movw_addr_im(s, CPU_FIELD_OFFSET(eip) + 2, 0);
+ }
+ gen_eob(s);
+ goto no_copy;
+ case 0xca: /* lret im */
+ case 0xcb: /* lret */
+ case 0xcf: /* iret */
+ case 0x9a: /* lcall im */
+ case 0xea: /* ljmp im */
+ goto unsupported_op;
+
+ case 0xe8: /* call im */
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = insn_get(s, ot);
+ next_eip = s->pc - s->cs_base;
+ val += next_eip;
+ if (s->dflag) {
+ gb(s, 0x68); /* pushl imm */
+ gl(s, next_eip);
+ } else {
+ gb(s, 0x66); /* pushw imm */
+ gb(s, 0x68);
+ gw(s, next_eip);
+ val &= 0xffff;
+ }
+ gen_jmp(s, val);
+ goto no_copy;
+ case 0xe9: /* jmp */
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = insn_get(s, ot);
+ val += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ val = val & 0xffff;
+ gen_jmp(s, val);
+ goto no_copy;
+ case 0xeb: /* jmp Jb */
+ val = (int8_t)insn_get(s, OT_BYTE);
+ val += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ val = val & 0xffff;
+ gen_jmp(s, val);
+ goto no_copy;
+ case 0x70 ... 0x7f: /* jcc Jb */
+ val = (int8_t)insn_get(s, OT_BYTE);
+ goto do_jcc;
+ case 0x180 ... 0x18f: /* jcc Jv */
+ if (dflag) {
+ val = insn_get(s, OT_LONG);
+ } else {
+ val = (int16_t)insn_get(s, OT_WORD);
+ }
+ do_jcc:
+ next_eip = s->pc - s->cs_base;
+ val += next_eip;
+ if (s->dflag == 0)
+ val &= 0xffff;
+ gen_jcc(s, b & 0xf, val, next_eip);
+ goto no_copy;
+
+ /************************/
+ /* flags */
+ case 0x9c: /* pushf */
+ /* XXX: put specific code ? */
+ goto unsupported_op;
+ case 0x9d: /* popf */
+ goto unsupported_op;
+
+ case 0x9e: /* sahf */
+ case 0x9f: /* lahf */
+ case 0xf5: /* cmc */
+ case 0xf8: /* clc */
+ case 0xf9: /* stc */
+ case 0xfc: /* cld */
+ case 0xfd: /* std */
+ break;
+
+ /************************/
+ /* bit operations */
+ case 0x1ba: /* bt/bts/btr/btc Gv, im */
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ op = (modrm >> 3) & 7;
+ parse_modrm(s, modrm);
+ /* load shift */
+ ldub_code(s->pc++);
+ if (op < 4)
+ goto illegal_op;
+ break;
+ /************************/
+ /* bcd */
+ case 0x27: /* daa */
+ break;
+ case 0x2f: /* das */
+ break;
+ case 0x37: /* aaa */
+ break;
+ case 0x3f: /* aas */
+ break;
+ case 0xd4: /* aam */
+ ldub_code(s->pc++);
+ break;
+ case 0xd5: /* aad */
+ ldub_code(s->pc++);
+ break;
+ /************************/
+ /* misc */
+ case 0x90: /* nop */
+ break;
+ case 0x9b: /* fwait */
+ if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) ==
+ (HF_MP_MASK | HF_TS_MASK)) {
+ goto unsupported_op;
+ }
+ break;
+ case 0xcc: /* int3 */
+ goto unsupported_op;
+ case 0xcd: /* int N */
+ goto unsupported_op;
+ case 0xce: /* into */
+ goto unsupported_op;
+ case 0xf1: /* icebp (undocumented, exits to external debugger) */
+ goto unsupported_op;
+ case 0xfa: /* cli */
+ goto unsupported_op;
+ case 0xfb: /* sti */
+ goto unsupported_op;
+ case 0x62: /* bound */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ parse_modrm(s, modrm);
+ break;
+ case 0x1c8 ... 0x1cf: /* bswap reg */
+ break;
+ case 0xd6: /* salc */
+ break;
+ case 0xe0: /* loopnz */
+ case 0xe1: /* loopz */
+ case 0xe2: /* loop */
+ case 0xe3: /* jecxz */
+ goto unsupported_op;
+
+ case 0x130: /* wrmsr */
+ case 0x132: /* rdmsr */
+ goto unsupported_op;
+ case 0x131: /* rdtsc */
+ goto unsupported_op;
+ case 0x1a2: /* cpuid */
+ goto unsupported_op;
+ case 0xf4: /* hlt */
+ goto unsupported_op;
+ case 0x100:
+ goto unsupported_op;
+ case 0x101:
+ goto unsupported_op;
+ case 0x108: /* invd */
+ case 0x109: /* wbinvd */
+ goto unsupported_op;
+ case 0x63: /* arpl */
+ goto unsupported_op;
+ case 0x102: /* lar */
+ case 0x103: /* lsl */
+ goto unsupported_op;
+ case 0x118:
+ goto unsupported_op;
+ case 0x120: /* mov reg, crN */
+ case 0x122: /* mov crN, reg */
+ goto unsupported_op;
+ case 0x121: /* mov reg, drN */
+ case 0x123: /* mov drN, reg */
+ goto unsupported_op;
+ case 0x106: /* clts */
+ goto unsupported_op;
+ default:
+ goto illegal_op;
+ }
+
+ /* just copy the code */
+
+ /* no override yet */
+ if (!s->dflag)
+ gb(s, 0x66);
+ if (!s->aflag)
+ gb(s, 0x67);
+ if (prefixes & PREFIX_REPZ)
+ gb(s, 0xf3);
+ else if (prefixes & PREFIX_REPNZ)
+ gb(s, 0xf2);
+ {
+ int len, i;
+ len = s->pc - pc_start_insn;
+ for(i = 0; i < len; i++) {
+ *s->gen_code_ptr++ = ldub_code(pc_start_insn + i);
+ }
+ }
+ no_copy:
+ return 0;
+ illegal_op:
+ unsupported_op:
+ /* fall back to slower code gen necessary */
+ s->pc = pc_start;
+ return -1;
+}
+
+#define GEN_CODE_MAX_SIZE 8192
+#define GEN_CODE_MAX_INSN_SIZE 512
+
+static inline int gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ uint8_t *gen_code_ptr,
+ int *gen_code_size_ptr,
+ int search_pc,
+ uint8_t *tc_ptr)
+{
+ DisasContext dc1, *dc = &dc1;
+ target_ulong pc_insn, pc_start, cs_base;
+ uint8_t *gen_code_end;
+ int flags, ret;
+
+ if (env->nb_breakpoints > 0 ||
+ env->singlestep_enabled)
+ return -1;
+ flags = tb->flags;
+ if (flags & (HF_TF_MASK | HF_ADDSEG_MASK |
+ HF_SOFTMMU_MASK | HF_INHIBIT_IRQ_MASK))
+ return -1;
+ if (!(flags & HF_SS32_MASK))
+ return -1;
+ if (tb->cflags & CF_SINGLE_INSN)
+ return -1;
+ gen_code_end = gen_code_ptr +
+ GEN_CODE_MAX_SIZE - GEN_CODE_MAX_INSN_SIZE;
+ dc->gen_code_ptr = gen_code_ptr;
+ dc->gen_code_start = gen_code_ptr;
+
+ /* generate intermediate code */
+ pc_start = tb->pc;
+ cs_base = tb->cs_base;
+ dc->pc = pc_start;
+ dc->cs_base = cs_base;
+ dc->pe = (flags >> HF_PE_SHIFT) & 1;
+ dc->code32 = (flags >> HF_CS32_SHIFT) & 1;
+ dc->f_st = 0;
+ dc->vm86 = (flags >> VM_SHIFT) & 1;
+ dc->cpl = (flags >> HF_CPL_SHIFT) & 3;
+ dc->iopl = (flags >> IOPL_SHIFT) & 3;
+ dc->tb = tb;
+ dc->flags = flags;
+
+ dc->is_jmp = 0;
+
+ for(;;) {
+ pc_insn = dc->pc;
+ ret = disas_insn(dc);
+ if (ret < 0) {
+ /* unsupported insn */
+ if (dc->pc == pc_start) {
+ /* if first instruction, signal that no copying was done */
+ return -1;
+ } else {
+ gen_jmp(dc, dc->pc - dc->cs_base);
+ dc->is_jmp = 1;
+ }
+ }
+ if (search_pc) {
+ /* search pc mode */
+ if (tc_ptr < dc->gen_code_ptr) {
+ env->eip = pc_insn - cs_base;
+ return 0;
+ }
+ }
+ /* stop translation if indicated */
+ if (dc->is_jmp)
+ break;
+ /* if too long translation, stop generation */
+ if (dc->gen_code_ptr >= gen_code_end ||
+ (dc->pc - pc_start) >= (TARGET_PAGE_SIZE - 32)) {
+ gen_jmp(dc, dc->pc - dc->cs_base);
+ break;
+ }
+ }
+
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "----------------\n");
+ fprintf(logfile, "IN: COPY: %s fpu=%d\n",
+ lookup_symbol(pc_start),
+ tb->cflags & CF_TB_FP_USED ? 1 : 0);
+ target_disas(logfile, pc_start, dc->pc - pc_start, !dc->code32);
+ fprintf(logfile, "\n");
+ }
+#endif
+
+ if (!search_pc) {
+ *gen_code_size_ptr = dc->gen_code_ptr - dc->gen_code_start;
+ tb->size = dc->pc - pc_start;
+ tb->cflags |= CF_CODE_COPY;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* generate code by just copying data. Return -1 if cannot generate
+ any code. Return 0 if code was generated */
+int cpu_gen_code_copy(CPUState *env, TranslationBlock *tb,
+ int max_code_size, int *gen_code_size_ptr)
+{
+ /* generate machine code */
+ tb->tb_next_offset[0] = 0xffff;
+ tb->tb_next_offset[1] = 0xffff;
+#ifdef USE_DIRECT_JUMP
+ /* the following two entries are optional (only used for string ops) */
+ tb->tb_jmp_offset[2] = 0xffff;
+ tb->tb_jmp_offset[3] = 0xffff;
+#endif
+ return gen_intermediate_code_internal(env, tb,
+ tb->tc_ptr, gen_code_size_ptr,
+ 0, NULL);
+}
+
+static uint8_t dummy_gen_code_buf[GEN_CODE_MAX_SIZE];
+
+int cpu_restore_state_copy(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+{
+ struct ucontext *uc = puc;
+ int ret, eflags;
+
+ /* find opc index corresponding to search_pc */
+ if (searched_pc < (unsigned long)tb->tc_ptr)
+ return -1;
+ searched_pc = searched_pc - (long)tb->tc_ptr + (long)dummy_gen_code_buf;
+ ret = gen_intermediate_code_internal(env, tb,
+ dummy_gen_code_buf, NULL,
+ 1, (uint8_t *)searched_pc);
+ if (ret < 0)
+ return ret;
+ /* restore all the CPU state from the CPU context from the
+ signal. The FPU context stays in the host CPU. */
+
+ env->regs[R_EAX] = uc->uc_mcontext.gregs[REG_EAX];
+ env->regs[R_ECX] = uc->uc_mcontext.gregs[REG_ECX];
+ env->regs[R_EDX] = uc->uc_mcontext.gregs[REG_EDX];
+ env->regs[R_EBX] = uc->uc_mcontext.gregs[REG_EBX];
+ env->regs[R_ESP] = uc->uc_mcontext.gregs[REG_ESP];
+ env->regs[R_EBP] = uc->uc_mcontext.gregs[REG_EBP];
+ env->regs[R_ESI] = uc->uc_mcontext.gregs[REG_ESI];
+ env->regs[R_EDI] = uc->uc_mcontext.gregs[REG_EDI];
+ eflags = uc->uc_mcontext.gregs[REG_EFL];
+ env->df = 1 - (2 * ((eflags >> 10) & 1));
+ env->cc_src = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ env->cc_op = CC_OP_EFLAGS;
+ return 0;
+}
+
+#endif /* USE_CODE_COPY */
diff --git a/target-i386/translate.c b/target-i386/translate.c
new file mode 100644
index 0000000..f905f32
--- /dev/null
+++ b/target-i386/translate.c
@@ -0,0 +1,6523 @@
+/*
+ * i386 translation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+/* XXX: move that elsewhere */
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+
+#define PREFIX_REPZ 0x01
+#define PREFIX_REPNZ 0x02
+#define PREFIX_LOCK 0x04
+#define PREFIX_DATA 0x08
+#define PREFIX_ADR 0x10
+
+#ifdef TARGET_X86_64
+#define X86_64_ONLY(x) x
+#define X86_64_DEF(x...) x
+#define CODE64(s) ((s)->code64)
+#define REX_X(s) ((s)->rex_x)
+#define REX_B(s) ((s)->rex_b)
+/* XXX: gcc generates push/pop in some opcodes, so we cannot use them */
+#if 1
+#define BUGGY_64(x) NULL
+#endif
+#else
+#define X86_64_ONLY(x) NULL
+#define X86_64_DEF(x...)
+#define CODE64(s) 0
+#define REX_X(s) 0
+#define REX_B(s) 0
+#endif
+
+#ifdef TARGET_X86_64
+static int x86_64_hregs;
+#endif
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+typedef struct DisasContext {
+ /* current insn context */
+ int override; /* -1 if no override */
+ int prefix;
+ int aflag, dflag;
+ target_ulong pc; /* pc = eip + cs_base */
+ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
+ static state change (stop translation) */
+ /* current block context */
+ target_ulong cs_base; /* base of CS segment */
+ int pe; /* protected mode */
+ int code32; /* 32 bit code segment */
+#ifdef TARGET_X86_64
+ int lma; /* long mode active */
+ int code64; /* 64 bit code segment */
+ int rex_x, rex_b;
+#endif
+ int ss32; /* 32 bit stack segment */
+ int cc_op; /* current CC operation */
+ int addseg; /* non zero if either DS/ES/SS have a non zero base */
+ int f_st; /* currently unused */
+ int vm86; /* vm86 mode */
+ int cpl;
+ int iopl;
+ int tf; /* TF cpu flag */
+ int singlestep_enabled; /* "hardware" single step enabled */
+ int jmp_opt; /* use direct block chaining for direct jumps */
+ int mem_index; /* select memory access functions */
+ int flags; /* all execution flags */
+ struct TranslationBlock *tb;
+ int popl_esp_hack; /* for correct popl with esp base handling */
+ int rip_offset; /* only used in x86_64, but left for simplicity */
+ int cpuid_features;
+ int cpuid_ext_features;
+} DisasContext;
+
+static void gen_eob(DisasContext *s);
+static void gen_jmp(DisasContext *s, target_ulong eip);
+static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num);
+
+/* i386 arith/logic operations */
+enum {
+ OP_ADDL,
+ OP_ORL,
+ OP_ADCL,
+ OP_SBBL,
+ OP_ANDL,
+ OP_SUBL,
+ OP_XORL,
+ OP_CMPL,
+};
+
+/* i386 shift ops */
+enum {
+ OP_ROL,
+ OP_ROR,
+ OP_RCL,
+ OP_RCR,
+ OP_SHL,
+ OP_SHR,
+ OP_SHL1, /* undocumented */
+ OP_SAR = 7,
+};
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+#include "gen-op.h"
+
+/* operand size */
+enum {
+ OT_BYTE = 0,
+ OT_WORD,
+ OT_LONG,
+ OT_QUAD,
+};
+
+enum {
+ /* I386 int registers */
+ OR_EAX, /* MUST be even numbered */
+ OR_ECX,
+ OR_EDX,
+ OR_EBX,
+ OR_ESP,
+ OR_EBP,
+ OR_ESI,
+ OR_EDI,
+
+ OR_TMP0 = 16, /* temporary operand register */
+ OR_TMP1,
+ OR_A0, /* temporary register used when doing address evaluation */
+};
+
+#ifdef TARGET_X86_64
+
+#define NB_OP_SIZES 4
+
+#define DEF_REGS(prefix, suffix) \
+ prefix ## EAX ## suffix,\
+ prefix ## ECX ## suffix,\
+ prefix ## EDX ## suffix,\
+ prefix ## EBX ## suffix,\
+ prefix ## ESP ## suffix,\
+ prefix ## EBP ## suffix,\
+ prefix ## ESI ## suffix,\
+ prefix ## EDI ## suffix,\
+ prefix ## R8 ## suffix,\
+ prefix ## R9 ## suffix,\
+ prefix ## R10 ## suffix,\
+ prefix ## R11 ## suffix,\
+ prefix ## R12 ## suffix,\
+ prefix ## R13 ## suffix,\
+ prefix ## R14 ## suffix,\
+ prefix ## R15 ## suffix,
+
+#define DEF_BREGS(prefixb, prefixh, suffix) \
+ \
+static void prefixb ## ESP ## suffix ## _wrapper(void) \
+{ \
+ if (x86_64_hregs) \
+ prefixb ## ESP ## suffix (); \
+ else \
+ prefixh ## EAX ## suffix (); \
+} \
+ \
+static void prefixb ## EBP ## suffix ## _wrapper(void) \
+{ \
+ if (x86_64_hregs) \
+ prefixb ## EBP ## suffix (); \
+ else \
+ prefixh ## ECX ## suffix (); \
+} \
+ \
+static void prefixb ## ESI ## suffix ## _wrapper(void) \
+{ \
+ if (x86_64_hregs) \
+ prefixb ## ESI ## suffix (); \
+ else \
+ prefixh ## EDX ## suffix (); \
+} \
+ \
+static void prefixb ## EDI ## suffix ## _wrapper(void) \
+{ \
+ if (x86_64_hregs) \
+ prefixb ## EDI ## suffix (); \
+ else \
+ prefixh ## EBX ## suffix (); \
+}
+
+DEF_BREGS(gen_op_movb_, gen_op_movh_, _T0)
+DEF_BREGS(gen_op_movb_, gen_op_movh_, _T1)
+DEF_BREGS(gen_op_movl_T0_, gen_op_movh_T0_, )
+DEF_BREGS(gen_op_movl_T1_, gen_op_movh_T1_, )
+
+#else /* !TARGET_X86_64 */
+
+#define NB_OP_SIZES 3
+
+#define DEF_REGS(prefix, suffix) \
+ prefix ## EAX ## suffix,\
+ prefix ## ECX ## suffix,\
+ prefix ## EDX ## suffix,\
+ prefix ## EBX ## suffix,\
+ prefix ## ESP ## suffix,\
+ prefix ## EBP ## suffix,\
+ prefix ## ESI ## suffix,\
+ prefix ## EDI ## suffix,
+
+#endif /* !TARGET_X86_64 */
+
+static GenOpFunc *gen_op_mov_reg_T0[NB_OP_SIZES][CPU_NB_REGS] = {
+ [OT_BYTE] = {
+ gen_op_movb_EAX_T0,
+ gen_op_movb_ECX_T0,
+ gen_op_movb_EDX_T0,
+ gen_op_movb_EBX_T0,
+#ifdef TARGET_X86_64
+ gen_op_movb_ESP_T0_wrapper,
+ gen_op_movb_EBP_T0_wrapper,
+ gen_op_movb_ESI_T0_wrapper,
+ gen_op_movb_EDI_T0_wrapper,
+ gen_op_movb_R8_T0,
+ gen_op_movb_R9_T0,
+ gen_op_movb_R10_T0,
+ gen_op_movb_R11_T0,
+ gen_op_movb_R12_T0,
+ gen_op_movb_R13_T0,
+ gen_op_movb_R14_T0,
+ gen_op_movb_R15_T0,
+#else
+ gen_op_movh_EAX_T0,
+ gen_op_movh_ECX_T0,
+ gen_op_movh_EDX_T0,
+ gen_op_movh_EBX_T0,
+#endif
+ },
+ [OT_WORD] = {
+ DEF_REGS(gen_op_movw_, _T0)
+ },
+ [OT_LONG] = {
+ DEF_REGS(gen_op_movl_, _T0)
+ },
+#ifdef TARGET_X86_64
+ [OT_QUAD] = {
+ DEF_REGS(gen_op_movq_, _T0)
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_mov_reg_T1[NB_OP_SIZES][CPU_NB_REGS] = {
+ [OT_BYTE] = {
+ gen_op_movb_EAX_T1,
+ gen_op_movb_ECX_T1,
+ gen_op_movb_EDX_T1,
+ gen_op_movb_EBX_T1,
+#ifdef TARGET_X86_64
+ gen_op_movb_ESP_T1_wrapper,
+ gen_op_movb_EBP_T1_wrapper,
+ gen_op_movb_ESI_T1_wrapper,
+ gen_op_movb_EDI_T1_wrapper,
+ gen_op_movb_R8_T1,
+ gen_op_movb_R9_T1,
+ gen_op_movb_R10_T1,
+ gen_op_movb_R11_T1,
+ gen_op_movb_R12_T1,
+ gen_op_movb_R13_T1,
+ gen_op_movb_R14_T1,
+ gen_op_movb_R15_T1,
+#else
+ gen_op_movh_EAX_T1,
+ gen_op_movh_ECX_T1,
+ gen_op_movh_EDX_T1,
+ gen_op_movh_EBX_T1,
+#endif
+ },
+ [OT_WORD] = {
+ DEF_REGS(gen_op_movw_, _T1)
+ },
+ [OT_LONG] = {
+ DEF_REGS(gen_op_movl_, _T1)
+ },
+#ifdef TARGET_X86_64
+ [OT_QUAD] = {
+ DEF_REGS(gen_op_movq_, _T1)
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_mov_reg_A0[NB_OP_SIZES - 1][CPU_NB_REGS] = {
+ [0] = {
+ DEF_REGS(gen_op_movw_, _A0)
+ },
+ [1] = {
+ DEF_REGS(gen_op_movl_, _A0)
+ },
+#ifdef TARGET_X86_64
+ [2] = {
+ DEF_REGS(gen_op_movq_, _A0)
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_mov_TN_reg[NB_OP_SIZES][2][CPU_NB_REGS] =
+{
+ [OT_BYTE] = {
+ {
+ gen_op_movl_T0_EAX,
+ gen_op_movl_T0_ECX,
+ gen_op_movl_T0_EDX,
+ gen_op_movl_T0_EBX,
+#ifdef TARGET_X86_64
+ gen_op_movl_T0_ESP_wrapper,
+ gen_op_movl_T0_EBP_wrapper,
+ gen_op_movl_T0_ESI_wrapper,
+ gen_op_movl_T0_EDI_wrapper,
+ gen_op_movl_T0_R8,
+ gen_op_movl_T0_R9,
+ gen_op_movl_T0_R10,
+ gen_op_movl_T0_R11,
+ gen_op_movl_T0_R12,
+ gen_op_movl_T0_R13,
+ gen_op_movl_T0_R14,
+ gen_op_movl_T0_R15,
+#else
+ gen_op_movh_T0_EAX,
+ gen_op_movh_T0_ECX,
+ gen_op_movh_T0_EDX,
+ gen_op_movh_T0_EBX,
+#endif
+ },
+ {
+ gen_op_movl_T1_EAX,
+ gen_op_movl_T1_ECX,
+ gen_op_movl_T1_EDX,
+ gen_op_movl_T1_EBX,
+#ifdef TARGET_X86_64
+ gen_op_movl_T1_ESP_wrapper,
+ gen_op_movl_T1_EBP_wrapper,
+ gen_op_movl_T1_ESI_wrapper,
+ gen_op_movl_T1_EDI_wrapper,
+ gen_op_movl_T1_R8,
+ gen_op_movl_T1_R9,
+ gen_op_movl_T1_R10,
+ gen_op_movl_T1_R11,
+ gen_op_movl_T1_R12,
+ gen_op_movl_T1_R13,
+ gen_op_movl_T1_R14,
+ gen_op_movl_T1_R15,
+#else
+ gen_op_movh_T1_EAX,
+ gen_op_movh_T1_ECX,
+ gen_op_movh_T1_EDX,
+ gen_op_movh_T1_EBX,
+#endif
+ },
+ },
+ [OT_WORD] = {
+ {
+ DEF_REGS(gen_op_movl_T0_, )
+ },
+ {
+ DEF_REGS(gen_op_movl_T1_, )
+ },
+ },
+ [OT_LONG] = {
+ {
+ DEF_REGS(gen_op_movl_T0_, )
+ },
+ {
+ DEF_REGS(gen_op_movl_T1_, )
+ },
+ },
+#ifdef TARGET_X86_64
+ [OT_QUAD] = {
+ {
+ DEF_REGS(gen_op_movl_T0_, )
+ },
+ {
+ DEF_REGS(gen_op_movl_T1_, )
+ },
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_movl_A0_reg[CPU_NB_REGS] = {
+ DEF_REGS(gen_op_movl_A0_, )
+};
+
+static GenOpFunc *gen_op_addl_A0_reg_sN[4][CPU_NB_REGS] = {
+ [0] = {
+ DEF_REGS(gen_op_addl_A0_, )
+ },
+ [1] = {
+ DEF_REGS(gen_op_addl_A0_, _s1)
+ },
+ [2] = {
+ DEF_REGS(gen_op_addl_A0_, _s2)
+ },
+ [3] = {
+ DEF_REGS(gen_op_addl_A0_, _s3)
+ },
+};
+
+#ifdef TARGET_X86_64
+static GenOpFunc *gen_op_movq_A0_reg[CPU_NB_REGS] = {
+ DEF_REGS(gen_op_movq_A0_, )
+};
+
+static GenOpFunc *gen_op_addq_A0_reg_sN[4][CPU_NB_REGS] = {
+ [0] = {
+ DEF_REGS(gen_op_addq_A0_, )
+ },
+ [1] = {
+ DEF_REGS(gen_op_addq_A0_, _s1)
+ },
+ [2] = {
+ DEF_REGS(gen_op_addq_A0_, _s2)
+ },
+ [3] = {
+ DEF_REGS(gen_op_addq_A0_, _s3)
+ },
+};
+#endif
+
+static GenOpFunc *gen_op_cmov_reg_T1_T0[NB_OP_SIZES - 1][CPU_NB_REGS] = {
+ [0] = {
+ DEF_REGS(gen_op_cmovw_, _T1_T0)
+ },
+ [1] = {
+ DEF_REGS(gen_op_cmovl_, _T1_T0)
+ },
+#ifdef TARGET_X86_64
+ [2] = {
+ DEF_REGS(gen_op_cmovq_, _T1_T0)
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_arith_T0_T1_cc[8] = {
+ NULL,
+ gen_op_orl_T0_T1,
+ NULL,
+ NULL,
+ gen_op_andl_T0_T1,
+ NULL,
+ gen_op_xorl_T0_T1,
+ NULL,
+};
+
+#define DEF_ARITHC(SUFFIX)\
+ {\
+ gen_op_adcb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sbbb ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ gen_op_adcw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sbbw ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ gen_op_adcl ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sbbl ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ X86_64_ONLY(gen_op_adcq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_sbbq ## SUFFIX ## _T0_T1_cc),\
+ },
+
+static GenOpFunc *gen_op_arithc_T0_T1_cc[4][2] = {
+ DEF_ARITHC( )
+};
+
+static GenOpFunc *gen_op_arithc_mem_T0_T1_cc[3 * 4][2] = {
+ DEF_ARITHC(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_ARITHC(_kernel)
+ DEF_ARITHC(_user)
+#endif
+};
+
+static const int cc_op_arithb[8] = {
+ CC_OP_ADDB,
+ CC_OP_LOGICB,
+ CC_OP_ADDB,
+ CC_OP_SUBB,
+ CC_OP_LOGICB,
+ CC_OP_SUBB,
+ CC_OP_LOGICB,
+ CC_OP_SUBB,
+};
+
+#define DEF_CMPXCHG(SUFFIX)\
+ gen_op_cmpxchgb ## SUFFIX ## _T0_T1_EAX_cc,\
+ gen_op_cmpxchgw ## SUFFIX ## _T0_T1_EAX_cc,\
+ gen_op_cmpxchgl ## SUFFIX ## _T0_T1_EAX_cc,\
+ X86_64_ONLY(gen_op_cmpxchgq ## SUFFIX ## _T0_T1_EAX_cc),
+
+static GenOpFunc *gen_op_cmpxchg_T0_T1_EAX_cc[4] = {
+ DEF_CMPXCHG( )
+};
+
+static GenOpFunc *gen_op_cmpxchg_mem_T0_T1_EAX_cc[3 * 4] = {
+ DEF_CMPXCHG(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_CMPXCHG(_kernel)
+ DEF_CMPXCHG(_user)
+#endif
+};
+
+#define DEF_SHIFT(SUFFIX)\
+ {\
+ gen_op_rolb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rorb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rclb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rcrb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shlb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shrb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shlb ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sarb ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ gen_op_rolw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rorw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rclw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rcrw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shlw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shrw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shlw ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sarw ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ gen_op_roll ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rorl ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rcll ## SUFFIX ## _T0_T1_cc,\
+ gen_op_rcrl ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shll ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shrl ## SUFFIX ## _T0_T1_cc,\
+ gen_op_shll ## SUFFIX ## _T0_T1_cc,\
+ gen_op_sarl ## SUFFIX ## _T0_T1_cc,\
+ },\
+ {\
+ X86_64_ONLY(gen_op_rolq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_rorq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_rclq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_rcrq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_shlq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_shrq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_shlq ## SUFFIX ## _T0_T1_cc),\
+ X86_64_ONLY(gen_op_sarq ## SUFFIX ## _T0_T1_cc),\
+ },
+
+static GenOpFunc *gen_op_shift_T0_T1_cc[4][8] = {
+ DEF_SHIFT( )
+};
+
+static GenOpFunc *gen_op_shift_mem_T0_T1_cc[3 * 4][8] = {
+ DEF_SHIFT(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_SHIFT(_kernel)
+ DEF_SHIFT(_user)
+#endif
+};
+
+#define DEF_SHIFTD(SUFFIX, op)\
+ {\
+ NULL,\
+ NULL,\
+ },\
+ {\
+ gen_op_shldw ## SUFFIX ## _T0_T1_ ## op ## _cc,\
+ gen_op_shrdw ## SUFFIX ## _T0_T1_ ## op ## _cc,\
+ },\
+ {\
+ gen_op_shldl ## SUFFIX ## _T0_T1_ ## op ## _cc,\
+ gen_op_shrdl ## SUFFIX ## _T0_T1_ ## op ## _cc,\
+ },\
+ {\
+X86_64_DEF(gen_op_shldq ## SUFFIX ## _T0_T1_ ## op ## _cc,\
+ gen_op_shrdq ## SUFFIX ## _T0_T1_ ## op ## _cc,)\
+ },
+
+static GenOpFunc1 *gen_op_shiftd_T0_T1_im_cc[4][2] = {
+ DEF_SHIFTD(, im)
+};
+
+static GenOpFunc *gen_op_shiftd_T0_T1_ECX_cc[4][2] = {
+ DEF_SHIFTD(, ECX)
+};
+
+static GenOpFunc1 *gen_op_shiftd_mem_T0_T1_im_cc[3 * 4][2] = {
+ DEF_SHIFTD(_raw, im)
+#ifndef CONFIG_USER_ONLY
+ DEF_SHIFTD(_kernel, im)
+ DEF_SHIFTD(_user, im)
+#endif
+};
+
+static GenOpFunc *gen_op_shiftd_mem_T0_T1_ECX_cc[3 * 4][2] = {
+ DEF_SHIFTD(_raw, ECX)
+#ifndef CONFIG_USER_ONLY
+ DEF_SHIFTD(_kernel, ECX)
+ DEF_SHIFTD(_user, ECX)
+#endif
+};
+
+static GenOpFunc *gen_op_btx_T0_T1_cc[3][4] = {
+ [0] = {
+ gen_op_btw_T0_T1_cc,
+ gen_op_btsw_T0_T1_cc,
+ gen_op_btrw_T0_T1_cc,
+ gen_op_btcw_T0_T1_cc,
+ },
+ [1] = {
+ gen_op_btl_T0_T1_cc,
+ gen_op_btsl_T0_T1_cc,
+ gen_op_btrl_T0_T1_cc,
+ gen_op_btcl_T0_T1_cc,
+ },
+#ifdef TARGET_X86_64
+ [2] = {
+ gen_op_btq_T0_T1_cc,
+ gen_op_btsq_T0_T1_cc,
+ gen_op_btrq_T0_T1_cc,
+ gen_op_btcq_T0_T1_cc,
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_add_bit_A0_T1[3] = {
+ gen_op_add_bitw_A0_T1,
+ gen_op_add_bitl_A0_T1,
+ X86_64_ONLY(gen_op_add_bitq_A0_T1),
+};
+
+static GenOpFunc *gen_op_bsx_T0_cc[3][2] = {
+ [0] = {
+ gen_op_bsfw_T0_cc,
+ gen_op_bsrw_T0_cc,
+ },
+ [1] = {
+ gen_op_bsfl_T0_cc,
+ gen_op_bsrl_T0_cc,
+ },
+#ifdef TARGET_X86_64
+ [2] = {
+ gen_op_bsfq_T0_cc,
+ gen_op_bsrq_T0_cc,
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_lds_T0_A0[3 * 4] = {
+ gen_op_ldsb_raw_T0_A0,
+ gen_op_ldsw_raw_T0_A0,
+ X86_64_ONLY(gen_op_ldsl_raw_T0_A0),
+ NULL,
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldsb_kernel_T0_A0,
+ gen_op_ldsw_kernel_T0_A0,
+ X86_64_ONLY(gen_op_ldsl_kernel_T0_A0),
+ NULL,
+
+ gen_op_ldsb_user_T0_A0,
+ gen_op_ldsw_user_T0_A0,
+ X86_64_ONLY(gen_op_ldsl_user_T0_A0),
+ NULL,
+#endif
+};
+
+static GenOpFunc *gen_op_ldu_T0_A0[3 * 4] = {
+ gen_op_ldub_raw_T0_A0,
+ gen_op_lduw_raw_T0_A0,
+ NULL,
+ NULL,
+
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldub_kernel_T0_A0,
+ gen_op_lduw_kernel_T0_A0,
+ NULL,
+ NULL,
+
+ gen_op_ldub_user_T0_A0,
+ gen_op_lduw_user_T0_A0,
+ NULL,
+ NULL,
+#endif
+};
+
+/* sign does not matter, except for lidt/lgdt call (TODO: fix it) */
+static GenOpFunc *gen_op_ld_T0_A0[3 * 4] = {
+ gen_op_ldub_raw_T0_A0,
+ gen_op_lduw_raw_T0_A0,
+ gen_op_ldl_raw_T0_A0,
+ X86_64_ONLY(gen_op_ldq_raw_T0_A0),
+
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldub_kernel_T0_A0,
+ gen_op_lduw_kernel_T0_A0,
+ gen_op_ldl_kernel_T0_A0,
+ X86_64_ONLY(gen_op_ldq_kernel_T0_A0),
+
+ gen_op_ldub_user_T0_A0,
+ gen_op_lduw_user_T0_A0,
+ gen_op_ldl_user_T0_A0,
+ X86_64_ONLY(gen_op_ldq_user_T0_A0),
+#endif
+};
+
+static GenOpFunc *gen_op_ld_T1_A0[3 * 4] = {
+ gen_op_ldub_raw_T1_A0,
+ gen_op_lduw_raw_T1_A0,
+ gen_op_ldl_raw_T1_A0,
+ X86_64_ONLY(gen_op_ldq_raw_T1_A0),
+
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldub_kernel_T1_A0,
+ gen_op_lduw_kernel_T1_A0,
+ gen_op_ldl_kernel_T1_A0,
+ X86_64_ONLY(gen_op_ldq_kernel_T1_A0),
+
+ gen_op_ldub_user_T1_A0,
+ gen_op_lduw_user_T1_A0,
+ gen_op_ldl_user_T1_A0,
+ X86_64_ONLY(gen_op_ldq_user_T1_A0),
+#endif
+};
+
+static GenOpFunc *gen_op_st_T0_A0[3 * 4] = {
+ gen_op_stb_raw_T0_A0,
+ gen_op_stw_raw_T0_A0,
+ gen_op_stl_raw_T0_A0,
+ X86_64_ONLY(gen_op_stq_raw_T0_A0),
+
+#ifndef CONFIG_USER_ONLY
+ gen_op_stb_kernel_T0_A0,
+ gen_op_stw_kernel_T0_A0,
+ gen_op_stl_kernel_T0_A0,
+ X86_64_ONLY(gen_op_stq_kernel_T0_A0),
+
+ gen_op_stb_user_T0_A0,
+ gen_op_stw_user_T0_A0,
+ gen_op_stl_user_T0_A0,
+ X86_64_ONLY(gen_op_stq_user_T0_A0),
+#endif
+};
+
+static GenOpFunc *gen_op_st_T1_A0[3 * 4] = {
+ NULL,
+ gen_op_stw_raw_T1_A0,
+ gen_op_stl_raw_T1_A0,
+ X86_64_ONLY(gen_op_stq_raw_T1_A0),
+
+#ifndef CONFIG_USER_ONLY
+ NULL,
+ gen_op_stw_kernel_T1_A0,
+ gen_op_stl_kernel_T1_A0,
+ X86_64_ONLY(gen_op_stq_kernel_T1_A0),
+
+ NULL,
+ gen_op_stw_user_T1_A0,
+ gen_op_stl_user_T1_A0,
+ X86_64_ONLY(gen_op_stq_user_T1_A0),
+#endif
+};
+
+static inline void gen_jmp_im(target_ulong pc)
+{
+#ifdef TARGET_X86_64
+ if (pc == (uint32_t)pc) {
+ gen_op_movl_eip_im(pc);
+ } else if (pc == (int32_t)pc) {
+ gen_op_movq_eip_im(pc);
+ } else {
+ gen_op_movq_eip_im64(pc >> 32, pc);
+ }
+#else
+ gen_op_movl_eip_im(pc);
+#endif
+}
+
+static inline void gen_string_movl_A0_ESI(DisasContext *s)
+{
+ int override;
+
+ override = s->override;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ if (override >= 0) {
+ gen_op_movq_A0_seg(offsetof(CPUX86State,segs[override].base));
+ gen_op_addq_A0_reg_sN[0][R_ESI]();
+ } else {
+ gen_op_movq_A0_reg[R_ESI]();
+ }
+ } else
+#endif
+ if (s->aflag) {
+ /* 32 bit address */
+ if (s->addseg && override < 0)
+ override = R_DS;
+ if (override >= 0) {
+ gen_op_movl_A0_seg(offsetof(CPUX86State,segs[override].base));
+ gen_op_addl_A0_reg_sN[0][R_ESI]();
+ } else {
+ gen_op_movl_A0_reg[R_ESI]();
+ }
+ } else {
+ /* 16 address, always override */
+ if (override < 0)
+ override = R_DS;
+ gen_op_movl_A0_reg[R_ESI]();
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
+ }
+}
+
+static inline void gen_string_movl_A0_EDI(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg[R_EDI]();
+ } else
+#endif
+ if (s->aflag) {
+ if (s->addseg) {
+ gen_op_movl_A0_seg(offsetof(CPUX86State,segs[R_ES].base));
+ gen_op_addl_A0_reg_sN[0][R_EDI]();
+ } else {
+ gen_op_movl_A0_reg[R_EDI]();
+ }
+ } else {
+ gen_op_movl_A0_reg[R_EDI]();
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_ES].base));
+ }
+}
+
+static GenOpFunc *gen_op_movl_T0_Dshift[4] = {
+ gen_op_movl_T0_Dshiftb,
+ gen_op_movl_T0_Dshiftw,
+ gen_op_movl_T0_Dshiftl,
+ X86_64_ONLY(gen_op_movl_T0_Dshiftq),
+};
+
+static GenOpFunc1 *gen_op_jnz_ecx[3] = {
+ gen_op_jnz_ecxw,
+ gen_op_jnz_ecxl,
+ X86_64_ONLY(gen_op_jnz_ecxq),
+};
+
+static GenOpFunc1 *gen_op_jz_ecx[3] = {
+ gen_op_jz_ecxw,
+ gen_op_jz_ecxl,
+ X86_64_ONLY(gen_op_jz_ecxq),
+};
+
+static GenOpFunc *gen_op_dec_ECX[3] = {
+ gen_op_decw_ECX,
+ gen_op_decl_ECX,
+ X86_64_ONLY(gen_op_decq_ECX),
+};
+
+static GenOpFunc1 *gen_op_string_jnz_sub[2][4] = {
+ {
+ gen_op_jnz_subb,
+ gen_op_jnz_subw,
+ gen_op_jnz_subl,
+ X86_64_ONLY(gen_op_jnz_subq),
+ },
+ {
+ gen_op_jz_subb,
+ gen_op_jz_subw,
+ gen_op_jz_subl,
+ X86_64_ONLY(gen_op_jz_subq),
+ },
+};
+
+static GenOpFunc *gen_op_in_DX_T0[3] = {
+ gen_op_inb_DX_T0,
+ gen_op_inw_DX_T0,
+ gen_op_inl_DX_T0,
+};
+
+static GenOpFunc *gen_op_out_DX_T0[3] = {
+ gen_op_outb_DX_T0,
+ gen_op_outw_DX_T0,
+ gen_op_outl_DX_T0,
+};
+
+static GenOpFunc *gen_op_in[3] = {
+ gen_op_inb_T0_T1,
+ gen_op_inw_T0_T1,
+ gen_op_inl_T0_T1,
+};
+
+static GenOpFunc *gen_op_out[3] = {
+ gen_op_outb_T0_T1,
+ gen_op_outw_T0_T1,
+ gen_op_outl_T0_T1,
+};
+
+static GenOpFunc *gen_check_io_T0[3] = {
+ gen_op_check_iob_T0,
+ gen_op_check_iow_T0,
+ gen_op_check_iol_T0,
+};
+
+static GenOpFunc *gen_check_io_DX[3] = {
+ gen_op_check_iob_DX,
+ gen_op_check_iow_DX,
+ gen_op_check_iol_DX,
+};
+
+static void gen_check_io(DisasContext *s, int ot, int use_dx, target_ulong cur_eip)
+{
+ if (s->pe && (s->cpl > s->iopl || s->vm86)) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ if (use_dx)
+ gen_check_io_DX[ot]();
+ else
+ gen_check_io_T0[ot]();
+ }
+}
+
+static inline void gen_movs(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_string_movl_A0_EDI(s);
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_ESI_T0();
+ gen_op_addq_EDI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_ESI_T0();
+ gen_op_addl_EDI_T0();
+ } else {
+ gen_op_addw_ESI_T0();
+ gen_op_addw_EDI_T0();
+ }
+}
+
+static inline void gen_update_cc_op(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+}
+
+/* XXX: does not work with gdbstub "ice" single step - not a
+ serious problem */
+static int gen_jz_ecx_string(DisasContext *s, target_ulong next_eip)
+{
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ gen_op_jnz_ecx[s->aflag](l1);
+ gen_set_label(l2);
+ gen_jmp_tb(s, next_eip, 1);
+ gen_set_label(l1);
+ return l2;
+}
+
+static inline void gen_stos(DisasContext *s, int ot)
+{
+ gen_op_mov_TN_reg[OT_LONG][0][R_EAX]();
+ gen_string_movl_A0_EDI(s);
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_EDI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_EDI_T0();
+ } else {
+ gen_op_addw_EDI_T0();
+ }
+}
+
+static inline void gen_lods(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_op_mov_reg_T0[ot][R_EAX]();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_ESI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_ESI_T0();
+ } else {
+ gen_op_addw_ESI_T0();
+ }
+}
+
+static inline void gen_scas(DisasContext *s, int ot)
+{
+ gen_op_mov_TN_reg[OT_LONG][0][R_EAX]();
+ gen_string_movl_A0_EDI(s);
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_op_cmpl_T0_T1_cc();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_EDI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_EDI_T0();
+ } else {
+ gen_op_addw_EDI_T0();
+ }
+}
+
+static inline void gen_cmps(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_string_movl_A0_EDI(s);
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_op_cmpl_T0_T1_cc();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_ESI_T0();
+ gen_op_addq_EDI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_ESI_T0();
+ gen_op_addl_EDI_T0();
+ } else {
+ gen_op_addw_ESI_T0();
+ gen_op_addw_EDI_T0();
+ }
+}
+
+static inline void gen_ins(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_EDI(s);
+ gen_op_movl_T0_0();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ gen_op_in_DX_T0[ot]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_EDI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_EDI_T0();
+ } else {
+ gen_op_addw_EDI_T0();
+ }
+}
+
+static inline void gen_outs(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_op_out_DX_T0[ot]();
+ gen_op_movl_T0_Dshift[ot]();
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_ESI_T0();
+ } else
+#endif
+ if (s->aflag) {
+ gen_op_addl_ESI_T0();
+ } else {
+ gen_op_addw_ESI_T0();
+ }
+}
+
+/* same method as Valgrind : we generate jumps to current or next
+ instruction */
+#define GEN_REPZ(op) \
+static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+ target_ulong cur_eip, target_ulong next_eip) \
+{ \
+ int l2;\
+ gen_update_cc_op(s); \
+ l2 = gen_jz_ecx_string(s, next_eip); \
+ gen_ ## op(s, ot); \
+ gen_op_dec_ECX[s->aflag](); \
+ /* a loop would cause two single step exceptions if ECX = 1 \
+ before rep string_insn */ \
+ if (!s->jmp_opt) \
+ gen_op_jz_ecx[s->aflag](l2); \
+ gen_jmp(s, cur_eip); \
+}
+
+#define GEN_REPZ2(op) \
+static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+ target_ulong cur_eip, \
+ target_ulong next_eip, \
+ int nz) \
+{ \
+ int l2;\
+ gen_update_cc_op(s); \
+ l2 = gen_jz_ecx_string(s, next_eip); \
+ gen_ ## op(s, ot); \
+ gen_op_dec_ECX[s->aflag](); \
+ gen_op_set_cc_op(CC_OP_SUBB + ot); \
+ gen_op_string_jnz_sub[nz][ot](l2);\
+ if (!s->jmp_opt) \
+ gen_op_jz_ecx[s->aflag](l2); \
+ gen_jmp(s, cur_eip); \
+}
+
+GEN_REPZ(movs)
+GEN_REPZ(stos)
+GEN_REPZ(lods)
+GEN_REPZ(ins)
+GEN_REPZ(outs)
+GEN_REPZ2(scas)
+GEN_REPZ2(cmps)
+
+enum {
+ JCC_O,
+ JCC_B,
+ JCC_Z,
+ JCC_BE,
+ JCC_S,
+ JCC_P,
+ JCC_L,
+ JCC_LE,
+};
+
+static GenOpFunc1 *gen_jcc_sub[4][8] = {
+ [OT_BYTE] = {
+ NULL,
+ gen_op_jb_subb,
+ gen_op_jz_subb,
+ gen_op_jbe_subb,
+ gen_op_js_subb,
+ NULL,
+ gen_op_jl_subb,
+ gen_op_jle_subb,
+ },
+ [OT_WORD] = {
+ NULL,
+ gen_op_jb_subw,
+ gen_op_jz_subw,
+ gen_op_jbe_subw,
+ gen_op_js_subw,
+ NULL,
+ gen_op_jl_subw,
+ gen_op_jle_subw,
+ },
+ [OT_LONG] = {
+ NULL,
+ gen_op_jb_subl,
+ gen_op_jz_subl,
+ gen_op_jbe_subl,
+ gen_op_js_subl,
+ NULL,
+ gen_op_jl_subl,
+ gen_op_jle_subl,
+ },
+#ifdef TARGET_X86_64
+ [OT_QUAD] = {
+ NULL,
+ BUGGY_64(gen_op_jb_subq),
+ gen_op_jz_subq,
+ BUGGY_64(gen_op_jbe_subq),
+ gen_op_js_subq,
+ NULL,
+ BUGGY_64(gen_op_jl_subq),
+ BUGGY_64(gen_op_jle_subq),
+ },
+#endif
+};
+static GenOpFunc1 *gen_op_loop[3][4] = {
+ [0] = {
+ gen_op_loopnzw,
+ gen_op_loopzw,
+ gen_op_jnz_ecxw,
+ },
+ [1] = {
+ gen_op_loopnzl,
+ gen_op_loopzl,
+ gen_op_jnz_ecxl,
+ },
+#ifdef TARGET_X86_64
+ [2] = {
+ gen_op_loopnzq,
+ gen_op_loopzq,
+ gen_op_jnz_ecxq,
+ },
+#endif
+};
+
+static GenOpFunc *gen_setcc_slow[8] = {
+ gen_op_seto_T0_cc,
+ gen_op_setb_T0_cc,
+ gen_op_setz_T0_cc,
+ gen_op_setbe_T0_cc,
+ gen_op_sets_T0_cc,
+ gen_op_setp_T0_cc,
+ gen_op_setl_T0_cc,
+ gen_op_setle_T0_cc,
+};
+
+static GenOpFunc *gen_setcc_sub[4][8] = {
+ [OT_BYTE] = {
+ NULL,
+ gen_op_setb_T0_subb,
+ gen_op_setz_T0_subb,
+ gen_op_setbe_T0_subb,
+ gen_op_sets_T0_subb,
+ NULL,
+ gen_op_setl_T0_subb,
+ gen_op_setle_T0_subb,
+ },
+ [OT_WORD] = {
+ NULL,
+ gen_op_setb_T0_subw,
+ gen_op_setz_T0_subw,
+ gen_op_setbe_T0_subw,
+ gen_op_sets_T0_subw,
+ NULL,
+ gen_op_setl_T0_subw,
+ gen_op_setle_T0_subw,
+ },
+ [OT_LONG] = {
+ NULL,
+ gen_op_setb_T0_subl,
+ gen_op_setz_T0_subl,
+ gen_op_setbe_T0_subl,
+ gen_op_sets_T0_subl,
+ NULL,
+ gen_op_setl_T0_subl,
+ gen_op_setle_T0_subl,
+ },
+#ifdef TARGET_X86_64
+ [OT_QUAD] = {
+ NULL,
+ gen_op_setb_T0_subq,
+ gen_op_setz_T0_subq,
+ gen_op_setbe_T0_subq,
+ gen_op_sets_T0_subq,
+ NULL,
+ gen_op_setl_T0_subq,
+ gen_op_setle_T0_subq,
+ },
+#endif
+};
+
+static GenOpFunc *gen_op_fp_arith_ST0_FT0[8] = {
+ gen_op_fadd_ST0_FT0,
+ gen_op_fmul_ST0_FT0,
+ gen_op_fcom_ST0_FT0,
+ gen_op_fcom_ST0_FT0,
+ gen_op_fsub_ST0_FT0,
+ gen_op_fsubr_ST0_FT0,
+ gen_op_fdiv_ST0_FT0,
+ gen_op_fdivr_ST0_FT0,
+};
+
+/* NOTE the exception in "r" op ordering */
+static GenOpFunc1 *gen_op_fp_arith_STN_ST0[8] = {
+ gen_op_fadd_STN_ST0,
+ gen_op_fmul_STN_ST0,
+ NULL,
+ NULL,
+ gen_op_fsubr_STN_ST0,
+ gen_op_fsub_STN_ST0,
+ gen_op_fdivr_STN_ST0,
+ gen_op_fdiv_STN_ST0,
+};
+
+/* if d == OR_TMP0, it means memory operand (address in A0) */
+static void gen_op(DisasContext *s1, int op, int ot, int d)
+{
+ GenOpFunc *gen_update_cc;
+
+ if (d != OR_TMP0) {
+ gen_op_mov_TN_reg[ot][0][d]();
+ } else {
+ gen_op_ld_T0_A0[ot + s1->mem_index]();
+ }
+ switch(op) {
+ case OP_ADCL:
+ case OP_SBBL:
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+ if (d != OR_TMP0) {
+ gen_op_arithc_T0_T1_cc[ot][op - OP_ADCL]();
+ gen_op_mov_reg_T0[ot][d]();
+ } else {
+ gen_op_arithc_mem_T0_T1_cc[ot + s1->mem_index][op - OP_ADCL]();
+ }
+ s1->cc_op = CC_OP_DYNAMIC;
+ goto the_end;
+ case OP_ADDL:
+ gen_op_addl_T0_T1();
+ s1->cc_op = CC_OP_ADDB + ot;
+ gen_update_cc = gen_op_update2_cc;
+ break;
+ case OP_SUBL:
+ gen_op_subl_T0_T1();
+ s1->cc_op = CC_OP_SUBB + ot;
+ gen_update_cc = gen_op_update2_cc;
+ break;
+ default:
+ case OP_ANDL:
+ case OP_ORL:
+ case OP_XORL:
+ gen_op_arith_T0_T1_cc[op]();
+ s1->cc_op = CC_OP_LOGICB + ot;
+ gen_update_cc = gen_op_update1_cc;
+ break;
+ case OP_CMPL:
+ gen_op_cmpl_T0_T1_cc();
+ s1->cc_op = CC_OP_SUBB + ot;
+ gen_update_cc = NULL;
+ break;
+ }
+ if (op != OP_CMPL) {
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0[ot][d]();
+ else
+ gen_op_st_T0_A0[ot + s1->mem_index]();
+ }
+ /* the flags update must happen after the memory write (precise
+ exception support) */
+ if (gen_update_cc)
+ gen_update_cc();
+ the_end: ;
+}
+
+/* if d == OR_TMP0, it means memory operand (address in A0) */
+static void gen_inc(DisasContext *s1, int ot, int d, int c)
+{
+ if (d != OR_TMP0)
+ gen_op_mov_TN_reg[ot][0][d]();
+ else
+ gen_op_ld_T0_A0[ot + s1->mem_index]();
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+ if (c > 0) {
+ gen_op_incl_T0();
+ s1->cc_op = CC_OP_INCB + ot;
+ } else {
+ gen_op_decl_T0();
+ s1->cc_op = CC_OP_DECB + ot;
+ }
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0[ot][d]();
+ else
+ gen_op_st_T0_A0[ot + s1->mem_index]();
+ gen_op_update_inc_cc();
+}
+
+static void gen_shift(DisasContext *s1, int op, int ot, int d, int s)
+{
+ if (d != OR_TMP0)
+ gen_op_mov_TN_reg[ot][0][d]();
+ else
+ gen_op_ld_T0_A0[ot + s1->mem_index]();
+ if (s != OR_TMP1)
+ gen_op_mov_TN_reg[ot][1][s]();
+ /* for zero counts, flags are not updated, so must do it dynamically */
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+
+ if (d != OR_TMP0)
+ gen_op_shift_T0_T1_cc[ot][op]();
+ else
+ gen_op_shift_mem_T0_T1_cc[ot + s1->mem_index][op]();
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0[ot][d]();
+ s1->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+}
+
+static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c)
+{
+ /* currently not optimized */
+ gen_op_movl_T1_im(c);
+ gen_shift(s1, op, ot, d, OR_TMP1);
+}
+
+static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
+{
+ target_long disp;
+ int havesib;
+ int base;
+ int index;
+ int scale;
+ int opreg;
+ int mod, rm, code, override, must_add_seg;
+
+ override = s->override;
+ must_add_seg = s->addseg;
+ if (override >= 0)
+ must_add_seg = 1;
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+
+ if (s->aflag) {
+
+ havesib = 0;
+ base = rm;
+ index = 0;
+ scale = 0;
+
+ if (base == 4) {
+ havesib = 1;
+ code = ldub_code(s->pc++);
+ scale = (code >> 6) & 3;
+ index = ((code >> 3) & 7) | REX_X(s);
+ base = (code & 7);
+ }
+ base |= REX_B(s);
+
+ switch (mod) {
+ case 0:
+ if ((base & 7) == 5) {
+ base = -1;
+ disp = (int32_t)ldl_code(s->pc);
+ s->pc += 4;
+ if (CODE64(s) && !havesib) {
+ disp += s->pc + s->rip_offset;
+ }
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+
+ if (base >= 0) {
+ /* for correct popl handling with esp */
+ if (base == 4 && s->popl_esp_hack)
+ disp += s->popl_esp_hack;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg[base]();
+ if (disp != 0) {
+ if ((int32_t)disp == disp)
+ gen_op_addq_A0_im(disp);
+ else
+ gen_op_addq_A0_im64(disp >> 32, disp);
+ }
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[base]();
+ if (disp != 0)
+ gen_op_addl_A0_im(disp);
+ }
+ } else {
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ if ((int32_t)disp == disp)
+ gen_op_movq_A0_im(disp);
+ else
+ gen_op_movq_A0_im64(disp >> 32, disp);
+ } else
+#endif
+ {
+ gen_op_movl_A0_im(disp);
+ }
+ }
+ /* XXX: index == 4 is always invalid */
+ if (havesib && (index != 4 || scale != 0)) {
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_A0_reg_sN[scale][index]();
+ } else
+#endif
+ {
+ gen_op_addl_A0_reg_sN[scale][index]();
+ }
+ }
+ if (must_add_seg) {
+ if (override < 0) {
+ if (base == R_EBP || base == R_ESP)
+ override = R_SS;
+ else
+ override = R_DS;
+ }
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_A0_seg(offsetof(CPUX86State,segs[override].base));
+ } else
+#endif
+ {
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
+ }
+ }
+ } else {
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ gen_op_movl_A0_im(disp);
+ rm = 0; /* avoid SS override */
+ goto no_rm;
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ }
+ switch(rm) {
+ case 0:
+ gen_op_movl_A0_reg[R_EBX]();
+ gen_op_addl_A0_reg_sN[0][R_ESI]();
+ break;
+ case 1:
+ gen_op_movl_A0_reg[R_EBX]();
+ gen_op_addl_A0_reg_sN[0][R_EDI]();
+ break;
+ case 2:
+ gen_op_movl_A0_reg[R_EBP]();
+ gen_op_addl_A0_reg_sN[0][R_ESI]();
+ break;
+ case 3:
+ gen_op_movl_A0_reg[R_EBP]();
+ gen_op_addl_A0_reg_sN[0][R_EDI]();
+ break;
+ case 4:
+ gen_op_movl_A0_reg[R_ESI]();
+ break;
+ case 5:
+ gen_op_movl_A0_reg[R_EDI]();
+ break;
+ case 6:
+ gen_op_movl_A0_reg[R_EBP]();
+ break;
+ default:
+ case 7:
+ gen_op_movl_A0_reg[R_EBX]();
+ break;
+ }
+ if (disp != 0)
+ gen_op_addl_A0_im(disp);
+ gen_op_andl_A0_ffff();
+ no_rm:
+ if (must_add_seg) {
+ if (override < 0) {
+ if (rm == 2 || rm == 3 || rm == 6)
+ override = R_SS;
+ else
+ override = R_DS;
+ }
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
+ }
+ }
+
+ opreg = OR_A0;
+ disp = 0;
+ *reg_ptr = opreg;
+ *offset_ptr = disp;
+}
+
+/* used for LEA and MOV AX, mem */
+static void gen_add_A0_ds_seg(DisasContext *s)
+{
+ int override, must_add_seg;
+ must_add_seg = s->addseg;
+ override = R_DS;
+ if (s->override >= 0) {
+ override = s->override;
+ must_add_seg = 1;
+ } else {
+ override = R_DS;
+ }
+ if (must_add_seg) {
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_addq_A0_seg(offsetof(CPUX86State,segs[override].base));
+ } else
+#endif
+ {
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
+ }
+ }
+}
+
+/* generate modrm memory load or store of 'reg'. TMP0 is used if reg !=
+ OR_TMP0 */
+static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store)
+{
+ int mod, rm, opreg, disp;
+
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod == 3) {
+ if (is_store) {
+ if (reg != OR_TMP0)
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_op_mov_reg_T0[ot][rm]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ if (reg != OR_TMP0)
+ gen_op_mov_reg_T0[ot][reg]();
+ }
+ } else {
+ gen_lea_modrm(s, modrm, &opreg, &disp);
+ if (is_store) {
+ if (reg != OR_TMP0)
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ if (reg != OR_TMP0)
+ gen_op_mov_reg_T0[ot][reg]();
+ }
+ }
+}
+
+static inline uint32_t insn_get(DisasContext *s, int ot)
+{
+ uint32_t ret;
+
+ switch(ot) {
+ case OT_BYTE:
+ ret = ldub_code(s->pc);
+ s->pc++;
+ break;
+ case OT_WORD:
+ ret = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ default:
+ case OT_LONG:
+ ret = ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+ return ret;
+}
+
+static inline int insn_const_size(unsigned int ot)
+{
+ if (ot <= OT_LONG)
+ return 1 << ot;
+ else
+ return 4;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip)
+{
+ TranslationBlock *tb;
+ target_ulong pc;
+
+ pc = s->cs_base + eip;
+ tb = s->tb;
+ /* NOTE: we handle the case where the TB spans two pages here */
+ if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) ||
+ (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) {
+ /* jump to same page: we can use a direct jump */
+ if (tb_num == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_jmp_im(eip);
+ gen_op_movl_T0_im((long)tb + tb_num);
+ gen_op_exit_tb();
+ } else {
+ /* jump to another page: currently not optimized */
+ gen_jmp_im(eip);
+ gen_eob(s);
+ }
+}
+
+static inline void gen_jcc(DisasContext *s, int b,
+ target_ulong val, target_ulong next_eip)
+{
+ TranslationBlock *tb;
+ int inv, jcc_op;
+ GenOpFunc1 *func;
+ target_ulong tmp;
+ int l1, l2;
+
+ inv = b & 1;
+ jcc_op = (b >> 1) & 7;
+
+ if (s->jmp_opt) {
+ switch(s->cc_op) {
+ /* we optimize the cmp/jcc case */
+ case CC_OP_SUBB:
+ case CC_OP_SUBW:
+ case CC_OP_SUBL:
+ case CC_OP_SUBQ:
+ func = gen_jcc_sub[s->cc_op - CC_OP_SUBB][jcc_op];
+ break;
+
+ /* some jumps are easy to compute */
+ case CC_OP_ADDB:
+ case CC_OP_ADDW:
+ case CC_OP_ADDL:
+ case CC_OP_ADDQ:
+
+ case CC_OP_ADCB:
+ case CC_OP_ADCW:
+ case CC_OP_ADCL:
+ case CC_OP_ADCQ:
+
+ case CC_OP_SBBB:
+ case CC_OP_SBBW:
+ case CC_OP_SBBL:
+ case CC_OP_SBBQ:
+
+ case CC_OP_LOGICB:
+ case CC_OP_LOGICW:
+ case CC_OP_LOGICL:
+ case CC_OP_LOGICQ:
+
+ case CC_OP_INCB:
+ case CC_OP_INCW:
+ case CC_OP_INCL:
+ case CC_OP_INCQ:
+
+ case CC_OP_DECB:
+ case CC_OP_DECW:
+ case CC_OP_DECL:
+ case CC_OP_DECQ:
+
+ case CC_OP_SHLB:
+ case CC_OP_SHLW:
+ case CC_OP_SHLL:
+ case CC_OP_SHLQ:
+
+ case CC_OP_SARB:
+ case CC_OP_SARW:
+ case CC_OP_SARL:
+ case CC_OP_SARQ:
+ switch(jcc_op) {
+ case JCC_Z:
+ func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op];
+ break;
+ case JCC_S:
+ func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op];
+ break;
+ default:
+ func = NULL;
+ break;
+ }
+ break;
+ default:
+ func = NULL;
+ break;
+ }
+
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+
+ if (!func) {
+ gen_setcc_slow[jcc_op]();
+ func = gen_op_jnz_T0_label;
+ }
+
+ if (inv) {
+ tmp = val;
+ val = next_eip;
+ next_eip = tmp;
+ }
+ tb = s->tb;
+
+ l1 = gen_new_label();
+ func(l1);
+
+ gen_goto_tb(s, 0, next_eip);
+
+ gen_set_label(l1);
+ gen_goto_tb(s, 1, val);
+
+ s->is_jmp = 3;
+ } else {
+
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_setcc_slow[jcc_op]();
+ if (inv) {
+ tmp = val;
+ val = next_eip;
+ next_eip = tmp;
+ }
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ gen_op_jnz_T0_label(l1);
+ gen_jmp_im(next_eip);
+ gen_op_jmp_label(l2);
+ gen_set_label(l1);
+ gen_jmp_im(val);
+ gen_set_label(l2);
+ gen_eob(s);
+ }
+}
+
+static void gen_setcc(DisasContext *s, int b)
+{
+ int inv, jcc_op;
+ GenOpFunc *func;
+
+ inv = b & 1;
+ jcc_op = (b >> 1) & 7;
+ switch(s->cc_op) {
+ /* we optimize the cmp/jcc case */
+ case CC_OP_SUBB:
+ case CC_OP_SUBW:
+ case CC_OP_SUBL:
+ case CC_OP_SUBQ:
+ func = gen_setcc_sub[s->cc_op - CC_OP_SUBB][jcc_op];
+ if (!func)
+ goto slow_jcc;
+ break;
+
+ /* some jumps are easy to compute */
+ case CC_OP_ADDB:
+ case CC_OP_ADDW:
+ case CC_OP_ADDL:
+ case CC_OP_ADDQ:
+
+ case CC_OP_LOGICB:
+ case CC_OP_LOGICW:
+ case CC_OP_LOGICL:
+ case CC_OP_LOGICQ:
+
+ case CC_OP_INCB:
+ case CC_OP_INCW:
+ case CC_OP_INCL:
+ case CC_OP_INCQ:
+
+ case CC_OP_DECB:
+ case CC_OP_DECW:
+ case CC_OP_DECL:
+ case CC_OP_DECQ:
+
+ case CC_OP_SHLB:
+ case CC_OP_SHLW:
+ case CC_OP_SHLL:
+ case CC_OP_SHLQ:
+ switch(jcc_op) {
+ case JCC_Z:
+ func = gen_setcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op];
+ break;
+ case JCC_S:
+ func = gen_setcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op];
+ break;
+ default:
+ goto slow_jcc;
+ }
+ break;
+ default:
+ slow_jcc:
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ func = gen_setcc_slow[jcc_op];
+ break;
+ }
+ func();
+ if (inv) {
+ gen_op_xor_T0_1();
+ }
+}
+
+/* move T0 to seg_reg and compute if the CPU state may change. Never
+ call this function with seg_reg == R_CS */
+static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip)
+{
+ if (s->pe && !s->vm86) {
+ /* XXX: optimize by finding processor state dynamically */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_op_movl_seg_T0(seg_reg);
+ /* abort translation because the addseg value may change or
+ because ss32 may change. For R_SS, translation must always
+ stop as a special handling must be done to disable hardware
+ interrupts for the next instruction */
+ if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS))
+ s->is_jmp = 3;
+ } else {
+ gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg]));
+ if (seg_reg == R_SS)
+ s->is_jmp = 3;
+ }
+}
+
+static inline void gen_stack_update(DisasContext *s, int addend)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ if (addend == 8)
+ gen_op_addq_ESP_8();
+ else
+ gen_op_addq_ESP_im(addend);
+ } else
+#endif
+ if (s->ss32) {
+ if (addend == 2)
+ gen_op_addl_ESP_2();
+ else if (addend == 4)
+ gen_op_addl_ESP_4();
+ else
+ gen_op_addl_ESP_im(addend);
+ } else {
+ if (addend == 2)
+ gen_op_addw_ESP_2();
+ else if (addend == 4)
+ gen_op_addw_ESP_4();
+ else
+ gen_op_addw_ESP_im(addend);
+ }
+}
+
+/* generate a push. It depends on ss32, addseg and dflag */
+static void gen_push_T0(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg[R_ESP]();
+ if (s->dflag) {
+ gen_op_subq_A0_8();
+ gen_op_st_T0_A0[OT_QUAD + s->mem_index]();
+ } else {
+ gen_op_subq_A0_2();
+ gen_op_st_T0_A0[OT_WORD + s->mem_index]();
+ }
+ gen_op_movq_ESP_A0();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_ESP]();
+ if (!s->dflag)
+ gen_op_subl_A0_2();
+ else
+ gen_op_subl_A0_4();
+ if (s->ss32) {
+ if (s->addseg) {
+ gen_op_movl_T1_A0();
+ gen_op_addl_A0_SS();
+ }
+ } else {
+ gen_op_andl_A0_ffff();
+ gen_op_movl_T1_A0();
+ gen_op_addl_A0_SS();
+ }
+ gen_op_st_T0_A0[s->dflag + 1 + s->mem_index]();
+ if (s->ss32 && !s->addseg)
+ gen_op_movl_ESP_A0();
+ else
+ gen_op_mov_reg_T1[s->ss32 + 1][R_ESP]();
+ }
+}
+
+/* generate a push. It depends on ss32, addseg and dflag */
+/* slower version for T1, only used for call Ev */
+static void gen_push_T1(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg[R_ESP]();
+ if (s->dflag) {
+ gen_op_subq_A0_8();
+ gen_op_st_T1_A0[OT_QUAD + s->mem_index]();
+ } else {
+ gen_op_subq_A0_2();
+ gen_op_st_T0_A0[OT_WORD + s->mem_index]();
+ }
+ gen_op_movq_ESP_A0();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_ESP]();
+ if (!s->dflag)
+ gen_op_subl_A0_2();
+ else
+ gen_op_subl_A0_4();
+ if (s->ss32) {
+ if (s->addseg) {
+ gen_op_addl_A0_SS();
+ }
+ } else {
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_SS();
+ }
+ gen_op_st_T1_A0[s->dflag + 1 + s->mem_index]();
+
+ if (s->ss32 && !s->addseg)
+ gen_op_movl_ESP_A0();
+ else
+ gen_stack_update(s, (-2) << s->dflag);
+ }
+}
+
+/* two step pop is necessary for precise exceptions */
+static void gen_pop_T0(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg[R_ESP]();
+ gen_op_ld_T0_A0[(s->dflag ? OT_QUAD : OT_WORD) + s->mem_index]();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_ESP]();
+ if (s->ss32) {
+ if (s->addseg)
+ gen_op_addl_A0_SS();
+ } else {
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_SS();
+ }
+ gen_op_ld_T0_A0[s->dflag + 1 + s->mem_index]();
+ }
+}
+
+static void gen_pop_update(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s) && s->dflag) {
+ gen_stack_update(s, 8);
+ } else
+#endif
+ {
+ gen_stack_update(s, 2 << s->dflag);
+ }
+}
+
+static void gen_stack_A0(DisasContext *s)
+{
+ gen_op_movl_A0_ESP();
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ gen_op_movl_T1_A0();
+ if (s->addseg)
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
+}
+
+/* NOTE: wrap around in 16 bit not fully handled */
+static void gen_pusha(DisasContext *s)
+{
+ int i;
+ gen_op_movl_A0_ESP();
+ gen_op_addl_A0_im(-16 << s->dflag);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ gen_op_movl_T1_A0();
+ if (s->addseg)
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
+ for(i = 0;i < 8; i++) {
+ gen_op_mov_TN_reg[OT_LONG][0][7 - i]();
+ gen_op_st_T0_A0[OT_WORD + s->dflag + s->mem_index]();
+ gen_op_addl_A0_im(2 << s->dflag);
+ }
+ gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP]();
+}
+
+/* NOTE: wrap around in 16 bit not fully handled */
+static void gen_popa(DisasContext *s)
+{
+ int i;
+ gen_op_movl_A0_ESP();
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ gen_op_movl_T1_A0();
+ gen_op_addl_T1_im(16 << s->dflag);
+ if (s->addseg)
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
+ for(i = 0;i < 8; i++) {
+ /* ESP is not reloaded */
+ if (i != 3) {
+ gen_op_ld_T0_A0[OT_WORD + s->dflag + s->mem_index]();
+ gen_op_mov_reg_T0[OT_WORD + s->dflag][7 - i]();
+ }
+ gen_op_addl_A0_im(2 << s->dflag);
+ }
+ gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP]();
+}
+
+static void gen_enter(DisasContext *s, int esp_addend, int level)
+{
+ int ot, opsize;
+
+ level &= 0x1f;
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ ot = s->dflag ? OT_QUAD : OT_WORD;
+ opsize = 1 << ot;
+
+ gen_op_movl_A0_ESP();
+ gen_op_addq_A0_im(-opsize);
+ gen_op_movl_T1_A0();
+
+ /* push bp */
+ gen_op_mov_TN_reg[OT_LONG][0][R_EBP]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ if (level) {
+ gen_op_enter64_level(level, (ot == OT_QUAD));
+ }
+ gen_op_mov_reg_T1[ot][R_EBP]();
+ gen_op_addl_T1_im( -esp_addend + (-opsize * level) );
+ gen_op_mov_reg_T1[OT_QUAD][R_ESP]();
+ } else
+#endif
+ {
+ ot = s->dflag + OT_WORD;
+ opsize = 2 << s->dflag;
+
+ gen_op_movl_A0_ESP();
+ gen_op_addl_A0_im(-opsize);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ gen_op_movl_T1_A0();
+ if (s->addseg)
+ gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
+ /* push bp */
+ gen_op_mov_TN_reg[OT_LONG][0][R_EBP]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ if (level) {
+ gen_op_enter_level(level, s->dflag);
+ }
+ gen_op_mov_reg_T1[ot][R_EBP]();
+ gen_op_addl_T1_im( -esp_addend + (-opsize * level) );
+ gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP]();
+ }
+}
+
+static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_op_raise_exception(trapno);
+ s->is_jmp = 3;
+}
+
+/* an interrupt is different from an exception because of the
+ priviledge checks */
+static void gen_interrupt(DisasContext *s, int intno,
+ target_ulong cur_eip, target_ulong next_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_op_raise_interrupt(intno, (int)(next_eip - cur_eip));
+ s->is_jmp = 3;
+}
+
+static void gen_debug(DisasContext *s, target_ulong cur_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_op_debug();
+ s->is_jmp = 3;
+}
+
+/* generate a generic end of block. Trace exception is also generated
+ if needed */
+static void gen_eob(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (s->tb->flags & HF_INHIBIT_IRQ_MASK) {
+ gen_op_reset_inhibit_irq();
+ }
+ if (s->singlestep_enabled) {
+ gen_op_debug();
+ } else if (s->tf) {
+ gen_op_raise_exception(EXCP01_SSTP);
+ } else {
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ }
+ s->is_jmp = 3;
+}
+
+/* generate a jump to eip. No segment change must happen before as a
+ direct call to the next block may occur */
+static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num)
+{
+ if (s->jmp_opt) {
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_goto_tb(s, tb_num, eip);
+ s->is_jmp = 3;
+ } else {
+ gen_jmp_im(eip);
+ gen_eob(s);
+ }
+}
+
+static void gen_jmp(DisasContext *s, target_ulong eip)
+{
+ gen_jmp_tb(s, eip, 0);
+}
+
+static void gen_movtl_T0_im(target_ulong val)
+{
+#ifdef TARGET_X86_64
+ if ((int32_t)val == val) {
+ gen_op_movl_T0_im(val);
+ } else {
+ gen_op_movq_T0_im64(val >> 32, val);
+ }
+#else
+ gen_op_movl_T0_im(val);
+#endif
+}
+
+static void gen_movtl_T1_im(target_ulong val)
+{
+#ifdef TARGET_X86_64
+ if ((int32_t)val == val) {
+ gen_op_movl_T1_im(val);
+ } else {
+ gen_op_movq_T1_im64(val >> 32, val);
+ }
+#else
+ gen_op_movl_T1_im(val);
+#endif
+}
+
+static void gen_add_A0_im(DisasContext *s, int val)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s))
+ gen_op_addq_A0_im(val);
+ else
+#endif
+ gen_op_addl_A0_im(val);
+}
+
+static GenOpFunc1 *gen_ldq_env_A0[3] = {
+ gen_op_ldq_raw_env_A0,
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldq_kernel_env_A0,
+ gen_op_ldq_user_env_A0,
+#endif
+};
+
+static GenOpFunc1 *gen_stq_env_A0[3] = {
+ gen_op_stq_raw_env_A0,
+#ifndef CONFIG_USER_ONLY
+ gen_op_stq_kernel_env_A0,
+ gen_op_stq_user_env_A0,
+#endif
+};
+
+static GenOpFunc1 *gen_ldo_env_A0[3] = {
+ gen_op_ldo_raw_env_A0,
+#ifndef CONFIG_USER_ONLY
+ gen_op_ldo_kernel_env_A0,
+ gen_op_ldo_user_env_A0,
+#endif
+};
+
+static GenOpFunc1 *gen_sto_env_A0[3] = {
+ gen_op_sto_raw_env_A0,
+#ifndef CONFIG_USER_ONLY
+ gen_op_sto_kernel_env_A0,
+ gen_op_sto_user_env_A0,
+#endif
+};
+
+#define SSE_SPECIAL ((GenOpFunc2 *)1)
+
+#define MMX_OP2(x) { gen_op_ ## x ## _mmx, gen_op_ ## x ## _xmm }
+#define SSE_FOP(x) { gen_op_ ## x ## ps, gen_op_ ## x ## pd, \
+ gen_op_ ## x ## ss, gen_op_ ## x ## sd, }
+
+static GenOpFunc2 *sse_op_table1[256][4] = {
+ /* pure SSE operations */
+ [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */
+ [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */
+ [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */
+ [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */
+ [0x14] = { gen_op_punpckldq_xmm, gen_op_punpcklqdq_xmm },
+ [0x15] = { gen_op_punpckhdq_xmm, gen_op_punpckhqdq_xmm },
+ [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */
+ [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */
+
+ [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */
+ [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */
+ [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */
+ [0x2b] = { SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd */
+ [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */
+ [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */
+ [0x2e] = { gen_op_ucomiss, gen_op_ucomisd },
+ [0x2f] = { gen_op_comiss, gen_op_comisd },
+ [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */
+ [0x51] = SSE_FOP(sqrt),
+ [0x52] = { gen_op_rsqrtps, NULL, gen_op_rsqrtss, NULL },
+ [0x53] = { gen_op_rcpps, NULL, gen_op_rcpss, NULL },
+ [0x54] = { gen_op_pand_xmm, gen_op_pand_xmm }, /* andps, andpd */
+ [0x55] = { gen_op_pandn_xmm, gen_op_pandn_xmm }, /* andnps, andnpd */
+ [0x56] = { gen_op_por_xmm, gen_op_por_xmm }, /* orps, orpd */
+ [0x57] = { gen_op_pxor_xmm, gen_op_pxor_xmm }, /* xorps, xorpd */
+ [0x58] = SSE_FOP(add),
+ [0x59] = SSE_FOP(mul),
+ [0x5a] = { gen_op_cvtps2pd, gen_op_cvtpd2ps,
+ gen_op_cvtss2sd, gen_op_cvtsd2ss },
+ [0x5b] = { gen_op_cvtdq2ps, gen_op_cvtps2dq, gen_op_cvttps2dq },
+ [0x5c] = SSE_FOP(sub),
+ [0x5d] = SSE_FOP(min),
+ [0x5e] = SSE_FOP(div),
+ [0x5f] = SSE_FOP(max),
+
+ [0xc2] = SSE_FOP(cmpeq),
+ [0xc6] = { (GenOpFunc2 *)gen_op_shufps, (GenOpFunc2 *)gen_op_shufpd },
+
+ /* MMX ops and their SSE extensions */
+ [0x60] = MMX_OP2(punpcklbw),
+ [0x61] = MMX_OP2(punpcklwd),
+ [0x62] = MMX_OP2(punpckldq),
+ [0x63] = MMX_OP2(packsswb),
+ [0x64] = MMX_OP2(pcmpgtb),
+ [0x65] = MMX_OP2(pcmpgtw),
+ [0x66] = MMX_OP2(pcmpgtl),
+ [0x67] = MMX_OP2(packuswb),
+ [0x68] = MMX_OP2(punpckhbw),
+ [0x69] = MMX_OP2(punpckhwd),
+ [0x6a] = MMX_OP2(punpckhdq),
+ [0x6b] = MMX_OP2(packssdw),
+ [0x6c] = { NULL, gen_op_punpcklqdq_xmm },
+ [0x6d] = { NULL, gen_op_punpckhqdq_xmm },
+ [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */
+ [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */
+ [0x70] = { (GenOpFunc2 *)gen_op_pshufw_mmx,
+ (GenOpFunc2 *)gen_op_pshufd_xmm,
+ (GenOpFunc2 *)gen_op_pshufhw_xmm,
+ (GenOpFunc2 *)gen_op_pshuflw_xmm },
+ [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */
+ [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */
+ [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */
+ [0x74] = MMX_OP2(pcmpeqb),
+ [0x75] = MMX_OP2(pcmpeqw),
+ [0x76] = MMX_OP2(pcmpeql),
+ [0x77] = { SSE_SPECIAL }, /* emms */
+ [0x7c] = { NULL, gen_op_haddpd, NULL, gen_op_haddps },
+ [0x7d] = { NULL, gen_op_hsubpd, NULL, gen_op_hsubps },
+ [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */
+ [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */
+ [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */
+ [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */
+ [0xd0] = { NULL, gen_op_addsubpd, NULL, gen_op_addsubps },
+ [0xd1] = MMX_OP2(psrlw),
+ [0xd2] = MMX_OP2(psrld),
+ [0xd3] = MMX_OP2(psrlq),
+ [0xd4] = MMX_OP2(paddq),
+ [0xd5] = MMX_OP2(pmullw),
+ [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL },
+ [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */
+ [0xd8] = MMX_OP2(psubusb),
+ [0xd9] = MMX_OP2(psubusw),
+ [0xda] = MMX_OP2(pminub),
+ [0xdb] = MMX_OP2(pand),
+ [0xdc] = MMX_OP2(paddusb),
+ [0xdd] = MMX_OP2(paddusw),
+ [0xde] = MMX_OP2(pmaxub),
+ [0xdf] = MMX_OP2(pandn),
+ [0xe0] = MMX_OP2(pavgb),
+ [0xe1] = MMX_OP2(psraw),
+ [0xe2] = MMX_OP2(psrad),
+ [0xe3] = MMX_OP2(pavgw),
+ [0xe4] = MMX_OP2(pmulhuw),
+ [0xe5] = MMX_OP2(pmulhw),
+ [0xe6] = { NULL, gen_op_cvttpd2dq, gen_op_cvtdq2pd, gen_op_cvtpd2dq },
+ [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */
+ [0xe8] = MMX_OP2(psubsb),
+ [0xe9] = MMX_OP2(psubsw),
+ [0xea] = MMX_OP2(pminsw),
+ [0xeb] = MMX_OP2(por),
+ [0xec] = MMX_OP2(paddsb),
+ [0xed] = MMX_OP2(paddsw),
+ [0xee] = MMX_OP2(pmaxsw),
+ [0xef] = MMX_OP2(pxor),
+ [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */
+ [0xf1] = MMX_OP2(psllw),
+ [0xf2] = MMX_OP2(pslld),
+ [0xf3] = MMX_OP2(psllq),
+ [0xf4] = MMX_OP2(pmuludq),
+ [0xf5] = MMX_OP2(pmaddwd),
+ [0xf6] = MMX_OP2(psadbw),
+ [0xf7] = MMX_OP2(maskmov),
+ [0xf8] = MMX_OP2(psubb),
+ [0xf9] = MMX_OP2(psubw),
+ [0xfa] = MMX_OP2(psubl),
+ [0xfb] = MMX_OP2(psubq),
+ [0xfc] = MMX_OP2(paddb),
+ [0xfd] = MMX_OP2(paddw),
+ [0xfe] = MMX_OP2(paddl),
+};
+
+static GenOpFunc2 *sse_op_table2[3 * 8][2] = {
+ [0 + 2] = MMX_OP2(psrlw),
+ [0 + 4] = MMX_OP2(psraw),
+ [0 + 6] = MMX_OP2(psllw),
+ [8 + 2] = MMX_OP2(psrld),
+ [8 + 4] = MMX_OP2(psrad),
+ [8 + 6] = MMX_OP2(pslld),
+ [16 + 2] = MMX_OP2(psrlq),
+ [16 + 3] = { NULL, gen_op_psrldq_xmm },
+ [16 + 6] = MMX_OP2(psllq),
+ [16 + 7] = { NULL, gen_op_pslldq_xmm },
+};
+
+static GenOpFunc1 *sse_op_table3[4 * 3] = {
+ gen_op_cvtsi2ss,
+ gen_op_cvtsi2sd,
+ X86_64_ONLY(gen_op_cvtsq2ss),
+ X86_64_ONLY(gen_op_cvtsq2sd),
+
+ gen_op_cvttss2si,
+ gen_op_cvttsd2si,
+ X86_64_ONLY(gen_op_cvttss2sq),
+ X86_64_ONLY(gen_op_cvttsd2sq),
+
+ gen_op_cvtss2si,
+ gen_op_cvtsd2si,
+ X86_64_ONLY(gen_op_cvtss2sq),
+ X86_64_ONLY(gen_op_cvtsd2sq),
+};
+
+static GenOpFunc2 *sse_op_table4[8][4] = {
+ SSE_FOP(cmpeq),
+ SSE_FOP(cmplt),
+ SSE_FOP(cmple),
+ SSE_FOP(cmpunord),
+ SSE_FOP(cmpneq),
+ SSE_FOP(cmpnlt),
+ SSE_FOP(cmpnle),
+ SSE_FOP(cmpord),
+};
+
+static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r)
+{
+ int b1, op1_offset, op2_offset, is_xmm, val, ot;
+ int modrm, mod, rm, reg, reg_addr, offset_addr;
+ GenOpFunc2 *sse_op2;
+ GenOpFunc3 *sse_op3;
+
+ b &= 0xff;
+ if (s->prefix & PREFIX_DATA)
+ b1 = 1;
+ else if (s->prefix & PREFIX_REPZ)
+ b1 = 2;
+ else if (s->prefix & PREFIX_REPNZ)
+ b1 = 3;
+ else
+ b1 = 0;
+ sse_op2 = sse_op_table1[b][b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if (b <= 0x5f || b == 0xc6 || b == 0xc2) {
+ is_xmm = 1;
+ } else {
+ if (b1 == 0) {
+ /* MMX case */
+ is_xmm = 0;
+ } else {
+ is_xmm = 1;
+ }
+ }
+ /* simple MMX/SSE operation */
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ return;
+ }
+ if (s->flags & HF_EM_MASK) {
+ illegal_op:
+ gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base);
+ return;
+ }
+ if (is_xmm && !(s->flags & HF_OSFXSR_MASK))
+ goto illegal_op;
+ if (b == 0x77) {
+ /* emms */
+ gen_op_emms();
+ return;
+ }
+ /* prepare MMX state (XXX: optimize by storing fptt and fptags in
+ the static cpu state) */
+ if (!is_xmm) {
+ gen_op_enter_mmx();
+ }
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7);
+ if (is_xmm)
+ reg |= rex_r;
+ mod = (modrm >> 6) & 3;
+ if (sse_op2 == SSE_SPECIAL) {
+ b |= (b1 << 8);
+ switch(b) {
+ case 0x0e7: /* movntq */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx));
+ break;
+ case 0x1e7: /* movntdq */
+ case 0x02b: /* movntps */
+ case 0x12b: /* movntps */
+ case 0x3f0: /* lddqu */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_sto_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg]));
+ break;
+ case 0x6e: /* movd mm, ea */
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0);
+ gen_op_movl_mm_T0_mmx(offsetof(CPUX86State,fpregs[reg].mmx));
+ break;
+ case 0x16e: /* movd xmm, ea */
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0);
+ gen_op_movl_mm_T0_xmm(offsetof(CPUX86State,xmm_regs[reg]));
+ break;
+ case 0x6f: /* movq mm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx));
+ } else {
+ rm = (modrm & 7);
+ gen_op_movq(offsetof(CPUX86State,fpregs[reg].mmx),
+ offsetof(CPUX86State,fpregs[rm].mmx));
+ }
+ break;
+ case 0x010: /* movups */
+ case 0x110: /* movupd */
+ case 0x028: /* movaps */
+ case 0x128: /* movapd */
+ case 0x16f: /* movdqa xmm, ea */
+ case 0x26f: /* movdqu xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movo(offsetof(CPUX86State,xmm_regs[reg]),
+ offsetof(CPUX86State,xmm_regs[rm]));
+ }
+ break;
+ case 0x210: /* movss xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[OT_LONG + s->mem_index]();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_movl_T0_0();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)));
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)));
+ }
+ break;
+ case 0x310: /* movsd xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_op_movl_T0_0();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ break;
+ case 0x012: /* movlps */
+ case 0x112: /* movlpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ /* movhlps */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1)));
+ }
+ break;
+ case 0x212: /* movsldup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(2)));
+ }
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ break;
+ case 0x312: /* movddup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ break;
+ case 0x016: /* movhps */
+ case 0x116: /* movhpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ } else {
+ /* movlhps */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ break;
+ case 0x216: /* movshdup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(1)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(3)));
+ }
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ break;
+ case 0x7e: /* movd ea, mm */
+ gen_op_movl_T0_mm_mmx(offsetof(CPUX86State,fpregs[reg].mmx));
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1);
+ break;
+ case 0x17e: /* movd ea, xmm */
+ gen_op_movl_T0_mm_xmm(offsetof(CPUX86State,xmm_regs[reg]));
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1);
+ break;
+ case 0x27e: /* movq xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ break;
+ case 0x7f: /* movq ea, mm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx));
+ } else {
+ rm = (modrm & 7);
+ gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx),
+ offsetof(CPUX86State,fpregs[reg].mmx));
+ }
+ break;
+ case 0x011: /* movups */
+ case 0x111: /* movupd */
+ case 0x029: /* movaps */
+ case 0x129: /* movapd */
+ case 0x17f: /* movdqa ea, xmm */
+ case 0x27f: /* movdqu ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_sto_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]),
+ offsetof(CPUX86State,xmm_regs[reg]));
+ }
+ break;
+ case 0x211: /* movss ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_movl_T0_env(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_st_T0_A0[OT_LONG + s->mem_index]();
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ }
+ break;
+ case 0x311: /* movsd ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ }
+ break;
+ case 0x013: /* movlps */
+ case 0x113: /* movlpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x017: /* movhps */
+ case 0x117: /* movhpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x71: /* shift mm, im */
+ case 0x72:
+ case 0x73:
+ case 0x171: /* shift xmm, im */
+ case 0x172:
+ case 0x173:
+ val = ldub_code(s->pc++);
+ if (is_xmm) {
+ gen_op_movl_T0_im(val);
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ gen_op_movl_T0_0();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(1)));
+ op1_offset = offsetof(CPUX86State,xmm_t0);
+ } else {
+ gen_op_movl_T0_im(val);
+ gen_op_movl_env_T0(offsetof(CPUX86State,mmx_t0.MMX_L(0)));
+ gen_op_movl_T0_0();
+ gen_op_movl_env_T0(offsetof(CPUX86State,mmx_t0.MMX_L(1)));
+ op1_offset = offsetof(CPUX86State,mmx_t0);
+ }
+ sse_op2 = sse_op_table2[((b - 1) & 3) * 8 + (((modrm >> 3)) & 7)][b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if (is_xmm) {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ sse_op2(op2_offset, op1_offset);
+ break;
+ case 0x050: /* movmskps */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movmskps(offsetof(CPUX86State,xmm_regs[rm]));
+ gen_op_mov_reg_T0[OT_LONG][reg]();
+ break;
+ case 0x150: /* movmskpd */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movmskpd(offsetof(CPUX86State,xmm_regs[rm]));
+ gen_op_mov_reg_T0[OT_LONG][reg]();
+ break;
+ case 0x02a: /* cvtpi2ps */
+ case 0x12a: /* cvtpi2pd */
+ gen_op_enter_mmx();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_ldq_env_A0[s->mem_index >> 2](op2_offset);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ switch(b >> 8) {
+ case 0x0:
+ gen_op_cvtpi2ps(op1_offset, op2_offset);
+ break;
+ default:
+ case 0x1:
+ gen_op_cvtpi2pd(op1_offset, op2_offset);
+ break;
+ }
+ break;
+ case 0x22a: /* cvtsi2ss */
+ case 0x32a: /* cvtsi2sd */
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2)](op1_offset);
+ break;
+ case 0x02c: /* cvttps2pi */
+ case 0x12c: /* cvttpd2pi */
+ case 0x02d: /* cvtps2pi */
+ case 0x12d: /* cvtpd2pi */
+ gen_op_enter_mmx();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ gen_ldo_env_A0[s->mem_index >> 2](op2_offset);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx);
+ switch(b) {
+ case 0x02c:
+ gen_op_cvttps2pi(op1_offset, op2_offset);
+ break;
+ case 0x12c:
+ gen_op_cvttpd2pi(op1_offset, op2_offset);
+ break;
+ case 0x02d:
+ gen_op_cvtps2pi(op1_offset, op2_offset);
+ break;
+ case 0x12d:
+ gen_op_cvtpd2pi(op1_offset, op2_offset);
+ break;
+ }
+ break;
+ case 0x22c: /* cvttss2si */
+ case 0x32c: /* cvttsd2si */
+ case 0x22d: /* cvtss2si */
+ case 0x32d: /* cvtsd2si */
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if ((b >> 8) & 1) {
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_t0.XMM_Q(0)));
+ } else {
+ gen_op_ld_T0_A0[OT_LONG + s->mem_index]();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ }
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2) + 4 +
+ (b & 1) * 4](op2_offset);
+ gen_op_mov_reg_T0[ot][reg]();
+ break;
+ case 0xc4: /* pinsrw */
+ case 0x1c4:
+ s->rip_offset = 1;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ val = ldub_code(s->pc++);
+ if (b1) {
+ val &= 7;
+ gen_op_pinsrw_xmm(offsetof(CPUX86State,xmm_regs[reg]), val);
+ } else {
+ val &= 3;
+ gen_op_pinsrw_mmx(offsetof(CPUX86State,fpregs[reg].mmx), val);
+ }
+ break;
+ case 0xc5: /* pextrw */
+ case 0x1c5:
+ if (mod != 3)
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ if (b1) {
+ val &= 7;
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_pextrw_xmm(offsetof(CPUX86State,xmm_regs[rm]), val);
+ } else {
+ val &= 3;
+ rm = (modrm & 7);
+ gen_op_pextrw_mmx(offsetof(CPUX86State,fpregs[rm].mmx), val);
+ }
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_op_mov_reg_T0[OT_LONG][reg]();
+ break;
+ case 0x1d6: /* movq ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1)));
+ }
+ break;
+ case 0x2d6: /* movq2dq */
+ gen_op_enter_mmx();
+ rm = (modrm & 7);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,fpregs[rm].mmx));
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ break;
+ case 0x3d6: /* movdq2q */
+ gen_op_enter_mmx();
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,fpregs[reg & 7].mmx),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ break;
+ case 0xd7: /* pmovmskb */
+ case 0x1d7:
+ if (mod != 3)
+ goto illegal_op;
+ if (b1) {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_pmovmskb_xmm(offsetof(CPUX86State,xmm_regs[rm]));
+ } else {
+ rm = (modrm & 7);
+ gen_op_pmovmskb_mmx(offsetof(CPUX86State,fpregs[rm].mmx));
+ }
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_op_mov_reg_T0[OT_LONG][reg]();
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ /* generic MMX or SSE operation */
+ switch(b) {
+ case 0xf7:
+ /* maskmov : we must prepare A0 */
+ if (mod != 3)
+ goto illegal_op;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg[R_EDI]();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_EDI]();
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ }
+ gen_add_A0_ds_seg(s);
+ break;
+ case 0x70: /* pshufx insn */
+ case 0xc6: /* pshufx insn */
+ case 0xc2: /* compare insns */
+ s->rip_offset = 1;
+ break;
+ default:
+ break;
+ }
+ if (is_xmm) {
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) ||
+ b == 0xc2)) {
+ /* specific case for SSE single instructions */
+ if (b1 == 2) {
+ /* 32 bit access */
+ gen_op_ld_T0_A0[OT_LONG + s->mem_index]();
+ gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ } else {
+ /* 64 bit access */
+ gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_t0.XMM_D(0)));
+ }
+ } else {
+ gen_ldo_env_A0[s->mem_index >> 2](op2_offset);
+ }
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ } else {
+ op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_ldq_env_A0[s->mem_index >> 2](op2_offset);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ }
+ switch(b) {
+ case 0x70: /* pshufx insn */
+ case 0xc6: /* pshufx insn */
+ val = ldub_code(s->pc++);
+ sse_op3 = (GenOpFunc3 *)sse_op2;
+ sse_op3(op1_offset, op2_offset, val);
+ break;
+ case 0xc2:
+ /* compare insns */
+ val = ldub_code(s->pc++);
+ if (val >= 8)
+ goto illegal_op;
+ sse_op2 = sse_op_table4[val][b1];
+ sse_op2(op1_offset, op2_offset);
+ break;
+ default:
+ sse_op2(op1_offset, op2_offset);
+ break;
+ }
+ if (b == 0x2e || b == 0x2f) {
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ }
+}
+
+
+/* convert one instruction. s->is_jmp is set if the translation must
+ be stopped. Return the next pc value */
+static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
+{
+ int b, prefixes, aflag, dflag;
+ int shift, ot;
+ int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val;
+ target_ulong next_eip, tval;
+ int rex_w, rex_r;
+
+ s->pc = pc_start;
+ prefixes = 0;
+ aflag = s->code32;
+ dflag = s->code32;
+ s->override = -1;
+ rex_w = -1;
+ rex_r = 0;
+#ifdef TARGET_X86_64
+ s->rex_x = 0;
+ s->rex_b = 0;
+ x86_64_hregs = 0;
+#endif
+ s->rip_offset = 0; /* for relative ip address */
+ next_byte:
+ b = ldub_code(s->pc);
+ s->pc++;
+ /* check prefixes */
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ switch (b) {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ goto next_byte;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ goto next_byte;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ goto next_byte;
+ case 0x2e:
+ s->override = R_CS;
+ goto next_byte;
+ case 0x36:
+ s->override = R_SS;
+ goto next_byte;
+ case 0x3e:
+ s->override = R_DS;
+ goto next_byte;
+ case 0x26:
+ s->override = R_ES;
+ goto next_byte;
+ case 0x64:
+ s->override = R_FS;
+ goto next_byte;
+ case 0x65:
+ s->override = R_GS;
+ goto next_byte;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ goto next_byte;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ goto next_byte;
+ case 0x40 ... 0x4f:
+ /* REX prefix */
+ rex_w = (b >> 3) & 1;
+ rex_r = (b & 0x4) << 1;
+ s->rex_x = (b & 0x2) << 2;
+ REX_B(s) = (b & 0x1) << 3;
+ x86_64_hregs = 1; /* select uniform byte register addressing */
+ goto next_byte;
+ }
+ if (rex_w == 1) {
+ /* 0x66 is ignored if rex.w is set */
+ dflag = 2;
+ } else {
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+ }
+ if (!(prefixes & PREFIX_ADR))
+ aflag = 2;
+ } else
+#endif
+ {
+ switch (b) {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ goto next_byte;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ goto next_byte;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ goto next_byte;
+ case 0x2e:
+ s->override = R_CS;
+ goto next_byte;
+ case 0x36:
+ s->override = R_SS;
+ goto next_byte;
+ case 0x3e:
+ s->override = R_DS;
+ goto next_byte;
+ case 0x26:
+ s->override = R_ES;
+ goto next_byte;
+ case 0x64:
+ s->override = R_FS;
+ goto next_byte;
+ case 0x65:
+ s->override = R_GS;
+ goto next_byte;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ goto next_byte;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ goto next_byte;
+ }
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+ if (prefixes & PREFIX_ADR)
+ aflag ^= 1;
+ }
+
+ s->prefix = prefixes;
+ s->aflag = aflag;
+ s->dflag = dflag;
+
+ /* lock generation */
+ if (prefixes & PREFIX_LOCK)
+ gen_op_lock();
+
+ /* now check op code */
+ reswitch:
+ switch(b) {
+ case 0x0f:
+ /**************************/
+ /* extended op code */
+ b = ldub_code(s->pc++) | 0x100;
+ goto reswitch;
+
+ /**************************/
+ /* arith & logic */
+ case 0x00 ... 0x05:
+ case 0x08 ... 0x0d:
+ case 0x10 ... 0x15:
+ case 0x18 ... 0x1d:
+ case 0x20 ... 0x25:
+ case 0x28 ... 0x2d:
+ case 0x30 ... 0x35:
+ case 0x38 ... 0x3d:
+ {
+ int op, f, val;
+ op = (b >> 3) & 7;
+ f = (b >> 1) & 3;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ switch(f) {
+ case 0: /* OP Ev, Gv */
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else if (op == OP_XORL && rm == reg) {
+ xor_zero:
+ /* xor reg, reg optimisation */
+ gen_op_movl_T0_0();
+ s->cc_op = CC_OP_LOGICB + ot;
+ gen_op_mov_reg_T0[ot][reg]();
+ gen_op_update1_cc();
+ break;
+ } else {
+ opreg = rm;
+ }
+ gen_op_mov_TN_reg[ot][1][reg]();
+ gen_op(s, op, ot, opreg);
+ break;
+ case 1: /* OP Gv, Ev */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ } else if (op == OP_XORL && rm == reg) {
+ goto xor_zero;
+ } else {
+ gen_op_mov_TN_reg[ot][1][rm]();
+ }
+ gen_op(s, op, ot, reg);
+ break;
+ case 2: /* OP A, Iv */
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ gen_op(s, op, ot, OR_EAX);
+ break;
+ }
+ }
+ break;
+
+ case 0x80: /* GRP1 */
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ {
+ int val;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+
+ if (mod != 3) {
+ if (b == 0x83)
+ s->rip_offset = 1;
+ else
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else {
+ opreg = rm;
+ }
+
+ switch(b) {
+ default:
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ val = insn_get(s, ot);
+ break;
+ case 0x83:
+ val = (int8_t)insn_get(s, OT_BYTE);
+ break;
+ }
+ gen_op_movl_T1_im(val);
+ gen_op(s, op, ot, opreg);
+ }
+ break;
+
+ /**************************/
+ /* inc, dec, and other misc arith */
+ case 0x40 ... 0x47: /* inc Gv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_inc(s, ot, OR_EAX + (b & 7), 1);
+ break;
+ case 0x48 ... 0x4f: /* dec Gv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_inc(s, ot, OR_EAX + (b & 7), -1);
+ break;
+ case 0xf6: /* GRP3 */
+ case 0xf7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+ if (mod != 3) {
+ if (op == 0)
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+
+ switch(op) {
+ case 0: /* test */
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+ case 2: /* not */
+ gen_op_notl_T0();
+ if (mod != 3) {
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ break;
+ case 3: /* neg */
+ gen_op_negl_T0();
+ if (mod != 3) {
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ gen_op_update_neg_cc();
+ s->cc_op = CC_OP_SUBB + ot;
+ break;
+ case 4: /* mul */
+ switch(ot) {
+ case OT_BYTE:
+ gen_op_mulb_AL_T0();
+ s->cc_op = CC_OP_MULB;
+ break;
+ case OT_WORD:
+ gen_op_mulw_AX_T0();
+ s->cc_op = CC_OP_MULW;
+ break;
+ default:
+ case OT_LONG:
+ gen_op_mull_EAX_T0();
+ s->cc_op = CC_OP_MULL;
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_op_mulq_EAX_T0();
+ s->cc_op = CC_OP_MULQ;
+ break;
+#endif
+ }
+ break;
+ case 5: /* imul */
+ switch(ot) {
+ case OT_BYTE:
+ gen_op_imulb_AL_T0();
+ s->cc_op = CC_OP_MULB;
+ break;
+ case OT_WORD:
+ gen_op_imulw_AX_T0();
+ s->cc_op = CC_OP_MULW;
+ break;
+ default:
+ case OT_LONG:
+ gen_op_imull_EAX_T0();
+ s->cc_op = CC_OP_MULL;
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_op_imulq_EAX_T0();
+ s->cc_op = CC_OP_MULQ;
+ break;
+#endif
+ }
+ break;
+ case 6: /* div */
+ switch(ot) {
+ case OT_BYTE:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_divb_AL_T0();
+ break;
+ case OT_WORD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_divw_AX_T0();
+ break;
+ default:
+ case OT_LONG:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_divl_EAX_T0();
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_divq_EAX_T0();
+ break;
+#endif
+ }
+ break;
+ case 7: /* idiv */
+ switch(ot) {
+ case OT_BYTE:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_idivb_AL_T0();
+ break;
+ case OT_WORD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_idivw_AX_T0();
+ break;
+ default:
+ case OT_LONG:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_idivl_EAX_T0();
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_idivq_EAX_T0();
+ break;
+#endif
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+
+ case 0xfe: /* GRP4 */
+ case 0xff: /* GRP5 */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+ if (op >= 2 && b == 0xfe) {
+ goto illegal_op;
+ }
+ if (CODE64(s)) {
+ if (op == 2 || op == 4) {
+ /* operand size for jumps is 64 bit */
+ ot = OT_QUAD;
+ } else if (op == 3 || op == 5) {
+ /* for call calls, the operand is 16 or 32 bit, even
+ in long mode */
+ ot = dflag ? OT_LONG : OT_WORD;
+ } else if (op == 6) {
+ /* default push size is 64 bit */
+ ot = dflag ? OT_QUAD : OT_WORD;
+ }
+ }
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (op >= 2 && op != 3 && op != 5)
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+
+ switch(op) {
+ case 0: /* inc Ev */
+ if (mod != 3)
+ opreg = OR_TMP0;
+ else
+ opreg = rm;
+ gen_inc(s, ot, opreg, 1);
+ break;
+ case 1: /* dec Ev */
+ if (mod != 3)
+ opreg = OR_TMP0;
+ else
+ opreg = rm;
+ gen_inc(s, ot, opreg, -1);
+ break;
+ case 2: /* call Ev */
+ /* XXX: optimize if memory (no 'and' is necessary) */
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ next_eip = s->pc - s->cs_base;
+ gen_movtl_T1_im(next_eip);
+ gen_push_T1(s);
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 3: /* lcall Ev */
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ gen_op_ldu_T0_A0[OT_WORD + s->mem_index]();
+ do_lcall:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_lcall_protected_T0_T1(dflag, s->pc - pc_start);
+ } else {
+ gen_op_lcall_real_T0_T1(dflag, s->pc - s->cs_base);
+ }
+ gen_eob(s);
+ break;
+ case 4: /* jmp Ev */
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 5: /* ljmp Ev */
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ gen_op_ldu_T0_A0[OT_WORD + s->mem_index]();
+ do_ljmp:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_ljmp_protected_T0_T1(s->pc - pc_start);
+ } else {
+ gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS]));
+ gen_op_movl_T0_T1();
+ gen_op_jmp_T0();
+ }
+ gen_eob(s);
+ break;
+ case 6: /* push Ev */
+ gen_push_T0(s);
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+
+ case 0x84: /* test Ev, Gv */
+ case 0x85:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_op_mov_TN_reg[ot][1][reg]();
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+
+ case 0xa8: /* test eAX, Iv */
+ case 0xa9:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ val = insn_get(s, ot);
+
+ gen_op_mov_TN_reg[ot][0][OR_EAX]();
+ gen_op_movl_T1_im(val);
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+
+ case 0x98: /* CWDE/CBW */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_movslq_RAX_EAX();
+ } else
+#endif
+ if (dflag == 1)
+ gen_op_movswl_EAX_AX();
+ else
+ gen_op_movsbw_AX_AL();
+ break;
+ case 0x99: /* CDQ/CWD */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_movsqo_RDX_RAX();
+ } else
+#endif
+ if (dflag == 1)
+ gen_op_movslq_EDX_EAX();
+ else
+ gen_op_movswl_DX_AX();
+ break;
+ case 0x1af: /* imul Gv, Ev */
+ case 0x69: /* imul Gv, Ev, I */
+ case 0x6b:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (b == 0x69)
+ s->rip_offset = insn_const_size(ot);
+ else if (b == 0x6b)
+ s->rip_offset = 1;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ if (b == 0x69) {
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ } else if (b == 0x6b) {
+ val = (int8_t)insn_get(s, OT_BYTE);
+ gen_op_movl_T1_im(val);
+ } else {
+ gen_op_mov_TN_reg[ot][1][reg]();
+ }
+
+#ifdef TARGET_X86_64
+ if (ot == OT_QUAD) {
+ gen_op_imulq_T0_T1();
+ } else
+#endif
+ if (ot == OT_LONG) {
+ gen_op_imull_T0_T1();
+ } else {
+ gen_op_imulw_T0_T1();
+ }
+ gen_op_mov_reg_T0[ot][reg]();
+ s->cc_op = CC_OP_MULB + ot;
+ break;
+ case 0x1c0:
+ case 0x1c1: /* xadd Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_op_mov_TN_reg[ot][1][rm]();
+ gen_op_addl_T0_T1();
+ gen_op_mov_reg_T1[ot][reg]();
+ gen_op_mov_reg_T0[ot][rm]();
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_op_addl_T0_T1();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ gen_op_mov_reg_T1[ot][reg]();
+ }
+ gen_op_update2_cc();
+ s->cc_op = CC_OP_ADDB + ot;
+ break;
+ case 0x1b0:
+ case 0x1b1: /* cmpxchg Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ gen_op_mov_TN_reg[ot][1][reg]();
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg[ot][0][rm]();
+ gen_op_cmpxchg_T0_T1_EAX_cc[ot]();
+ gen_op_mov_reg_T0[ot][rm]();
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_op_cmpxchg_mem_T0_T1_EAX_cc[ot + s->mem_index]();
+ }
+ s->cc_op = CC_OP_SUBB + ot;
+ break;
+ case 0x1c7: /* cmpxchg8b */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_cmpxchg8b();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+
+ /**************************/
+ /* push/pop */
+ case 0x50 ... 0x57: /* push */
+ gen_op_mov_TN_reg[OT_LONG][0][(b & 7) | REX_B(s)]();
+ gen_push_T0(s);
+ break;
+ case 0x58 ... 0x5f: /* pop */
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ gen_pop_T0(s);
+ /* NOTE: order is important for pop %sp */
+ gen_pop_update(s);
+ gen_op_mov_reg_T0[ot][(b & 7) | REX_B(s)]();
+ break;
+ case 0x60: /* pusha */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_pusha(s);
+ break;
+ case 0x61: /* popa */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_popa(s);
+ break;
+ case 0x68: /* push Iv */
+ case 0x6a:
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ if (b == 0x68)
+ val = insn_get(s, ot);
+ else
+ val = (int8_t)insn_get(s, OT_BYTE);
+ gen_op_movl_T0_im(val);
+ gen_push_T0(s);
+ break;
+ case 0x8f: /* pop Ev */
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ gen_pop_T0(s);
+ if (mod == 3) {
+ /* NOTE: order is important for pop %sp */
+ gen_pop_update(s);
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_reg_T0[ot][rm]();
+ } else {
+ /* NOTE: order is important too for MMU exceptions */
+ s->popl_esp_hack = 1 << ot;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ s->popl_esp_hack = 0;
+ gen_pop_update(s);
+ }
+ break;
+ case 0xc8: /* enter */
+ {
+ int level;
+ val = lduw_code(s->pc);
+ s->pc += 2;
+ level = ldub_code(s->pc++);
+ gen_enter(s, val, level);
+ }
+ break;
+ case 0xc9: /* leave */
+ /* XXX: exception not precise (ESP is updated before potential exception) */
+ if (CODE64(s)) {
+ gen_op_mov_TN_reg[OT_QUAD][0][R_EBP]();
+ gen_op_mov_reg_T0[OT_QUAD][R_ESP]();
+ } else if (s->ss32) {
+ gen_op_mov_TN_reg[OT_LONG][0][R_EBP]();
+ gen_op_mov_reg_T0[OT_LONG][R_ESP]();
+ } else {
+ gen_op_mov_TN_reg[OT_WORD][0][R_EBP]();
+ gen_op_mov_reg_T0[OT_WORD][R_ESP]();
+ }
+ gen_pop_T0(s);
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ gen_op_mov_reg_T0[ot][R_EBP]();
+ gen_pop_update(s);
+ break;
+ case 0x06: /* push es */
+ case 0x0e: /* push cs */
+ case 0x16: /* push ss */
+ case 0x1e: /* push ds */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_op_movl_T0_seg(b >> 3);
+ gen_push_T0(s);
+ break;
+ case 0x1a0: /* push fs */
+ case 0x1a8: /* push gs */
+ gen_op_movl_T0_seg((b >> 3) & 7);
+ gen_push_T0(s);
+ break;
+ case 0x07: /* pop es */
+ case 0x17: /* pop ss */
+ case 0x1f: /* pop ds */
+ if (CODE64(s))
+ goto illegal_op;
+ reg = b >> 3;
+ gen_pop_T0(s);
+ gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
+ gen_pop_update(s);
+ if (reg == R_SS) {
+ /* if reg == SS, inhibit interrupts/trace. */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_op_set_inhibit_irq();
+ s->tf = 0;
+ }
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x1a1: /* pop fs */
+ case 0x1a9: /* pop gs */
+ gen_pop_T0(s);
+ gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base);
+ gen_pop_update(s);
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+
+ /**************************/
+ /* mov */
+ case 0x88:
+ case 0x89: /* mov Gv, Ev */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ /* generate a generic store */
+ gen_ldst_modrm(s, modrm, ot, reg, 1);
+ break;
+ case 0xc6:
+ case 0xc7: /* mov Ev, Iv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod != 3) {
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ }
+ val = insn_get(s, ot);
+ gen_op_movl_T0_im(val);
+ if (mod != 3)
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ else
+ gen_op_mov_reg_T0[ot][(modrm & 7) | REX_B(s)]();
+ break;
+ case 0x8a:
+ case 0x8b: /* mov Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = OT_WORD + dflag;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_op_mov_reg_T0[ot][reg]();
+ break;
+ case 0x8e: /* mov seg, Gv */
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ if (reg >= 6 || reg == R_CS)
+ goto illegal_op;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
+ if (reg == R_SS) {
+ /* if reg == SS, inhibit interrupts/trace */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_op_set_inhibit_irq();
+ s->tf = 0;
+ }
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x8c: /* mov Gv, seg */
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ if (reg >= 6)
+ goto illegal_op;
+ gen_op_movl_T0_seg(reg);
+ if (mod == 3)
+ ot = OT_WORD + dflag;
+ else
+ ot = OT_WORD;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+
+ case 0x1b6: /* movzbS Gv, Eb */
+ case 0x1b7: /* movzwS Gv, Eb */
+ case 0x1be: /* movsbS Gv, Eb */
+ case 0x1bf: /* movswS Gv, Eb */
+ {
+ int d_ot;
+ /* d_ot is the size of destination */
+ d_ot = dflag + OT_WORD;
+ /* ot is the size of source */
+ ot = (b & 1) + OT_BYTE;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+
+ if (mod == 3) {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ switch(ot | (b & 8)) {
+ case OT_BYTE:
+ gen_op_movzbl_T0_T0();
+ break;
+ case OT_BYTE | 8:
+ gen_op_movsbl_T0_T0();
+ break;
+ case OT_WORD:
+ gen_op_movzwl_T0_T0();
+ break;
+ default:
+ case OT_WORD | 8:
+ gen_op_movswl_T0_T0();
+ break;
+ }
+ gen_op_mov_reg_T0[d_ot][reg]();
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (b & 8) {
+ gen_op_lds_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_ldu_T0_A0[ot + s->mem_index]();
+ }
+ gen_op_mov_reg_T0[d_ot][reg]();
+ }
+ }
+ break;
+
+ case 0x8d: /* lea */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ /* we must ensure that no segment is added */
+ s->override = -1;
+ val = s->addseg;
+ s->addseg = 0;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ s->addseg = val;
+ gen_op_mov_reg_A0[ot - OT_WORD][reg]();
+ break;
+
+ case 0xa0: /* mov EAX, Ov */
+ case 0xa1:
+ case 0xa2: /* mov Ov, EAX */
+ case 0xa3:
+ {
+ target_ulong offset_addr;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ offset_addr = ldq_code(s->pc);
+ s->pc += 8;
+ if (offset_addr == (int32_t)offset_addr)
+ gen_op_movq_A0_im(offset_addr);
+ else
+ gen_op_movq_A0_im64(offset_addr >> 32, offset_addr);
+ } else
+#endif
+ {
+ if (s->aflag) {
+ offset_addr = insn_get(s, OT_LONG);
+ } else {
+ offset_addr = insn_get(s, OT_WORD);
+ }
+ gen_op_movl_A0_im(offset_addr);
+ }
+ gen_add_A0_ds_seg(s);
+ if ((b & 2) == 0) {
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ gen_op_mov_reg_T0[ot][R_EAX]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][R_EAX]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ }
+ }
+ break;
+ case 0xd7: /* xlat */
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg[R_EBX]();
+ gen_op_addq_A0_AL();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_EBX]();
+ gen_op_addl_A0_AL();
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ }
+ gen_add_A0_ds_seg(s);
+ gen_op_ldu_T0_A0[OT_BYTE + s->mem_index]();
+ gen_op_mov_reg_T0[OT_BYTE][R_EAX]();
+ break;
+ case 0xb0 ... 0xb7: /* mov R, Ib */
+ val = insn_get(s, OT_BYTE);
+ gen_op_movl_T0_im(val);
+ gen_op_mov_reg_T0[OT_BYTE][(b & 7) | REX_B(s)]();
+ break;
+ case 0xb8 ... 0xbf: /* mov R, Iv */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ uint64_t tmp;
+ /* 64 bit case */
+ tmp = ldq_code(s->pc);
+ s->pc += 8;
+ reg = (b & 7) | REX_B(s);
+ gen_movtl_T0_im(tmp);
+ gen_op_mov_reg_T0[OT_QUAD][reg]();
+ } else
+#endif
+ {
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = insn_get(s, ot);
+ reg = (b & 7) | REX_B(s);
+ gen_op_movl_T0_im(val);
+ gen_op_mov_reg_T0[ot][reg]();
+ }
+ break;
+
+ case 0x91 ... 0x97: /* xchg R, EAX */
+ ot = dflag + OT_WORD;
+ reg = (b & 7) | REX_B(s);
+ rm = R_EAX;
+ goto do_xchg_reg;
+ case 0x86:
+ case 0x87: /* xchg Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ do_xchg_reg:
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_op_mov_TN_reg[ot][1][rm]();
+ gen_op_mov_reg_T0[ot][rm]();
+ gen_op_mov_reg_T1[ot][reg]();
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_mov_TN_reg[ot][0][reg]();
+ /* for xchg, lock is implicit */
+ if (!(prefixes & PREFIX_LOCK))
+ gen_op_lock();
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ if (!(prefixes & PREFIX_LOCK))
+ gen_op_unlock();
+ gen_op_mov_reg_T1[ot][reg]();
+ }
+ break;
+ case 0xc4: /* les Gv */
+ if (CODE64(s))
+ goto illegal_op;
+ op = R_ES;
+ goto do_lxx;
+ case 0xc5: /* lds Gv */
+ if (CODE64(s))
+ goto illegal_op;
+ op = R_DS;
+ goto do_lxx;
+ case 0x1b2: /* lss Gv */
+ op = R_SS;
+ goto do_lxx;
+ case 0x1b4: /* lfs Gv */
+ op = R_FS;
+ goto do_lxx;
+ case 0x1b5: /* lgs Gv */
+ op = R_GS;
+ do_lxx:
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ /* load the segment first to handle exceptions properly */
+ gen_op_ldu_T0_A0[OT_WORD + s->mem_index]();
+ gen_movl_seg_T0(s, op, pc_start - s->cs_base);
+ /* then put the data */
+ gen_op_mov_reg_T1[ot][reg]();
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+
+ /************************/
+ /* shifts */
+ case 0xc0:
+ case 0xc1:
+ /* shift Ev,Ib */
+ shift = 2;
+ grp2:
+ {
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+
+ if (mod != 3) {
+ if (shift == 2) {
+ s->rip_offset = 1;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else {
+ opreg = (modrm & 7) | REX_B(s);
+ }
+
+ /* simpler op */
+ if (shift == 0) {
+ gen_shift(s, op, ot, opreg, OR_ECX);
+ } else {
+ if (shift == 2) {
+ shift = ldub_code(s->pc++);
+ }
+ gen_shifti(s, op, ot, opreg, shift);
+ }
+ }
+ break;
+ case 0xd0:
+ case 0xd1:
+ /* shift Ev,1 */
+ shift = 1;
+ goto grp2;
+ case 0xd2:
+ case 0xd3:
+ /* shift Ev,cl */
+ shift = 0;
+ goto grp2;
+
+ case 0x1a4: /* shld imm */
+ op = 0;
+ shift = 1;
+ goto do_shiftd;
+ case 0x1a5: /* shld cl */
+ op = 0;
+ shift = 0;
+ goto do_shiftd;
+ case 0x1ac: /* shrd imm */
+ op = 1;
+ shift = 1;
+ goto do_shiftd;
+ case 0x1ad: /* shrd cl */
+ op = 1;
+ shift = 0;
+ do_shiftd:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+ gen_op_mov_TN_reg[ot][1][reg]();
+
+ if (shift) {
+ val = ldub_code(s->pc++);
+ if (ot == OT_QUAD)
+ val &= 0x3f;
+ else
+ val &= 0x1f;
+ if (val) {
+ if (mod == 3)
+ gen_op_shiftd_T0_T1_im_cc[ot][op](val);
+ else
+ gen_op_shiftd_mem_T0_T1_im_cc[ot + s->mem_index][op](val);
+ if (op == 0 && ot != OT_WORD)
+ s->cc_op = CC_OP_SHLB + ot;
+ else
+ s->cc_op = CC_OP_SARB + ot;
+ }
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (mod == 3)
+ gen_op_shiftd_T0_T1_ECX_cc[ot][op]();
+ else
+ gen_op_shiftd_mem_T0_T1_ECX_cc[ot + s->mem_index][op]();
+ s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+ }
+ if (mod == 3) {
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ break;
+
+ /************************/
+ /* floats */
+ case 0xd8 ... 0xdf:
+ if (s->flags & (HF_EM_MASK | HF_TS_MASK)) {
+ /* if CR0.EM or CR0.TS are set, generate an FPU exception */
+ /* XXX: what to do if illegal op ? */
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ op = ((b & 7) << 3) | ((modrm >> 3) & 7);
+ if (mod != 3) {
+ /* memory op */
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ switch(op) {
+ case 0x00 ... 0x07: /* fxxxs */
+ case 0x10 ... 0x17: /* fixxxl */
+ case 0x20 ... 0x27: /* fxxxl */
+ case 0x30 ... 0x37: /* fixxx */
+ {
+ int op1;
+ op1 = op & 7;
+
+ switch(op >> 4) {
+ case 0:
+ gen_op_flds_FT0_A0();
+ break;
+ case 1:
+ gen_op_fildl_FT0_A0();
+ break;
+ case 2:
+ gen_op_fldl_FT0_A0();
+ break;
+ case 3:
+ default:
+ gen_op_fild_FT0_A0();
+ break;
+ }
+
+ gen_op_fp_arith_ST0_FT0[op1]();
+ if (op1 == 3) {
+ /* fcomp needs pop */
+ gen_op_fpop();
+ }
+ }
+ break;
+ case 0x08: /* flds */
+ case 0x0a: /* fsts */
+ case 0x0b: /* fstps */
+ case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */
+ case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */
+ case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */
+ switch(op & 7) {
+ case 0:
+ switch(op >> 4) {
+ case 0:
+ gen_op_flds_ST0_A0();
+ break;
+ case 1:
+ gen_op_fildl_ST0_A0();
+ break;
+ case 2:
+ gen_op_fldl_ST0_A0();
+ break;
+ case 3:
+ default:
+ gen_op_fild_ST0_A0();
+ break;
+ }
+ break;
+ case 1:
+ switch(op >> 4) {
+ case 1:
+ gen_op_fisttl_ST0_A0();
+ break;
+ case 2:
+ gen_op_fisttll_ST0_A0();
+ break;
+ case 3:
+ default:
+ gen_op_fistt_ST0_A0();
+ }
+ gen_op_fpop();
+ break;
+ default:
+ switch(op >> 4) {
+ case 0:
+ gen_op_fsts_ST0_A0();
+ break;
+ case 1:
+ gen_op_fistl_ST0_A0();
+ break;
+ case 2:
+ gen_op_fstl_ST0_A0();
+ break;
+ case 3:
+ default:
+ gen_op_fist_ST0_A0();
+ break;
+ }
+ if ((op & 7) == 3)
+ gen_op_fpop();
+ break;
+ }
+ break;
+ case 0x0c: /* fldenv mem */
+ gen_op_fldenv_A0(s->dflag);
+ break;
+ case 0x0d: /* fldcw mem */
+ gen_op_fldcw_A0();
+ break;
+ case 0x0e: /* fnstenv mem */
+ gen_op_fnstenv_A0(s->dflag);
+ break;
+ case 0x0f: /* fnstcw mem */
+ gen_op_fnstcw_A0();
+ break;
+ case 0x1d: /* fldt mem */
+ gen_op_fldt_ST0_A0();
+ break;
+ case 0x1f: /* fstpt mem */
+ gen_op_fstt_ST0_A0();
+ gen_op_fpop();
+ break;
+ case 0x2c: /* frstor mem */
+ gen_op_frstor_A0(s->dflag);
+ break;
+ case 0x2e: /* fnsave mem */
+ gen_op_fnsave_A0(s->dflag);
+ break;
+ case 0x2f: /* fnstsw mem */
+ gen_op_fnstsw_A0();
+ break;
+ case 0x3c: /* fbld */
+ gen_op_fbld_ST0_A0();
+ break;
+ case 0x3e: /* fbstp */
+ gen_op_fbst_ST0_A0();
+ gen_op_fpop();
+ break;
+ case 0x3d: /* fildll */
+ gen_op_fildll_ST0_A0();
+ break;
+ case 0x3f: /* fistpll */
+ gen_op_fistll_ST0_A0();
+ gen_op_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ /* register float ops */
+ opreg = rm;
+
+ switch(op) {
+ case 0x08: /* fld sti */
+ gen_op_fpush();
+ gen_op_fmov_ST0_STN((opreg + 1) & 7);
+ break;
+ case 0x09: /* fxchg sti */
+ case 0x29: /* fxchg4 sti, undocumented op */
+ case 0x39: /* fxchg7 sti, undocumented op */
+ gen_op_fxchg_ST0_STN(opreg);
+ break;
+ case 0x0a: /* grp d9/2 */
+ switch(rm) {
+ case 0: /* fnop */
+ /* check exceptions (FreeBSD FPU probe) */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_fwait();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0c: /* grp d9/4 */
+ switch(rm) {
+ case 0: /* fchs */
+ gen_op_fchs_ST0();
+ break;
+ case 1: /* fabs */
+ gen_op_fabs_ST0();
+ break;
+ case 4: /* ftst */
+ gen_op_fldz_FT0();
+ gen_op_fcom_ST0_FT0();
+ break;
+ case 5: /* fxam */
+ gen_op_fxam_ST0();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0d: /* grp d9/5 */
+ {
+ switch(rm) {
+ case 0:
+ gen_op_fpush();
+ gen_op_fld1_ST0();
+ break;
+ case 1:
+ gen_op_fpush();
+ gen_op_fldl2t_ST0();
+ break;
+ case 2:
+ gen_op_fpush();
+ gen_op_fldl2e_ST0();
+ break;
+ case 3:
+ gen_op_fpush();
+ gen_op_fldpi_ST0();
+ break;
+ case 4:
+ gen_op_fpush();
+ gen_op_fldlg2_ST0();
+ break;
+ case 5:
+ gen_op_fpush();
+ gen_op_fldln2_ST0();
+ break;
+ case 6:
+ gen_op_fpush();
+ gen_op_fldz_ST0();
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ case 0x0e: /* grp d9/6 */
+ switch(rm) {
+ case 0: /* f2xm1 */
+ gen_op_f2xm1();
+ break;
+ case 1: /* fyl2x */
+ gen_op_fyl2x();
+ break;
+ case 2: /* fptan */
+ gen_op_fptan();
+ break;
+ case 3: /* fpatan */
+ gen_op_fpatan();
+ break;
+ case 4: /* fxtract */
+ gen_op_fxtract();
+ break;
+ case 5: /* fprem1 */
+ gen_op_fprem1();
+ break;
+ case 6: /* fdecstp */
+ gen_op_fdecstp();
+ break;
+ default:
+ case 7: /* fincstp */
+ gen_op_fincstp();
+ break;
+ }
+ break;
+ case 0x0f: /* grp d9/7 */
+ switch(rm) {
+ case 0: /* fprem */
+ gen_op_fprem();
+ break;
+ case 1: /* fyl2xp1 */
+ gen_op_fyl2xp1();
+ break;
+ case 2: /* fsqrt */
+ gen_op_fsqrt();
+ break;
+ case 3: /* fsincos */
+ gen_op_fsincos();
+ break;
+ case 5: /* fscale */
+ gen_op_fscale();
+ break;
+ case 4: /* frndint */
+ gen_op_frndint();
+ break;
+ case 6: /* fsin */
+ gen_op_fsin();
+ break;
+ default:
+ case 7: /* fcos */
+ gen_op_fcos();
+ break;
+ }
+ break;
+ case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */
+ case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */
+ case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */
+ {
+ int op1;
+
+ op1 = op & 7;
+ if (op >= 0x20) {
+ gen_op_fp_arith_STN_ST0[op1](opreg);
+ if (op >= 0x30)
+ gen_op_fpop();
+ } else {
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fp_arith_ST0_FT0[op1]();
+ }
+ }
+ break;
+ case 0x02: /* fcom */
+ case 0x22: /* fcom2, undocumented op */
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fcom_ST0_FT0();
+ break;
+ case 0x03: /* fcomp */
+ case 0x23: /* fcomp3, undocumented op */
+ case 0x32: /* fcomp5, undocumented op */
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fcom_ST0_FT0();
+ gen_op_fpop();
+ break;
+ case 0x15: /* da/5 */
+ switch(rm) {
+ case 1: /* fucompp */
+ gen_op_fmov_FT0_STN(1);
+ gen_op_fucom_ST0_FT0();
+ gen_op_fpop();
+ gen_op_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1c:
+ switch(rm) {
+ case 0: /* feni (287 only, just do nop here) */
+ break;
+ case 1: /* fdisi (287 only, just do nop here) */
+ break;
+ case 2: /* fclex */
+ gen_op_fclex();
+ break;
+ case 3: /* fninit */
+ gen_op_fninit();
+ break;
+ case 4: /* fsetpm (287 only, just do nop here) */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1d: /* fucomi */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fucomi_ST0_FT0();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x1e: /* fcomi */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fcomi_ST0_FT0();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x28: /* ffree sti */
+ gen_op_ffree_STN(opreg);
+ break;
+ case 0x2a: /* fst sti */
+ gen_op_fmov_STN_ST0(opreg);
+ break;
+ case 0x2b: /* fstp sti */
+ case 0x0b: /* fstp1 sti, undocumented op */
+ case 0x3a: /* fstp8 sti, undocumented op */
+ case 0x3b: /* fstp9 sti, undocumented op */
+ gen_op_fmov_STN_ST0(opreg);
+ gen_op_fpop();
+ break;
+ case 0x2c: /* fucom st(i) */
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fucom_ST0_FT0();
+ break;
+ case 0x2d: /* fucomp st(i) */
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fucom_ST0_FT0();
+ gen_op_fpop();
+ break;
+ case 0x33: /* de/3 */
+ switch(rm) {
+ case 1: /* fcompp */
+ gen_op_fmov_FT0_STN(1);
+ gen_op_fcom_ST0_FT0();
+ gen_op_fpop();
+ gen_op_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x38: /* ffreep sti, undocumented op */
+ gen_op_ffree_STN(opreg);
+ gen_op_fpop();
+ break;
+ case 0x3c: /* df/4 */
+ switch(rm) {
+ case 0:
+ gen_op_fnstsw_EAX();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x3d: /* fucomip */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fucomi_ST0_FT0();
+ gen_op_fpop();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x3e: /* fcomip */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_fmov_FT0_STN(opreg);
+ gen_op_fcomi_ST0_FT0();
+ gen_op_fpop();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x10 ... 0x13: /* fcmovxx */
+ case 0x18 ... 0x1b:
+ {
+ int op1;
+ const static uint8_t fcmov_cc[8] = {
+ (JCC_B << 1),
+ (JCC_Z << 1),
+ (JCC_BE << 1),
+ (JCC_P << 1),
+ };
+ op1 = fcmov_cc[op & 3] | ((op >> 3) & 1);
+ gen_setcc(s, op1);
+ gen_op_fcmov_ST0_STN_T0(opreg);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+#ifdef USE_CODE_COPY
+ s->tb->cflags |= CF_TB_FP_USED;
+#endif
+ break;
+ /************************/
+ /* string ops */
+
+ case 0xa4: /* movsS */
+ case 0xa5:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_movs(s, ot);
+ }
+ break;
+
+ case 0xaa: /* stosS */
+ case 0xab:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_stos(s, ot);
+ }
+ break;
+ case 0xac: /* lodsS */
+ case 0xad:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_lods(s, ot);
+ }
+ break;
+ case 0xae: /* scasS */
+ case 0xaf:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & PREFIX_REPNZ) {
+ gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
+ } else if (prefixes & PREFIX_REPZ) {
+ gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0);
+ } else {
+ gen_scas(s, ot);
+ s->cc_op = CC_OP_SUBB + ot;
+ }
+ break;
+
+ case 0xa6: /* cmpsS */
+ case 0xa7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & PREFIX_REPNZ) {
+ gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
+ } else if (prefixes & PREFIX_REPZ) {
+ gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0);
+ } else {
+ gen_cmps(s, ot);
+ s->cc_op = CC_OP_SUBB + ot;
+ }
+ break;
+ case 0x6c: /* insS */
+ case 0x6d:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_check_io(s, ot, 1, pc_start - s->cs_base);
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_ins(s, ot);
+ }
+ break;
+ case 0x6e: /* outsS */
+ case 0x6f:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_check_io(s, ot, 1, pc_start - s->cs_base);
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_outs(s, ot);
+ }
+ break;
+
+ /************************/
+ /* port I/O */
+ case 0xe4:
+ case 0xe5:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = ldub_code(s->pc++);
+ gen_op_movl_T0_im(val);
+ gen_check_io(s, ot, 0, pc_start - s->cs_base);
+ gen_op_in[ot]();
+ gen_op_mov_reg_T1[ot][R_EAX]();
+ break;
+ case 0xe6:
+ case 0xe7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = ldub_code(s->pc++);
+ gen_op_movl_T0_im(val);
+ gen_check_io(s, ot, 0, pc_start - s->cs_base);
+ gen_op_mov_TN_reg[ot][1][R_EAX]();
+ gen_op_out[ot]();
+ break;
+ case 0xec:
+ case 0xed:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg[OT_WORD][0][R_EDX]();
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, 0, pc_start - s->cs_base);
+ gen_op_in[ot]();
+ gen_op_mov_reg_T1[ot][R_EAX]();
+ break;
+ case 0xee:
+ case 0xef:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg[OT_WORD][0][R_EDX]();
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, 0, pc_start - s->cs_base);
+ gen_op_mov_TN_reg[ot][1][R_EAX]();
+ gen_op_out[ot]();
+ break;
+
+ /************************/
+ /* control */
+ case 0xc2: /* ret im */
+ val = ldsw_code(s->pc);
+ s->pc += 2;
+ gen_pop_T0(s);
+ if (CODE64(s) && s->dflag)
+ s->dflag = 2;
+ gen_stack_update(s, val + (2 << s->dflag));
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 0xc3: /* ret */
+ gen_pop_T0(s);
+ gen_pop_update(s);
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 0xca: /* lret im */
+ val = ldsw_code(s->pc);
+ s->pc += 2;
+ do_lret:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_lret_protected(s->dflag, val);
+ } else {
+ gen_stack_A0(s);
+ /* pop offset */
+ gen_op_ld_T0_A0[1 + s->dflag + s->mem_index]();
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ /* NOTE: keeping EIP updated is not a problem in case of
+ exception */
+ gen_op_jmp_T0();
+ /* pop selector */
+ gen_op_addl_A0_im(2 << s->dflag);
+ gen_op_ld_T0_A0[1 + s->dflag + s->mem_index]();
+ gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS]));
+ /* add stack offset */
+ gen_stack_update(s, val + (4 << s->dflag));
+ }
+ gen_eob(s);
+ break;
+ case 0xcb: /* lret */
+ val = 0;
+ goto do_lret;
+ case 0xcf: /* iret */
+ if (!s->pe) {
+ /* real mode */
+ gen_op_iret_real(s->dflag);
+ s->cc_op = CC_OP_EFLAGS;
+ } else if (s->vm86) {
+ if (s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_op_iret_real(s->dflag);
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_iret_protected(s->dflag, s->pc - s->cs_base);
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ gen_eob(s);
+ break;
+ case 0xe8: /* call im */
+ {
+ if (dflag)
+ tval = (int32_t)insn_get(s, OT_LONG);
+ else
+ tval = (int16_t)insn_get(s, OT_WORD);
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_movtl_T0_im(next_eip);
+ gen_push_T0(s);
+ gen_jmp(s, tval);
+ }
+ break;
+ case 0x9a: /* lcall im */
+ {
+ unsigned int selector, offset;
+
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ offset = insn_get(s, ot);
+ selector = insn_get(s, OT_WORD);
+
+ gen_op_movl_T0_im(selector);
+ gen_op_movl_T1_imu(offset);
+ }
+ goto do_lcall;
+ case 0xe9: /* jmp im */
+ if (dflag)
+ tval = (int32_t)insn_get(s, OT_LONG);
+ else
+ tval = (int16_t)insn_get(s, OT_WORD);
+ tval += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_jmp(s, tval);
+ break;
+ case 0xea: /* ljmp im */
+ {
+ unsigned int selector, offset;
+
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ offset = insn_get(s, ot);
+ selector = insn_get(s, OT_WORD);
+
+ gen_op_movl_T0_im(selector);
+ gen_op_movl_T1_imu(offset);
+ }
+ goto do_ljmp;
+ case 0xeb: /* jmp Jb */
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ tval += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_jmp(s, tval);
+ break;
+ case 0x70 ... 0x7f: /* jcc Jb */
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ goto do_jcc;
+ case 0x180 ... 0x18f: /* jcc Jv */
+ if (dflag) {
+ tval = (int32_t)insn_get(s, OT_LONG);
+ } else {
+ tval = (int16_t)insn_get(s, OT_WORD);
+ }
+ do_jcc:
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_jcc(s, b, tval, next_eip);
+ break;
+
+ case 0x190 ... 0x19f: /* setcc Gv */
+ modrm = ldub_code(s->pc++);
+ gen_setcc(s, b);
+ gen_ldst_modrm(s, modrm, OT_BYTE, OR_TMP0, 1);
+ break;
+ case 0x140 ... 0x14f: /* cmov Gv, Ev */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ gen_setcc(s, b);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0[ot + s->mem_index]();
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg[ot][1][rm]();
+ }
+ gen_op_cmov_reg_T1_T0[ot - OT_WORD][reg]();
+ break;
+
+ /************************/
+ /* flags */
+ case 0x9c: /* pushf */
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_movl_T0_eflags();
+ gen_push_T0(s);
+ }
+ break;
+ case 0x9d: /* popf */
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_pop_T0(s);
+ if (s->cpl == 0) {
+ if (s->dflag) {
+ gen_op_movl_eflags_T0_cpl0();
+ } else {
+ gen_op_movw_eflags_T0_cpl0();
+ }
+ } else {
+ if (s->cpl <= s->iopl) {
+ if (s->dflag) {
+ gen_op_movl_eflags_T0_io();
+ } else {
+ gen_op_movw_eflags_T0_io();
+ }
+ } else {
+ if (s->dflag) {
+ gen_op_movl_eflags_T0();
+ } else {
+ gen_op_movw_eflags_T0();
+ }
+ }
+ }
+ gen_pop_update(s);
+ s->cc_op = CC_OP_EFLAGS;
+ /* abort translation because TF flag may change */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x9e: /* sahf */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_op_mov_TN_reg[OT_BYTE][0][R_AH]();
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_movb_eflags_T0();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x9f: /* lahf */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_movl_T0_eflags();
+ gen_op_mov_reg_T0[OT_BYTE][R_AH]();
+ break;
+ case 0xf5: /* cmc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_cmc();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xf8: /* clc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_clc();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xf9: /* stc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_stc();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xfc: /* cld */
+ gen_op_cld();
+ break;
+ case 0xfd: /* std */
+ gen_op_std();
+ break;
+
+ /************************/
+ /* bit operations */
+ case 0x1ba: /* bt/bts/btr/btc Gv, im */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ op = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ s->rip_offset = 1;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+ /* load shift */
+ val = ldub_code(s->pc++);
+ gen_op_movl_T1_im(val);
+ if (op < 4)
+ goto illegal_op;
+ op -= 4;
+ gen_op_btx_T0_T1_cc[ot - OT_WORD][op]();
+ s->cc_op = CC_OP_SARB + ot;
+ if (op != 0) {
+ if (mod != 3)
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ else
+ gen_op_mov_reg_T0[ot][rm]();
+ gen_op_update_bt_cc();
+ }
+ break;
+ case 0x1a3: /* bt Gv, Ev */
+ op = 0;
+ goto do_btx;
+ case 0x1ab: /* bts */
+ op = 1;
+ goto do_btx;
+ case 0x1b3: /* btr */
+ op = 2;
+ goto do_btx;
+ case 0x1bb: /* btc */
+ op = 3;
+ do_btx:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg[OT_LONG][1][reg]();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* specific case: we need to add a displacement */
+ gen_op_add_bit_A0_T1[ot - OT_WORD]();
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+ gen_op_btx_T0_T1_cc[ot - OT_WORD][op]();
+ s->cc_op = CC_OP_SARB + ot;
+ if (op != 0) {
+ if (mod != 3)
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ else
+ gen_op_mov_reg_T0[ot][rm]();
+ gen_op_update_bt_cc();
+ }
+ break;
+ case 0x1bc: /* bsf */
+ case 0x1bd: /* bsr */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ /* NOTE: in order to handle the 0 case, we must load the
+ result. It could be optimized with a generated jump */
+ gen_op_mov_TN_reg[ot][1][reg]();
+ gen_op_bsx_T0_cc[ot - OT_WORD][b & 1]();
+ gen_op_mov_reg_T1[ot][reg]();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+ /************************/
+ /* bcd */
+ case 0x27: /* daa */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_daa();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x2f: /* das */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_das();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x37: /* aaa */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_aaa();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x3f: /* aas */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_aas();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xd4: /* aam */
+ if (CODE64(s))
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ gen_op_aam(val);
+ s->cc_op = CC_OP_LOGICB;
+ break;
+ case 0xd5: /* aad */
+ if (CODE64(s))
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ gen_op_aad(val);
+ s->cc_op = CC_OP_LOGICB;
+ break;
+ /************************/
+ /* misc */
+ case 0x90: /* nop */
+ /* XXX: xchg + rex handling */
+ /* XXX: correct lock test for all insn */
+ if (prefixes & PREFIX_LOCK)
+ goto illegal_op;
+ break;
+ case 0x9b: /* fwait */
+ if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) ==
+ (HF_MP_MASK | HF_TS_MASK)) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_fwait();
+ }
+ break;
+ case 0xcc: /* int3 */
+ gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base);
+ break;
+ case 0xcd: /* int N */
+ val = ldub_code(s->pc++);
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base);
+ }
+ break;
+ case 0xce: /* into */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_into(s->pc - pc_start);
+ break;
+ case 0xf1: /* icebp (undocumented, exits to external debugger) */
+#if 1
+ gen_debug(s, pc_start - s->cs_base);
+#else
+ /* start debug */
+ tb_flush(cpu_single_env);
+ cpu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM);
+#endif
+ break;
+ case 0xfa: /* cli */
+ if (!s->vm86) {
+ if (s->cpl <= s->iopl) {
+ gen_op_cli();
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ } else {
+ if (s->iopl == 3) {
+ gen_op_cli();
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ }
+ break;
+ case 0xfb: /* sti */
+ if (!s->vm86) {
+ if (s->cpl <= s->iopl) {
+ gen_sti:
+ gen_op_sti();
+ /* interruptions are enabled only the first insn after sti */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_op_set_inhibit_irq();
+ /* give a chance to handle pending irqs */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ } else {
+ if (s->iopl == 3) {
+ goto gen_sti;
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ }
+ break;
+ case 0x62: /* bound */
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_op_mov_TN_reg[ot][0][reg]();
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_jmp_im(pc_start - s->cs_base);
+ if (ot == OT_WORD)
+ gen_op_boundw();
+ else
+ gen_op_boundl();
+ break;
+ case 0x1c8 ... 0x1cf: /* bswap reg */
+ reg = (b & 7) | REX_B(s);
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_mov_TN_reg[OT_QUAD][0][reg]();
+ gen_op_bswapq_T0();
+ gen_op_mov_reg_T0[OT_QUAD][reg]();
+ } else
+#endif
+ {
+ gen_op_mov_TN_reg[OT_LONG][0][reg]();
+ gen_op_bswapl_T0();
+ gen_op_mov_reg_T0[OT_LONG][reg]();
+ }
+ break;
+ case 0xd6: /* salc */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_salc();
+ break;
+ case 0xe0: /* loopnz */
+ case 0xe1: /* loopz */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ /* FALL THRU */
+ case 0xe2: /* loop */
+ case 0xe3: /* jecxz */
+ {
+ int l1, l2;
+
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ b &= 3;
+ if (b == 3) {
+ gen_op_jz_ecx[s->aflag](l1);
+ } else {
+ gen_op_dec_ECX[s->aflag]();
+ if (b <= 1)
+ gen_op_mov_T0_cc();
+ gen_op_loop[s->aflag][b](l1);
+ }
+
+ gen_jmp_im(next_eip);
+ gen_op_jmp_label(l2);
+ gen_set_label(l1);
+ gen_jmp_im(tval);
+ gen_set_label(l2);
+ gen_eob(s);
+ }
+ break;
+ case 0x130: /* wrmsr */
+ case 0x132: /* rdmsr */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (b & 2)
+ gen_op_rdmsr();
+ else
+ gen_op_wrmsr();
+ }
+ break;
+ case 0x131: /* rdtsc */
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_rdtsc();
+ break;
+ case 0x134: /* sysenter */
+ if (CODE64(s))
+ goto illegal_op;
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_sysenter();
+ gen_eob(s);
+ }
+ break;
+ case 0x135: /* sysexit */
+ if (CODE64(s))
+ goto illegal_op;
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_sysexit();
+ gen_eob(s);
+ }
+ break;
+#ifdef TARGET_X86_64
+ case 0x105: /* syscall */
+ /* XXX: is it usable in real mode ? */
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_syscall(s->pc - pc_start);
+ gen_eob(s);
+ break;
+ case 0x107: /* sysret */
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_sysret(s->dflag);
+ /* condition codes are modified only in long mode */
+ if (s->lma)
+ s->cc_op = CC_OP_EFLAGS;
+ gen_eob(s);
+ }
+ break;
+#endif
+ case 0x1a2: /* cpuid */
+ gen_op_cpuid();
+ break;
+ case 0xf4: /* hlt */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_op_hlt();
+ s->is_jmp = 3;
+ }
+ break;
+ case 0x100:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* sldt */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_op_movl_T0_env(offsetof(CPUX86State,ldt.selector));
+ ot = OT_WORD;
+ if (mod == 3)
+ ot += s->dflag;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+ case 2: /* lldt */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_lldt_T0();
+ }
+ break;
+ case 1: /* str */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_op_movl_T0_env(offsetof(CPUX86State,tr.selector));
+ ot = OT_WORD;
+ if (mod == 3)
+ ot += s->dflag;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+ case 3: /* ltr */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_op_ltr_T0();
+ }
+ break;
+ case 4: /* verr */
+ case 5: /* verw */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (op == 4)
+ gen_op_verr();
+ else
+ gen_op_verw();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x101:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ rm = modrm & 7;
+ switch(op) {
+ case 0: /* sgdt */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_movl_T0_env(offsetof(CPUX86State, gdt.limit));
+ gen_op_st_T0_A0[OT_WORD + s->mem_index]();
+ gen_add_A0_im(s, 2);
+ gen_op_movtl_T0_env(offsetof(CPUX86State, gdt.base));
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ gen_op_st_T0_A0[CODE64(s) + OT_LONG + s->mem_index]();
+ break;
+ case 1:
+ if (mod == 3) {
+ switch (rm) {
+ case 0: /* monitor */
+ if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
+ s->cpl != 0)
+ goto illegal_op;
+ gen_jmp_im(pc_start - s->cs_base);
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg[R_EBX]();
+ gen_op_addq_A0_AL();
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg[R_EBX]();
+ gen_op_addl_A0_AL();
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ }
+ gen_add_A0_ds_seg(s);
+ gen_op_monitor();
+ break;
+ case 1: /* mwait */
+ if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
+ s->cpl != 0)
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_op_mwait();
+ gen_eob(s);
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else { /* sidt */
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_movl_T0_env(offsetof(CPUX86State, idt.limit));
+ gen_op_st_T0_A0[OT_WORD + s->mem_index]();
+ gen_add_A0_im(s, 2);
+ gen_op_movtl_T0_env(offsetof(CPUX86State, idt.base));
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ gen_op_st_T0_A0[CODE64(s) + OT_LONG + s->mem_index]();
+ }
+ break;
+ case 2: /* lgdt */
+ case 3: /* lidt */
+ if (mod == 3)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0[OT_WORD + s->mem_index]();
+ gen_add_A0_im(s, 2);
+ gen_op_ld_T0_A0[CODE64(s) + OT_LONG + s->mem_index]();
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ if (op == 2) {
+ gen_op_movtl_env_T0(offsetof(CPUX86State,gdt.base));
+ gen_op_movl_env_T1(offsetof(CPUX86State,gdt.limit));
+ } else {
+ gen_op_movtl_env_T0(offsetof(CPUX86State,idt.base));
+ gen_op_movl_env_T1(offsetof(CPUX86State,idt.limit));
+ }
+ }
+ break;
+ case 4: /* smsw */
+ gen_op_movl_T0_env(offsetof(CPUX86State,cr[0]));
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 1);
+ break;
+ case 6: /* lmsw */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_op_lmsw_T0();
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 7: /* invlpg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (mod == 3) {
+#ifdef TARGET_X86_64
+ if (CODE64(s) && rm == 0) {
+ /* swapgs */
+ gen_op_movtl_T0_env(offsetof(CPUX86State,segs[R_GS].base));
+ gen_op_movtl_T1_env(offsetof(CPUX86State,kernelgsbase));
+ gen_op_movtl_env_T1(offsetof(CPUX86State,segs[R_GS].base));
+ gen_op_movtl_env_T0(offsetof(CPUX86State,kernelgsbase));
+ } else
+#endif
+ {
+ goto illegal_op;
+ }
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_invlpg_A0();
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x108: /* invd */
+ case 0x109: /* wbinvd */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ /* nothing to do */
+ }
+ break;
+ case 0x63: /* arpl or movslS (x86_64) */
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ int d_ot;
+ /* d_ot is the size of destination */
+ d_ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+
+ if (mod == 3) {
+ gen_op_mov_TN_reg[OT_LONG][0][rm]();
+ /* sign extend */
+ if (d_ot == OT_QUAD)
+ gen_op_movslq_T0_T0();
+ gen_op_mov_reg_T0[d_ot][reg]();
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (d_ot == OT_QUAD) {
+ gen_op_lds_T0_A0[OT_LONG + s->mem_index]();
+ } else {
+ gen_op_ld_T0_A0[OT_LONG + s->mem_index]();
+ }
+ gen_op_mov_reg_T0[d_ot][reg]();
+ }
+ } else
+#endif
+ {
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ }
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_arpl();
+ s->cc_op = CC_OP_EFLAGS;
+ if (mod != 3) {
+ gen_op_st_T0_A0[ot + s->mem_index]();
+ } else {
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ gen_op_arpl_update();
+ }
+ break;
+ case 0x102: /* lar */
+ case 0x103: /* lsl */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_op_mov_TN_reg[ot][1][reg]();
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (b == 0x102)
+ gen_op_lar();
+ else
+ gen_op_lsl();
+ s->cc_op = CC_OP_EFLAGS;
+ gen_op_mov_reg_T1[ot][reg]();
+ break;
+ case 0x118:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* prefetchnta */
+ case 1: /* prefetchnt0 */
+ case 2: /* prefetchnt0 */
+ case 3: /* prefetchnt0 */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* nothing more to do */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x120: /* mov reg, crN */
+ case 0x122: /* mov crN, reg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ modrm = ldub_code(s->pc++);
+ if ((modrm & 0xc0) != 0xc0)
+ goto illegal_op;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (CODE64(s))
+ ot = OT_QUAD;
+ else
+ ot = OT_LONG;
+ switch(reg) {
+ case 0:
+ case 2:
+ case 3:
+ case 4:
+ case 8:
+ if (b & 2) {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ gen_op_movl_crN_T0(reg);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+#if !defined(CONFIG_USER_ONLY)
+ if (reg == 8)
+ gen_op_movtl_T0_cr8();
+ else
+#endif
+ gen_op_movtl_T0_env(offsetof(CPUX86State,cr[reg]));
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ case 0x121: /* mov reg, drN */
+ case 0x123: /* mov drN, reg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ modrm = ldub_code(s->pc++);
+ if ((modrm & 0xc0) != 0xc0)
+ goto illegal_op;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (CODE64(s))
+ ot = OT_QUAD;
+ else
+ ot = OT_LONG;
+ /* XXX: do it dynamically with CR4.DE bit */
+ if (reg == 4 || reg == 5 || reg >= 8)
+ goto illegal_op;
+ if (b & 2) {
+ gen_op_mov_TN_reg[ot][0][rm]();
+ gen_op_movl_drN_T0(reg);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+ gen_op_movtl_T0_env(offsetof(CPUX86State,dr[reg]));
+ gen_op_mov_reg_T0[ot][rm]();
+ }
+ }
+ break;
+ case 0x106: /* clts */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_op_clts();
+ /* abort block because static cpu state changed */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ /* MMX/SSE/SSE2/PNI support */
+ case 0x1c3: /* MOVNTI reg, mem */
+ if (!(s->cpuid_features & CPUID_SSE2))
+ goto illegal_op;
+ ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ /* generate a generic store */
+ gen_ldst_modrm(s, modrm, ot, reg, 1);
+ break;
+ case 0x1ae:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* fxsave */
+ if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) ||
+ (s->flags & HF_EM_MASK))
+ goto illegal_op;
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_fxsave_A0((s->dflag == 2));
+ break;
+ case 1: /* fxrstor */
+ if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) ||
+ (s->flags & HF_EM_MASK))
+ goto illegal_op;
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_fxrstor_A0((s->dflag == 2));
+ break;
+ case 2: /* ldmxcsr */
+ case 3: /* stmxcsr */
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) ||
+ mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (op == 2) {
+ gen_op_ld_T0_A0[OT_LONG + s->mem_index]();
+ gen_op_movl_env_T0(offsetof(CPUX86State, mxcsr));
+ } else {
+ gen_op_movl_T0_env(offsetof(CPUX86State, mxcsr));
+ gen_op_st_T0_A0[OT_LONG + s->mem_index]();
+ }
+ break;
+ case 5: /* lfence */
+ case 6: /* mfence */
+ if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE))
+ goto illegal_op;
+ break;
+ case 7: /* sfence / clflush */
+ if ((modrm & 0xc7) == 0xc0) {
+ /* sfence */
+ if (!(s->cpuid_features & CPUID_SSE))
+ goto illegal_op;
+ } else {
+ /* clflush */
+ if (!(s->cpuid_features & CPUID_CLFLUSH))
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x10d: /* prefetch */
+ modrm = ldub_code(s->pc++);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* ignore for now */
+ break;
+ case 0x110 ... 0x117:
+ case 0x128 ... 0x12f:
+ case 0x150 ... 0x177:
+ case 0x17c ... 0x17f:
+ case 0x1c2:
+ case 0x1c4 ... 0x1c6:
+ case 0x1d0 ... 0x1fe:
+ gen_sse(s, b, pc_start, rex_r);
+ break;
+ default:
+ goto illegal_op;
+ }
+ /* lock generation */
+ if (s->prefix & PREFIX_LOCK)
+ gen_op_unlock();
+ return s->pc;
+ illegal_op:
+ if (s->prefix & PREFIX_LOCK)
+ gen_op_unlock();
+ /* XXX: ensure that no lock was generated */
+ gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base);
+ return s->pc;
+}
+
+#define CC_OSZAPC (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C)
+#define CC_OSZAP (CC_O | CC_S | CC_Z | CC_A | CC_P)
+
+/* flags read by an operation */
+static uint16_t opc_read_flags[NB_OPS] = {
+ [INDEX_op_aas] = CC_A,
+ [INDEX_op_aaa] = CC_A,
+ [INDEX_op_das] = CC_A | CC_C,
+ [INDEX_op_daa] = CC_A | CC_C,
+
+ /* subtle: due to the incl/decl implementation, C is used */
+ [INDEX_op_update_inc_cc] = CC_C,
+
+ [INDEX_op_into] = CC_O,
+
+ [INDEX_op_jb_subb] = CC_C,
+ [INDEX_op_jb_subw] = CC_C,
+ [INDEX_op_jb_subl] = CC_C,
+
+ [INDEX_op_jz_subb] = CC_Z,
+ [INDEX_op_jz_subw] = CC_Z,
+ [INDEX_op_jz_subl] = CC_Z,
+
+ [INDEX_op_jbe_subb] = CC_Z | CC_C,
+ [INDEX_op_jbe_subw] = CC_Z | CC_C,
+ [INDEX_op_jbe_subl] = CC_Z | CC_C,
+
+ [INDEX_op_js_subb] = CC_S,
+ [INDEX_op_js_subw] = CC_S,
+ [INDEX_op_js_subl] = CC_S,
+
+ [INDEX_op_jl_subb] = CC_O | CC_S,
+ [INDEX_op_jl_subw] = CC_O | CC_S,
+ [INDEX_op_jl_subl] = CC_O | CC_S,
+
+ [INDEX_op_jle_subb] = CC_O | CC_S | CC_Z,
+ [INDEX_op_jle_subw] = CC_O | CC_S | CC_Z,
+ [INDEX_op_jle_subl] = CC_O | CC_S | CC_Z,
+
+ [INDEX_op_loopnzw] = CC_Z,
+ [INDEX_op_loopnzl] = CC_Z,
+ [INDEX_op_loopzw] = CC_Z,
+ [INDEX_op_loopzl] = CC_Z,
+
+ [INDEX_op_seto_T0_cc] = CC_O,
+ [INDEX_op_setb_T0_cc] = CC_C,
+ [INDEX_op_setz_T0_cc] = CC_Z,
+ [INDEX_op_setbe_T0_cc] = CC_Z | CC_C,
+ [INDEX_op_sets_T0_cc] = CC_S,
+ [INDEX_op_setp_T0_cc] = CC_P,
+ [INDEX_op_setl_T0_cc] = CC_O | CC_S,
+ [INDEX_op_setle_T0_cc] = CC_O | CC_S | CC_Z,
+
+ [INDEX_op_setb_T0_subb] = CC_C,
+ [INDEX_op_setb_T0_subw] = CC_C,
+ [INDEX_op_setb_T0_subl] = CC_C,
+
+ [INDEX_op_setz_T0_subb] = CC_Z,
+ [INDEX_op_setz_T0_subw] = CC_Z,
+ [INDEX_op_setz_T0_subl] = CC_Z,
+
+ [INDEX_op_setbe_T0_subb] = CC_Z | CC_C,
+ [INDEX_op_setbe_T0_subw] = CC_Z | CC_C,
+ [INDEX_op_setbe_T0_subl] = CC_Z | CC_C,
+
+ [INDEX_op_sets_T0_subb] = CC_S,
+ [INDEX_op_sets_T0_subw] = CC_S,
+ [INDEX_op_sets_T0_subl] = CC_S,
+
+ [INDEX_op_setl_T0_subb] = CC_O | CC_S,
+ [INDEX_op_setl_T0_subw] = CC_O | CC_S,
+ [INDEX_op_setl_T0_subl] = CC_O | CC_S,
+
+ [INDEX_op_setle_T0_subb] = CC_O | CC_S | CC_Z,
+ [INDEX_op_setle_T0_subw] = CC_O | CC_S | CC_Z,
+ [INDEX_op_setle_T0_subl] = CC_O | CC_S | CC_Z,
+
+ [INDEX_op_movl_T0_eflags] = CC_OSZAPC,
+ [INDEX_op_cmc] = CC_C,
+ [INDEX_op_salc] = CC_C,
+
+ /* needed for correct flag optimisation before string ops */
+ [INDEX_op_jnz_ecxw] = CC_OSZAPC,
+ [INDEX_op_jnz_ecxl] = CC_OSZAPC,
+ [INDEX_op_jz_ecxw] = CC_OSZAPC,
+ [INDEX_op_jz_ecxl] = CC_OSZAPC,
+
+#ifdef TARGET_X86_64
+ [INDEX_op_jb_subq] = CC_C,
+ [INDEX_op_jz_subq] = CC_Z,
+ [INDEX_op_jbe_subq] = CC_Z | CC_C,
+ [INDEX_op_js_subq] = CC_S,
+ [INDEX_op_jl_subq] = CC_O | CC_S,
+ [INDEX_op_jle_subq] = CC_O | CC_S | CC_Z,
+
+ [INDEX_op_loopnzq] = CC_Z,
+ [INDEX_op_loopzq] = CC_Z,
+
+ [INDEX_op_setb_T0_subq] = CC_C,
+ [INDEX_op_setz_T0_subq] = CC_Z,
+ [INDEX_op_setbe_T0_subq] = CC_Z | CC_C,
+ [INDEX_op_sets_T0_subq] = CC_S,
+ [INDEX_op_setl_T0_subq] = CC_O | CC_S,
+ [INDEX_op_setle_T0_subq] = CC_O | CC_S | CC_Z,
+
+ [INDEX_op_jnz_ecxq] = CC_OSZAPC,
+ [INDEX_op_jz_ecxq] = CC_OSZAPC,
+#endif
+
+#define DEF_READF(SUFFIX)\
+ [INDEX_op_adcb ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_adcw ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_adcl ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ X86_64_DEF([INDEX_op_adcq ## SUFFIX ## _T0_T1_cc] = CC_C,)\
+ [INDEX_op_sbbb ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_sbbw ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_sbbl ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ X86_64_DEF([INDEX_op_sbbq ## SUFFIX ## _T0_T1_cc] = CC_C,)\
+\
+ [INDEX_op_rclb ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_rclw ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_rcll ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ X86_64_DEF([INDEX_op_rclq ## SUFFIX ## _T0_T1_cc] = CC_C,)\
+ [INDEX_op_rcrb ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_rcrw ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ [INDEX_op_rcrl ## SUFFIX ## _T0_T1_cc] = CC_C,\
+ X86_64_DEF([INDEX_op_rcrq ## SUFFIX ## _T0_T1_cc] = CC_C,)
+
+ DEF_READF( )
+ DEF_READF(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_READF(_kernel)
+ DEF_READF(_user)
+#endif
+};
+
+/* flags written by an operation */
+static uint16_t opc_write_flags[NB_OPS] = {
+ [INDEX_op_update2_cc] = CC_OSZAPC,
+ [INDEX_op_update1_cc] = CC_OSZAPC,
+ [INDEX_op_cmpl_T0_T1_cc] = CC_OSZAPC,
+ [INDEX_op_update_neg_cc] = CC_OSZAPC,
+ /* subtle: due to the incl/decl implementation, C is used */
+ [INDEX_op_update_inc_cc] = CC_OSZAPC,
+ [INDEX_op_testl_T0_T1_cc] = CC_OSZAPC,
+
+ [INDEX_op_mulb_AL_T0] = CC_OSZAPC,
+ [INDEX_op_mulw_AX_T0] = CC_OSZAPC,
+ [INDEX_op_mull_EAX_T0] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_mulq_EAX_T0] = CC_OSZAPC,)
+ [INDEX_op_imulb_AL_T0] = CC_OSZAPC,
+ [INDEX_op_imulw_AX_T0] = CC_OSZAPC,
+ [INDEX_op_imull_EAX_T0] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_imulq_EAX_T0] = CC_OSZAPC,)
+ [INDEX_op_imulw_T0_T1] = CC_OSZAPC,
+ [INDEX_op_imull_T0_T1] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_imulq_T0_T1] = CC_OSZAPC,)
+
+ /* sse */
+ [INDEX_op_ucomiss] = CC_OSZAPC,
+ [INDEX_op_ucomisd] = CC_OSZAPC,
+ [INDEX_op_comiss] = CC_OSZAPC,
+ [INDEX_op_comisd] = CC_OSZAPC,
+
+ /* bcd */
+ [INDEX_op_aam] = CC_OSZAPC,
+ [INDEX_op_aad] = CC_OSZAPC,
+ [INDEX_op_aas] = CC_OSZAPC,
+ [INDEX_op_aaa] = CC_OSZAPC,
+ [INDEX_op_das] = CC_OSZAPC,
+ [INDEX_op_daa] = CC_OSZAPC,
+
+ [INDEX_op_movb_eflags_T0] = CC_S | CC_Z | CC_A | CC_P | CC_C,
+ [INDEX_op_movw_eflags_T0] = CC_OSZAPC,
+ [INDEX_op_movl_eflags_T0] = CC_OSZAPC,
+ [INDEX_op_movw_eflags_T0_io] = CC_OSZAPC,
+ [INDEX_op_movl_eflags_T0_io] = CC_OSZAPC,
+ [INDEX_op_movw_eflags_T0_cpl0] = CC_OSZAPC,
+ [INDEX_op_movl_eflags_T0_cpl0] = CC_OSZAPC,
+ [INDEX_op_clc] = CC_C,
+ [INDEX_op_stc] = CC_C,
+ [INDEX_op_cmc] = CC_C,
+
+ [INDEX_op_btw_T0_T1_cc] = CC_OSZAPC,
+ [INDEX_op_btl_T0_T1_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_btq_T0_T1_cc] = CC_OSZAPC,)
+ [INDEX_op_btsw_T0_T1_cc] = CC_OSZAPC,
+ [INDEX_op_btsl_T0_T1_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_btsq_T0_T1_cc] = CC_OSZAPC,)
+ [INDEX_op_btrw_T0_T1_cc] = CC_OSZAPC,
+ [INDEX_op_btrl_T0_T1_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_btrq_T0_T1_cc] = CC_OSZAPC,)
+ [INDEX_op_btcw_T0_T1_cc] = CC_OSZAPC,
+ [INDEX_op_btcl_T0_T1_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_btcq_T0_T1_cc] = CC_OSZAPC,)
+
+ [INDEX_op_bsfw_T0_cc] = CC_OSZAPC,
+ [INDEX_op_bsfl_T0_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_bsfq_T0_cc] = CC_OSZAPC,)
+ [INDEX_op_bsrw_T0_cc] = CC_OSZAPC,
+ [INDEX_op_bsrl_T0_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_bsrq_T0_cc] = CC_OSZAPC,)
+
+ [INDEX_op_cmpxchgb_T0_T1_EAX_cc] = CC_OSZAPC,
+ [INDEX_op_cmpxchgw_T0_T1_EAX_cc] = CC_OSZAPC,
+ [INDEX_op_cmpxchgl_T0_T1_EAX_cc] = CC_OSZAPC,
+ X86_64_DEF([INDEX_op_cmpxchgq_T0_T1_EAX_cc] = CC_OSZAPC,)
+
+ [INDEX_op_cmpxchg8b] = CC_Z,
+ [INDEX_op_lar] = CC_Z,
+ [INDEX_op_lsl] = CC_Z,
+ [INDEX_op_verr] = CC_Z,
+ [INDEX_op_verw] = CC_Z,
+ [INDEX_op_fcomi_ST0_FT0] = CC_Z | CC_P | CC_C,
+ [INDEX_op_fucomi_ST0_FT0] = CC_Z | CC_P | CC_C,
+
+#define DEF_WRITEF(SUFFIX)\
+ [INDEX_op_adcb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_adcw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_adcl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_adcq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\
+ [INDEX_op_sbbb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_sbbw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_sbbl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_sbbq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_rolb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rolw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_roll ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ X86_64_DEF([INDEX_op_rolq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\
+ [INDEX_op_rorb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rorw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rorl ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ X86_64_DEF([INDEX_op_rorq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\
+\
+ [INDEX_op_rclb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rclw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rcll ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ X86_64_DEF([INDEX_op_rclq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\
+ [INDEX_op_rcrb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rcrw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ [INDEX_op_rcrl ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\
+ X86_64_DEF([INDEX_op_rcrq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\
+\
+ [INDEX_op_shlb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_shlw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_shll ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shlq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_shrb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_shrw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_shrl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shrq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_sarb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_sarw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ [INDEX_op_sarl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_sarq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_shldw ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\
+ [INDEX_op_shldl ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shldq ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,)\
+ [INDEX_op_shldw ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\
+ [INDEX_op_shldl ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shldq ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_shrdw ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\
+ [INDEX_op_shrdl ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shrdq ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,)\
+ [INDEX_op_shrdw ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\
+ [INDEX_op_shrdl ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_shrdq ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,)\
+\
+ [INDEX_op_cmpxchgb ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\
+ [INDEX_op_cmpxchgw ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\
+ [INDEX_op_cmpxchgl ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\
+ X86_64_DEF([INDEX_op_cmpxchgq ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,)
+
+
+ DEF_WRITEF( )
+ DEF_WRITEF(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_WRITEF(_kernel)
+ DEF_WRITEF(_user)
+#endif
+};
+
+/* simpler form of an operation if no flags need to be generated */
+static uint16_t opc_simpler[NB_OPS] = {
+ [INDEX_op_update2_cc] = INDEX_op_nop,
+ [INDEX_op_update1_cc] = INDEX_op_nop,
+ [INDEX_op_update_neg_cc] = INDEX_op_nop,
+#if 0
+ /* broken: CC_OP logic must be rewritten */
+ [INDEX_op_update_inc_cc] = INDEX_op_nop,
+#endif
+
+ [INDEX_op_shlb_T0_T1_cc] = INDEX_op_shlb_T0_T1,
+ [INDEX_op_shlw_T0_T1_cc] = INDEX_op_shlw_T0_T1,
+ [INDEX_op_shll_T0_T1_cc] = INDEX_op_shll_T0_T1,
+ X86_64_DEF([INDEX_op_shlq_T0_T1_cc] = INDEX_op_shlq_T0_T1,)
+
+ [INDEX_op_shrb_T0_T1_cc] = INDEX_op_shrb_T0_T1,
+ [INDEX_op_shrw_T0_T1_cc] = INDEX_op_shrw_T0_T1,
+ [INDEX_op_shrl_T0_T1_cc] = INDEX_op_shrl_T0_T1,
+ X86_64_DEF([INDEX_op_shrq_T0_T1_cc] = INDEX_op_shrq_T0_T1,)
+
+ [INDEX_op_sarb_T0_T1_cc] = INDEX_op_sarb_T0_T1,
+ [INDEX_op_sarw_T0_T1_cc] = INDEX_op_sarw_T0_T1,
+ [INDEX_op_sarl_T0_T1_cc] = INDEX_op_sarl_T0_T1,
+ X86_64_DEF([INDEX_op_sarq_T0_T1_cc] = INDEX_op_sarq_T0_T1,)
+
+#define DEF_SIMPLER(SUFFIX)\
+ [INDEX_op_rolb ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolb ## SUFFIX ## _T0_T1,\
+ [INDEX_op_rolw ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolw ## SUFFIX ## _T0_T1,\
+ [INDEX_op_roll ## SUFFIX ## _T0_T1_cc] = INDEX_op_roll ## SUFFIX ## _T0_T1,\
+ X86_64_DEF([INDEX_op_rolq ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolq ## SUFFIX ## _T0_T1,)\
+\
+ [INDEX_op_rorb ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorb ## SUFFIX ## _T0_T1,\
+ [INDEX_op_rorw ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorw ## SUFFIX ## _T0_T1,\
+ [INDEX_op_rorl ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorl ## SUFFIX ## _T0_T1,\
+ X86_64_DEF([INDEX_op_rorq ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorq ## SUFFIX ## _T0_T1,)
+
+ DEF_SIMPLER( )
+ DEF_SIMPLER(_raw)
+#ifndef CONFIG_USER_ONLY
+ DEF_SIMPLER(_kernel)
+ DEF_SIMPLER(_user)
+#endif
+};
+
+void optimize_flags_init(void)
+{
+ int i;
+ /* put default values in arrays */
+ for(i = 0; i < NB_OPS; i++) {
+ if (opc_simpler[i] == 0)
+ opc_simpler[i] = i;
+ }
+}
+
+/* CPU flags computation optimization: we move backward thru the
+ generated code to see which flags are needed. The operation is
+ modified if suitable */
+static void optimize_flags(uint16_t *opc_buf, int opc_buf_len)
+{
+ uint16_t *opc_ptr;
+ int live_flags, write_flags, op;
+
+ opc_ptr = opc_buf + opc_buf_len;
+ /* live_flags contains the flags needed by the next instructions
+ in the code. At the end of the bloc, we consider that all the
+ flags are live. */
+ live_flags = CC_OSZAPC;
+ while (opc_ptr > opc_buf) {
+ op = *--opc_ptr;
+ /* if none of the flags written by the instruction is used,
+ then we can try to find a simpler instruction */
+ write_flags = opc_write_flags[op];
+ if ((live_flags & write_flags) == 0) {
+ *opc_ptr = opc_simpler[op];
+ }
+ /* compute the live flags before the instruction */
+ live_flags &= ~write_flags;
+ live_flags |= opc_read_flags[op];
+ }
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+ basic block 'tb'. If search_pc is TRUE, also generate PC
+ information for each intermediate instruction. */
+static inline int gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ target_ulong pc_ptr;
+ uint16_t *gen_opc_end;
+ int flags, j, lj, cflags;
+ target_ulong pc_start;
+ target_ulong cs_base;
+
+ /* generate intermediate code */
+ pc_start = tb->pc;
+ cs_base = tb->cs_base;
+ flags = tb->flags;
+ cflags = tb->cflags;
+
+ dc->pe = (flags >> HF_PE_SHIFT) & 1;
+ dc->code32 = (flags >> HF_CS32_SHIFT) & 1;
+ dc->ss32 = (flags >> HF_SS32_SHIFT) & 1;
+ dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1;
+ dc->f_st = 0;
+ dc->vm86 = (flags >> VM_SHIFT) & 1;
+ dc->cpl = (flags >> HF_CPL_SHIFT) & 3;
+ dc->iopl = (flags >> IOPL_SHIFT) & 3;
+ dc->tf = (flags >> TF_SHIFT) & 1;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->cc_op = CC_OP_DYNAMIC;
+ dc->cs_base = cs_base;
+ dc->tb = tb;
+ dc->popl_esp_hack = 0;
+ /* select memory access functions */
+ dc->mem_index = 0;
+ if (flags & HF_SOFTMMU_MASK) {
+ if (dc->cpl == 3)
+ dc->mem_index = 2 * 4;
+ else
+ dc->mem_index = 1 * 4;
+ }
+ dc->cpuid_features = env->cpuid_features;
+ dc->cpuid_ext_features = env->cpuid_ext_features;
+#ifdef TARGET_X86_64
+ dc->lma = (flags >> HF_LMA_SHIFT) & 1;
+ dc->code64 = (flags >> HF_CS64_SHIFT) & 1;
+#endif
+ dc->flags = flags;
+ dc->jmp_opt = !(dc->tf || env->singlestep_enabled ||
+ (flags & HF_INHIBIT_IRQ_MASK)
+#ifndef CONFIG_SOFTMMU
+ || (flags & HF_SOFTMMU_MASK)
+#endif
+ );
+#if 0
+ /* check addseg logic */
+ if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32))
+ printf("ERROR addseg\n");
+#endif
+
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ nb_gen_labels = 0;
+
+ dc->is_jmp = DISAS_NEXT;
+ pc_ptr = pc_start;
+ lj = -1;
+
+ for(;;) {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == pc_ptr) {
+ gen_debug(dc, pc_ptr - dc->cs_base);
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+ gen_opc_pc[lj] = pc_ptr;
+ gen_opc_cc_op[lj] = dc->cc_op;
+ gen_opc_instr_start[lj] = 1;
+ }
+ pc_ptr = disas_insn(dc, pc_ptr);
+ /* stop translation if indicated */
+ if (dc->is_jmp)
+ break;
+ /* if single step mode, we generate only one instruction and
+ generate an exception */
+ /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
+ the flag and abort the translation to give the irqs a
+ change to be happen */
+ if (dc->tf || dc->singlestep_enabled ||
+ (flags & HF_INHIBIT_IRQ_MASK) ||
+ (cflags & CF_SINGLE_INSN)) {
+ gen_jmp_im(pc_ptr - dc->cs_base);
+ gen_eob(dc);
+ break;
+ }
+ /* if too long translation, stop generation too */
+ if (gen_opc_ptr >= gen_opc_end ||
+ (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32)) {
+ gen_jmp_im(pc_ptr - dc->cs_base);
+ gen_eob(dc);
+ break;
+ }
+ }
+ *gen_opc_ptr = INDEX_op_end;
+ /* we don't forget to fill the last values */
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_CPU) {
+ cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+ }
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ int disas_flags;
+ fprintf(logfile, "----------------\n");
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+#ifdef TARGET_X86_64
+ if (dc->code64)
+ disas_flags = 2;
+ else
+#endif
+ disas_flags = !dc->code32;
+ target_disas(logfile, pc_start, pc_ptr - pc_start, disas_flags);
+ fprintf(logfile, "\n");
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+ }
+#endif
+
+ /* optimize flag computations */
+ optimize_flags(gen_opc_buf, gen_opc_ptr - gen_opc_buf);
+
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_OP_OPT) {
+ fprintf(logfile, "AFTER FLAGS OPT:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+#endif
+ if (!search_pc)
+ tb->size = pc_ptr - pc_start;
+ return 0;
+}
+
+int gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
+
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
new file mode 100644
index 0000000..330f9eb
--- /dev/null
+++ b/target-mips/cpu.h
@@ -0,0 +1,279 @@
+#if !defined (__MIPS_CPU_H__)
+#define __MIPS_CPU_H__
+
+#define TARGET_HAS_ICE 1
+
+#include "config.h"
+#include "mips-defs.h"
+#include "cpu-defs.h"
+#include "softfloat.h"
+
+// uint_fast8_t and uint_fast16_t not in <sys/int_types.h>
+// XXX: move that elsewhere
+#if defined(HOST_SOLARIS) && SOLARISREV < 10
+typedef unsigned char uint_fast8_t;
+typedef unsigned int uint_fast16_t;
+#endif
+
+typedef union fpr_t fpr_t;
+union fpr_t {
+ float64 fd; /* ieee double precision */
+ float32 fs[2];/* ieee single precision */
+ uint64_t d; /* binary single fixed-point */
+ uint32_t w[2]; /* binary single fixed-point */
+};
+/* define FP_ENDIAN_IDX to access the same location
+ * in the fpr_t union regardless of the host endianess
+ */
+#if defined(WORDS_BIGENDIAN)
+# define FP_ENDIAN_IDX 1
+#else
+# define FP_ENDIAN_IDX 0
+#endif
+
+#if defined(MIPS_USES_R4K_TLB)
+typedef struct tlb_t tlb_t;
+struct tlb_t {
+ target_ulong VPN;
+ target_ulong end;
+ target_ulong end2;
+ uint_fast8_t ASID;
+ uint_fast16_t G:1;
+ uint_fast16_t C0:3;
+ uint_fast16_t C1:3;
+ uint_fast16_t V0:1;
+ uint_fast16_t V1:1;
+ uint_fast16_t D0:1;
+ uint_fast16_t D1:1;
+ target_ulong PFN[2];
+};
+#endif
+
+typedef struct CPUMIPSState CPUMIPSState;
+struct CPUMIPSState {
+ /* General integer registers */
+ target_ulong gpr[32];
+ /* Special registers */
+ target_ulong PC;
+ uint32_t HI, LO;
+ uint32_t DCR; /* ? */
+#if defined(MIPS_USES_FPU)
+ /* Floating point registers */
+ fpr_t fpr[16];
+#define FPR(cpu, n) ((fpr_t*)&(cpu)->fpr[(n) / 2])
+#define FPR_FD(cpu, n) (FPR(cpu, n)->fd)
+#define FPR_FS(cpu, n) (FPR(cpu, n)->fs[((n) & 1) ^ FP_ENDIAN_IDX])
+#define FPR_D(cpu, n) (FPR(cpu, n)->d)
+#define FPR_W(cpu, n) (FPR(cpu, n)->w[((n) & 1) ^ FP_ENDIAN_IDX])
+
+#ifndef USE_HOST_FLOAT_REGS
+ fpr_t ft0;
+ fpr_t ft1;
+ fpr_t ft2;
+#endif
+ float_status fp_status;
+ /* fpu implementation/revision register */
+ uint32_t fcr0;
+ /* fcsr */
+ uint32_t fcr31;
+#define SET_FP_COND(reg) do { (reg) |= (1<<23); } while(0)
+#define CLEAR_FP_COND(reg) do { (reg) &= ~(1<<23); } while(0)
+#define IS_FP_COND_SET(reg) (((reg) & (1<<23)) != 0)
+#define GET_FP_CAUSE(reg) (((reg) >> 12) & 0x3f)
+#define GET_FP_ENABLE(reg) (((reg) >> 7) & 0x1f)
+#define GET_FP_FLAGS(reg) (((reg) >> 2) & 0x1f)
+#define SET_FP_CAUSE(reg,v) do { (reg) = ((reg) & ~(0x3f << 12)) | ((v) << 12); } while(0)
+#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v) << 7); } while(0)
+#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v) << 2); } while(0)
+#define FP_INEXACT 1
+#define FP_UNDERFLOW 2
+#define FP_OVERFLOW 4
+#define FP_DIV0 8
+#define FP_INVALID 16
+#define FP_UNIMPLEMENTED 32
+
+#endif
+#if defined(MIPS_USES_R4K_TLB)
+ tlb_t tlb[MIPS_TLB_NB];
+#endif
+ uint32_t CP0_index;
+ uint32_t CP0_random;
+ uint32_t CP0_EntryLo0;
+ uint32_t CP0_EntryLo1;
+ uint32_t CP0_Context;
+ uint32_t CP0_PageMask;
+ uint32_t CP0_Wired;
+ uint32_t CP0_BadVAddr;
+ uint32_t CP0_Count;
+ uint32_t CP0_EntryHi;
+ uint32_t CP0_Compare;
+ uint32_t CP0_Status;
+#define CP0St_CU3 31
+#define CP0St_CU2 30
+#define CP0St_CU1 29
+#define CP0St_CU0 28
+#define CP0St_RP 27
+#define CP0St_FR 26
+#define CP0St_RE 25
+#define CP0St_BEV 22
+#define CP0St_TS 21
+#define CP0St_SR 20
+#define CP0St_NMI 19
+#define CP0St_IM 8
+#define CP0St_UM 4
+#define CP0St_ERL 2
+#define CP0St_EXL 1
+#define CP0St_IE 0
+ uint32_t CP0_Cause;
+#define CP0Ca_IV 23
+ uint32_t CP0_EPC;
+ uint32_t CP0_PRid;
+ uint32_t CP0_Config0;
+#define CP0C0_M 31
+#define CP0C0_K23 28
+#define CP0C0_KU 25
+#define CP0C0_MDU 20
+#define CP0C0_MM 17
+#define CP0C0_BM 16
+#define CP0C0_BE 15
+#define CP0C0_AT 13
+#define CP0C0_AR 10
+#define CP0C0_MT 7
+#define CP0C0_K0 0
+ uint32_t CP0_Config1;
+#define CP0C1_MMU 25
+#define CP0C1_IS 22
+#define CP0C1_IL 19
+#define CP0C1_IA 16
+#define CP0C1_DS 13
+#define CP0C1_DL 10
+#define CP0C1_DA 7
+#define CP0C1_PC 4
+#define CP0C1_WR 3
+#define CP0C1_CA 2
+#define CP0C1_EP 1
+#define CP0C1_FP 0
+ uint32_t CP0_LLAddr;
+ uint32_t CP0_WatchLo;
+ uint32_t CP0_WatchHi;
+ uint32_t CP0_Debug;
+#define CPDB_DBD 31
+#define CP0DB_DM 30
+#define CP0DB_LSNM 28
+#define CP0DB_Doze 27
+#define CP0DB_Halt 26
+#define CP0DB_CNT 25
+#define CP0DB_IBEP 24
+#define CP0DB_DBEP 21
+#define CP0DB_IEXI 20
+#define CP0DB_VER 15
+#define CP0DB_DEC 10
+#define CP0DB_SSt 8
+#define CP0DB_DINT 5
+#define CP0DB_DIB 4
+#define CP0DB_DDBS 3
+#define CP0DB_DDBL 2
+#define CP0DB_DBp 1
+#define CP0DB_DSS 0
+ uint32_t CP0_DEPC;
+ uint32_t CP0_TagLo;
+ uint32_t CP0_DataLo;
+ uint32_t CP0_ErrorEPC;
+ uint32_t CP0_DESAVE;
+ /* Qemu */
+ struct QEMUTimer *timer; /* Internal timer */
+ int interrupt_request;
+ jmp_buf jmp_env;
+ int exception_index;
+ int error_code;
+ int user_mode_only; /* user mode only simulation */
+ uint32_t hflags; /* CPU State */
+ /* TMASK defines different execution modes */
+#define MIPS_HFLAG_TMASK 0x007F
+#define MIPS_HFLAG_MODE 0x001F /* execution modes */
+#define MIPS_HFLAG_UM 0x0001 /* user mode */
+#define MIPS_HFLAG_ERL 0x0002 /* Error mode */
+#define MIPS_HFLAG_EXL 0x0004 /* Exception mode */
+#define MIPS_HFLAG_DM 0x0008 /* Debug mode */
+#define MIPS_HFLAG_SM 0x0010 /* Supervisor mode */
+#define MIPS_HFLAG_RE 0x0040 /* Reversed endianness */
+ /* If translation is interrupted between the branch instruction and
+ * the delay slot, record what type of branch it is so that we can
+ * resume translation properly. It might be possible to reduce
+ * this from three bits to two. */
+#define MIPS_HFLAG_BMASK 0x0380
+#define MIPS_HFLAG_B 0x0080 /* Unconditional branch */
+#define MIPS_HFLAG_BC 0x0100 /* Conditional branch */
+#define MIPS_HFLAG_BL 0x0180 /* Likely branch */
+#define MIPS_HFLAG_BR 0x0200 /* branch to register (can't link TB) */
+ target_ulong btarget; /* Jump / branch target */
+ int bcond; /* Branch condition (if needed) */
+
+ int halted; /* TRUE if the CPU is in suspend state */
+
+ CPU_COMMON
+};
+
+#include "cpu-all.h"
+
+/* Memory access type :
+ * may be needed for precise access rights control and precise exceptions.
+ */
+enum {
+ /* 1 bit to define user level / supervisor access */
+ ACCESS_USER = 0x00,
+ ACCESS_SUPER = 0x01,
+ /* 1 bit to indicate direction */
+ ACCESS_STORE = 0x02,
+ /* Type of instruction that generated the access */
+ ACCESS_CODE = 0x10, /* Code fetch access */
+ ACCESS_INT = 0x20, /* Integer load/store access */
+ ACCESS_FLOAT = 0x30, /* floating point load/store access */
+};
+
+/* Exceptions */
+enum {
+ EXCP_NONE = -1,
+ EXCP_RESET = 0,
+ EXCP_SRESET,
+ EXCP_DSS,
+ EXCP_DINT,
+ EXCP_NMI,
+ EXCP_MCHECK,
+ EXCP_EXT_INTERRUPT,
+ EXCP_DFWATCH,
+ EXCP_DIB, /* 8 */
+ EXCP_IWATCH,
+ EXCP_AdEL,
+ EXCP_AdES,
+ EXCP_TLBF,
+ EXCP_IBE,
+ EXCP_DBp,
+ EXCP_SYSCALL,
+ EXCP_BREAK, /* 16 */
+ EXCP_CpU,
+ EXCP_RI,
+ EXCP_OVERFLOW,
+ EXCP_TRAP,
+ EXCP_DDBS,
+ EXCP_DWATCH,
+ EXCP_LAE,
+ EXCP_SAE, /* 24 */
+ EXCP_LTLBL,
+ EXCP_TLBL,
+ EXCP_TLBS,
+ EXCP_DBE,
+ EXCP_DDBL,
+ EXCP_MTCP0 = 0x104, /* mtmsr instruction: */
+ /* may change privilege level */
+ EXCP_BRANCH = 0x108, /* branch instruction */
+ EXCP_ERET = 0x10C, /* return from interrupt */
+ EXCP_SYSCALL_USER = 0x110, /* System call in user mode only */
+ EXCP_FLUSH = 0x109,
+};
+
+int cpu_mips_exec(CPUMIPSState *s);
+CPUMIPSState *cpu_mips_init(void);
+uint32_t cpu_mips_get_clock (void);
+
+#endif /* !defined (__MIPS_CPU_H__) */
diff --git a/target-mips/exec.h b/target-mips/exec.h
new file mode 100644
index 0000000..93014d6
--- /dev/null
+++ b/target-mips/exec.h
@@ -0,0 +1,119 @@
+#if !defined(__QEMU_MIPS_EXEC_H__)
+#define __QEMU_MIPS_EXEC_H__
+
+//#define DEBUG_OP
+
+#include "mips-defs.h"
+#include "dyngen-exec.h"
+
+register struct CPUMIPSState *env asm(AREG0);
+
+#if defined (USE_64BITS_REGS)
+typedef int64_t host_int_t;
+typedef uint64_t host_uint_t;
+#else
+typedef int32_t host_int_t;
+typedef uint32_t host_uint_t;
+#endif
+
+register host_uint_t T0 asm(AREG1);
+register host_uint_t T1 asm(AREG2);
+register host_uint_t T2 asm(AREG3);
+
+#if defined (USE_HOST_FLOAT_REGS)
+#error "implement me."
+#else
+#define FDT0 (env->ft0.fd)
+#define FDT1 (env->ft1.fd)
+#define FDT2 (env->ft2.fd)
+#define FST0 (env->ft0.fs[FP_ENDIAN_IDX])
+#define FST1 (env->ft1.fs[FP_ENDIAN_IDX])
+#define FST2 (env->ft2.fs[FP_ENDIAN_IDX])
+#define DT0 (env->ft0.d)
+#define DT1 (env->ft1.d)
+#define DT2 (env->ft2.d)
+#define WT0 (env->ft0.w[FP_ENDIAN_IDX])
+#define WT1 (env->ft1.w[FP_ENDIAN_IDX])
+#define WT2 (env->ft2.w[FP_ENDIAN_IDX])
+#endif
+
+#if defined (DEBUG_OP)
+#define RETURN() __asm__ __volatile__("nop");
+#else
+#define RETURN() __asm__ __volatile__("");
+#endif
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+#if (HOST_LONG_BITS == 32)
+void do_mult (void);
+void do_multu (void);
+void do_madd (void);
+void do_maddu (void);
+void do_msub (void);
+void do_msubu (void);
+#endif
+void do_mfc0(int reg, int sel);
+void do_mtc0(int reg, int sel);
+void do_tlbwi (void);
+void do_tlbwr (void);
+void do_tlbp (void);
+void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+void dump_fpu(CPUState *env);
+void fpu_dump_state(CPUState *env, FILE *f,
+ int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags);
+#endif
+void dump_sc (void);
+void do_lwl_raw (uint32_t);
+void do_lwr_raw (uint32_t);
+uint32_t do_swl_raw (uint32_t);
+uint32_t do_swr_raw (uint32_t);
+#if !defined(CONFIG_USER_ONLY)
+void do_lwl_user (uint32_t);
+void do_lwl_kernel (uint32_t);
+void do_lwr_user (uint32_t);
+void do_lwr_kernel (uint32_t);
+uint32_t do_swl_user (uint32_t);
+uint32_t do_swl_kernel (uint32_t);
+uint32_t do_swr_user (uint32_t);
+uint32_t do_swr_kernel (uint32_t);
+#endif
+void do_pmon (int function);
+
+void dump_sc (void);
+
+int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu);
+void do_interrupt (CPUState *env);
+
+void cpu_loop_exit(void);
+void do_raise_exception_err (uint32_t exception, int error_code);
+void do_raise_exception (uint32_t exception);
+void do_raise_exception_direct (uint32_t exception);
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags);
+void cpu_mips_irqctrl_init (void);
+uint32_t cpu_mips_get_random (CPUState *env);
+uint32_t cpu_mips_get_count (CPUState *env);
+void cpu_mips_store_count (CPUState *env, uint32_t value);
+void cpu_mips_store_compare (CPUState *env, uint32_t value);
+void cpu_mips_clock_init (CPUState *env);
+
+#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
diff --git a/target-mips/fop_template.c b/target-mips/fop_template.c
new file mode 100644
index 0000000..bc0a6e0
--- /dev/null
+++ b/target-mips/fop_template.c
@@ -0,0 +1,99 @@
+/*
+ * MIPS emulation micro-operations templates for floating point reg
+ * load & store for qemu.
+ *
+ * Copyright (c) 2006 Marius Groeger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(SFREG)
+
+#define OP_WLOAD_FREG(treg, tregname, SFREG) \
+ void glue(glue(op_load_fpr_,tregname), SFREG) (void) \
+ { \
+ treg = FPR_W(env, SFREG); \
+ RETURN(); \
+ }
+
+#define OP_WSTORE_FREG(treg, tregname, SFREG) \
+ void glue(glue(op_store_fpr_,tregname), SFREG) (void)\
+ { \
+ FPR_W(env, SFREG) = treg; \
+ RETURN(); \
+ }
+
+/* WT0 = SFREG.w: op_load_fpr_WT0_fprSFREG */
+OP_WLOAD_FREG(WT0, WT0_fpr, SFREG)
+/* SFREG.w = WT0: op_store_fpr_WT0_fprSFREG */
+OP_WSTORE_FREG(WT0, WT0_fpr, SFREG)
+
+OP_WLOAD_FREG(WT1, WT1_fpr, SFREG)
+OP_WSTORE_FREG(WT1, WT1_fpr, SFREG)
+
+OP_WLOAD_FREG(WT2, WT2_fpr, SFREG)
+OP_WSTORE_FREG(WT2, WT2_fpr, SFREG)
+
+#endif
+
+#if defined(DFREG)
+
+#define OP_DLOAD_FREG(treg, tregname, DFREG) \
+ void glue(glue(op_load_fpr_,tregname), DFREG) (void) \
+ { \
+ treg = FPR_D(env, DFREG); \
+ RETURN(); \
+ }
+
+#define OP_DSTORE_FREG(treg, tregname, DFREG) \
+ void glue(glue(op_store_fpr_,tregname), DFREG) (void)\
+ { \
+ FPR_D(env, DFREG) = treg; \
+ RETURN(); \
+ }
+
+OP_DLOAD_FREG(DT0, DT0_fpr, DFREG)
+OP_DSTORE_FREG(DT0, DT0_fpr, DFREG)
+
+OP_DLOAD_FREG(DT1, DT1_fpr, DFREG)
+OP_DSTORE_FREG(DT1, DT1_fpr, DFREG)
+
+OP_DLOAD_FREG(DT2, DT2_fpr, DFREG)
+OP_DSTORE_FREG(DT2, DT2_fpr, DFREG)
+
+#endif
+
+#if defined (FTN)
+
+#define SET_RESET(treg, tregname) \
+ void glue(op_set, tregname)(void) \
+ { \
+ treg = PARAM1; \
+ RETURN(); \
+ } \
+ void glue(op_reset, tregname)(void) \
+ { \
+ treg = 0; \
+ RETURN(); \
+ } \
+
+SET_RESET(WT0, _WT0)
+SET_RESET(WT1, _WT1)
+SET_RESET(WT2, _WT2)
+SET_RESET(DT0, _DT0)
+SET_RESET(DT1, _DT1)
+SET_RESET(DT2, _DT2)
+
+#endif
diff --git a/target-mips/helper.c b/target-mips/helper.c
new file mode 100644
index 0000000..9c8e4f6
--- /dev/null
+++ b/target-mips/helper.c
@@ -0,0 +1,432 @@
+/*
+ * MIPS emulation helpers for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+enum {
+ TLBRET_DIRTY = -4,
+ TLBRET_INVALID = -3,
+ TLBRET_NOMATCH = -2,
+ TLBRET_BADADDR = -1,
+ TLBRET_MATCH = 0
+};
+
+/* MIPS32 4K MMU emulation */
+#ifdef MIPS_USES_R4K_TLB
+static int map_address (CPUState *env, target_ulong *physical, int *prot,
+ target_ulong address, int rw, int access_type)
+{
+ target_ulong tag = address & (TARGET_PAGE_MASK << 1);
+ uint8_t ASID = env->CP0_EntryHi & 0xFF;
+ tlb_t *tlb;
+ int i, n;
+
+ for (i = 0; i < MIPS_TLB_NB; i++) {
+ tlb = &env->tlb[i];
+ /* Check ASID, virtual page number & size */
+ if ((tlb->G == 1 || tlb->ASID == ASID) &&
+ tlb->VPN == tag && address < tlb->end2) {
+ /* TLB match */
+ n = (address >> TARGET_PAGE_BITS) & 1;
+ /* Check access rights */
+ if (!(n ? tlb->V1 : tlb->V0))
+ return TLBRET_INVALID;
+ if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
+ *physical = tlb->PFN[n] | (address & ~TARGET_PAGE_MASK);
+ *prot = PAGE_READ;
+ if (n ? tlb->D1 : tlb->D0)
+ *prot |= PAGE_WRITE;
+ return TLBRET_MATCH;
+ }
+ return TLBRET_DIRTY;
+ }
+ }
+ return TLBRET_NOMATCH;
+}
+#endif
+
+static int get_physical_address (CPUState *env, target_ulong *physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ /* User mode can only access useg */
+ int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
+ int ret = TLBRET_MATCH;
+
+#if 0
+ if (logfile) {
+ fprintf(logfile, "user mode %d h %08x\n",
+ user_mode, env->hflags);
+ }
+#endif
+ if (user_mode && address > 0x7FFFFFFFUL)
+ return TLBRET_BADADDR;
+ if (address < 0x80000000UL) {
+ if (!(env->hflags & MIPS_HFLAG_ERL)) {
+#ifdef MIPS_USES_R4K_TLB
+ ret = map_address(env, physical, prot, address, rw, access_type);
+#else
+ *physical = address + 0x40000000UL;
+ *prot = PAGE_READ | PAGE_WRITE;
+#endif
+ } else {
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+ }
+ } else if (address < 0xA0000000UL) {
+ /* kseg0 */
+ /* XXX: check supervisor mode */
+ *physical = address - 0x80000000UL;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else if (address < 0xC0000000UL) {
+ /* kseg1 */
+ /* XXX: check supervisor mode */
+ *physical = address - 0xA0000000UL;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else if (address < 0xE0000000UL) {
+ /* kseg2 */
+#ifdef MIPS_USES_R4K_TLB
+ ret = map_address(env, physical, prot, address, rw, access_type);
+#else
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+#endif
+ } else {
+ /* kseg3 */
+ /* XXX: check supervisor mode */
+ /* XXX: debug segment is not emulated */
+#ifdef MIPS_USES_R4K_TLB
+ ret = map_address(env, physical, prot, address, rw, access_type);
+#else
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+#endif
+ }
+#if 0
+ if (logfile) {
+ fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw,
+ access_type, *physical, *prot, ret);
+ }
+#endif
+
+ return ret;
+}
+
+#if defined(CONFIG_USER_ONLY)
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+#else
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ target_ulong phys_addr;
+ int prot;
+
+ if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
+ return -1;
+ return phys_addr;
+}
+
+void cpu_mips_init_mmu (CPUState *env)
+{
+}
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ target_ulong physical;
+ int prot;
+ int exception = 0, error_code = 0;
+ int access_type;
+ int ret = 0;
+
+ if (logfile) {
+#if 0
+ cpu_dump_state(env, logfile, fprintf, 0);
+#endif
+ fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
+ __func__, env->PC, address, rw, is_user, is_softmmu);
+ }
+
+ rw &= 1;
+
+ /* data access */
+ /* XXX: put correct access by using cpu_restore_state()
+ correctly */
+ access_type = ACCESS_INT;
+ if (env->user_mode_only) {
+ /* user mode only emulation */
+ ret = TLBRET_NOMATCH;
+ goto do_fault;
+ }
+ ret = get_physical_address(env, &physical, &prot,
+ address, rw, access_type);
+ if (logfile) {
+ fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n",
+ __func__, address, ret, physical, prot);
+ }
+ if (ret == TLBRET_MATCH) {
+ ret = tlb_set_page(env, address & TARGET_PAGE_MASK,
+ physical & TARGET_PAGE_MASK, prot,
+ is_user, is_softmmu);
+ } else if (ret < 0) {
+ do_fault:
+ switch (ret) {
+ default:
+ case TLBRET_BADADDR:
+ /* Reference to kernel address from user mode or supervisor mode */
+ /* Reference to supervisor address from user mode */
+ if (rw)
+ exception = EXCP_AdES;
+ else
+ exception = EXCP_AdEL;
+ break;
+ case TLBRET_NOMATCH:
+ /* No TLB match for a mapped address */
+ if (rw)
+ exception = EXCP_TLBS;
+ else
+ exception = EXCP_TLBL;
+ error_code = 1;
+ break;
+ case TLBRET_INVALID:
+ /* TLB match with no valid bit */
+ if (rw)
+ exception = EXCP_TLBS;
+ else
+ exception = EXCP_TLBL;
+ break;
+ case TLBRET_DIRTY:
+ /* TLB match but 'D' bit is cleared */
+ exception = EXCP_LTLBL;
+ break;
+
+ }
+ /* Raise exception */
+ env->CP0_BadVAddr = address;
+ env->CP0_Context = (env->CP0_Context & 0xff800000) |
+ ((address >> 9) & 0x007ffff0);
+ env->CP0_EntryHi =
+ (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
+ env->exception_index = exception;
+ env->error_code = error_code;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+void do_interrupt (CPUState *env)
+{
+ target_ulong pc, offset;
+ int cause = -1;
+
+ if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
+ fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
+ __func__, env->PC, env->CP0_EPC, cause, env->exception_index);
+ }
+ if (env->exception_index == EXCP_EXT_INTERRUPT &&
+ (env->hflags & MIPS_HFLAG_DM))
+ env->exception_index = EXCP_DINT;
+ offset = 0x180;
+ switch (env->exception_index) {
+ case EXCP_DSS:
+ env->CP0_Debug |= 1 << CP0DB_DSS;
+ /* Debug single step cannot be raised inside a delay slot and
+ * resume will always occur on the next instruction
+ * (but we assume the pc has always been updated during
+ * code translation).
+ */
+ env->CP0_DEPC = env->PC;
+ goto enter_debug_mode;
+ case EXCP_DINT:
+ env->CP0_Debug |= 1 << CP0DB_DINT;
+ goto set_DEPC;
+ case EXCP_DIB:
+ env->CP0_Debug |= 1 << CP0DB_DIB;
+ goto set_DEPC;
+ case EXCP_DBp:
+ env->CP0_Debug |= 1 << CP0DB_DBp;
+ goto set_DEPC;
+ case EXCP_DDBS:
+ env->CP0_Debug |= 1 << CP0DB_DDBS;
+ goto set_DEPC;
+ case EXCP_DDBL:
+ env->CP0_Debug |= 1 << CP0DB_DDBL;
+ goto set_DEPC;
+ set_DEPC:
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /* If the exception was raised from a delay slot,
+ * come back to the jump
+ */
+ env->CP0_DEPC = env->PC - 4;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ } else {
+ env->CP0_DEPC = env->PC;
+ }
+ enter_debug_mode:
+ env->hflags |= MIPS_HFLAG_DM;
+ /* EJTAG probe trap enable is not implemented... */
+ pc = 0xBFC00480;
+ break;
+ case EXCP_RESET:
+#ifdef MIPS_USES_R4K_TLB
+ env->CP0_random = MIPS_TLB_NB - 1;
+#endif
+ env->CP0_Wired = 0;
+ env->CP0_Config0 = MIPS_CONFIG0;
+#if defined (MIPS_CONFIG1)
+ env->CP0_Config1 = MIPS_CONFIG1;
+#endif
+#if defined (MIPS_CONFIG2)
+ env->CP0_Config2 = MIPS_CONFIG2;
+#endif
+#if defined (MIPS_CONFIG3)
+ env->CP0_Config3 = MIPS_CONFIG3;
+#endif
+ env->CP0_WatchLo = 0;
+ env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV);
+ goto set_error_EPC;
+ case EXCP_SRESET:
+ env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
+ (1 << CP0St_SR);
+ env->CP0_WatchLo = 0;
+ goto set_error_EPC;
+ case EXCP_NMI:
+ env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
+ (1 << CP0St_NMI);
+ set_error_EPC:
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /* If the exception was raised from a delay slot,
+ * come back to the jump
+ */
+ env->CP0_ErrorEPC = env->PC - 4;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ } else {
+ env->CP0_ErrorEPC = env->PC;
+ }
+ env->hflags |= MIPS_HFLAG_ERL;
+ env->CP0_Status |= (1 << CP0St_ERL);
+ pc = 0xBFC00000;
+ break;
+ case EXCP_MCHECK:
+ cause = 24;
+ goto set_EPC;
+ case EXCP_EXT_INTERRUPT:
+ cause = 0;
+ if (env->CP0_Cause & (1 << CP0Ca_IV))
+ offset = 0x200;
+ goto set_EPC;
+ case EXCP_DWATCH:
+ cause = 23;
+ /* XXX: TODO: manage defered watch exceptions */
+ goto set_EPC;
+ case EXCP_AdEL:
+ case EXCP_AdES:
+ cause = 4;
+ goto set_EPC;
+ case EXCP_TLBL:
+ cause = 2;
+ if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
+ offset = 0x000;
+ goto set_EPC;
+ case EXCP_IBE:
+ cause = 6;
+ goto set_EPC;
+ case EXCP_DBE:
+ cause = 7;
+ goto set_EPC;
+ case EXCP_SYSCALL:
+ cause = 8;
+ goto set_EPC;
+ case EXCP_BREAK:
+ cause = 9;
+ goto set_EPC;
+ case EXCP_RI:
+ cause = 10;
+ goto set_EPC;
+ case EXCP_CpU:
+ cause = 11;
+ env->CP0_Cause = (env->CP0_Cause & ~0x03000000) | (env->error_code << 28);
+ goto set_EPC;
+ case EXCP_OVERFLOW:
+ cause = 12;
+ goto set_EPC;
+ case EXCP_TRAP:
+ cause = 13;
+ goto set_EPC;
+ case EXCP_LTLBL:
+ cause = 1;
+ goto set_EPC;
+ case EXCP_TLBS:
+ cause = 3;
+ if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
+ offset = 0x000;
+ goto set_EPC;
+ set_EPC:
+ if (env->CP0_Status & (1 << CP0St_BEV)) {
+ pc = 0xBFC00200;
+ } else {
+ pc = 0x80000000;
+ }
+ env->hflags |= MIPS_HFLAG_EXL;
+ env->CP0_Status |= (1 << CP0St_EXL);
+ pc += offset;
+ env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2);
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /* If the exception was raised from a delay slot,
+ * come back to the jump
+ */
+ env->CP0_EPC = env->PC - 4;
+ env->CP0_Cause |= 0x80000000;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ } else {
+ env->CP0_EPC = env->PC;
+ env->CP0_Cause &= ~0x80000000;
+ }
+ break;
+ default:
+ if (logfile) {
+ fprintf(logfile, "Invalid MIPS exception %d. Exiting\n",
+ env->exception_index);
+ }
+ printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
+ exit(1);
+ }
+ env->PC = pc;
+ if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
+ fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n"
+ " S %08x C %08x A %08x D %08x\n",
+ __func__, env->PC, env->CP0_EPC, cause, env->exception_index,
+ env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
+ env->CP0_DEPC);
+ }
+ env->exception_index = EXCP_NONE;
+}
diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
new file mode 100644
index 0000000..c6f9e9c
--- /dev/null
+++ b/target-mips/mips-defs.h
@@ -0,0 +1,67 @@
+#if !defined (__QEMU_MIPS_DEFS_H__)
+#define __QEMU_MIPS_DEFS_H__
+
+/* If we want to use 64 bits host regs... */
+//#define USE_64BITS_REGS
+/* If we want to use host float regs... */
+//#define USE_HOST_FLOAT_REGS
+
+#define MIPS_R4Kc 0x00018000
+#define MIPS_R4Kp 0x00018300
+
+/* Emulate MIPS R4Kc for now */
+#define MIPS_CPU MIPS_R4Kc
+
+#if (MIPS_CPU == MIPS_R4Kc)
+/* 32 bits target */
+#define TARGET_LONG_BITS 32
+/* real pages are variable size... */
+#define TARGET_PAGE_BITS 12
+/* Uses MIPS R4Kx enhancements to MIPS32 architecture */
+#define MIPS_USES_R4K_EXT
+/* Uses MIPS R4Kc TLB model */
+#define MIPS_USES_R4K_TLB
+#define MIPS_TLB_NB 16
+/* basic FPU register support */
+#define MIPS_USES_FPU 1
+/* Define a implementation number of 1.
+ * Define a major version 1, minor version 0.
+ */
+#define MIPS_FCR0 ((0 << 16) | (1 << 8) | (1 << 4) | 0)
+/* Have config1, uses TLB */
+#define MIPS_CONFIG0_1 \
+((1 << CP0C0_M) | (0 << CP0C0_K23) | (0 << CP0C0_KU) | \
+ (1 << CP0C0_MT) | (2 << CP0C0_K0))
+#ifdef TARGET_WORDS_BIGENDIAN
+#define MIPS_CONFIG0 (MIPS_CONFIG0_1 | (1 << CP0C0_BE))
+#else
+#define MIPS_CONFIG0 MIPS_CONFIG0_1
+#endif
+/* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache,
+ * 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache,
+ * no performance counters, watch registers present, no code compression,
+ * EJTAG present, FPU enable bit depending on MIPS_USES_FPU
+ */
+#define MIPS_CONFIG1 \
+((15 << CP0C1_MMU) | \
+ (0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \
+ (0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \
+ (0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \
+ (1 << CP0C1_EP) | (MIPS_USES_FPU << CP0C1_FP))
+#elif (MIPS_CPU == MIPS_R4Kp)
+/* 32 bits target */
+#define TARGET_LONG_BITS 32
+/* real pages are variable size... */
+#define TARGET_PAGE_BITS 12
+/* Uses MIPS R4Kx enhancements to MIPS32 architecture */
+#define MIPS_USES_R4K_EXT
+/* Uses MIPS R4Km FPM MMU model */
+#define MIPS_USES_R4K_FPM
+#else
+#error "MIPS CPU not defined"
+/* Remainder for other flags */
+//#define TARGET_MIPS64
+//#define MIPS_USES_FPU
+#endif
+
+#endif /* !defined (__QEMU_MIPS_DEFS_H__) */
diff --git a/target-mips/op.c b/target-mips/op.c
new file mode 100644
index 0000000..4575517
--- /dev/null
+++ b/target-mips/op.c
@@ -0,0 +1,1155 @@
+/*
+ * MIPS emulation micro-operations for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "exec.h"
+
+#ifndef CALL_FROM_TB0
+#define CALL_FROM_TB0(func) func();
+#endif
+#ifndef CALL_FROM_TB1
+#define CALL_FROM_TB1(func, arg0) func(arg0);
+#endif
+#ifndef CALL_FROM_TB1_CONST16
+#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0);
+#endif
+#ifndef CALL_FROM_TB2
+#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1);
+#endif
+#ifndef CALL_FROM_TB2_CONST16
+#define CALL_FROM_TB2_CONST16(func, arg0, arg1) \
+CALL_FROM_TB2(func, arg0, arg1);
+#endif
+#ifndef CALL_FROM_TB3
+#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2);
+#endif
+#ifndef CALL_FROM_TB4
+#define CALL_FROM_TB4(func, arg0, arg1, arg2, arg3) \
+ func(arg0, arg1, arg2, arg3);
+#endif
+
+#define REG 1
+#include "op_template.c"
+#undef REG
+#define REG 2
+#include "op_template.c"
+#undef REG
+#define REG 3
+#include "op_template.c"
+#undef REG
+#define REG 4
+#include "op_template.c"
+#undef REG
+#define REG 5
+#include "op_template.c"
+#undef REG
+#define REG 6
+#include "op_template.c"
+#undef REG
+#define REG 7
+#include "op_template.c"
+#undef REG
+#define REG 8
+#include "op_template.c"
+#undef REG
+#define REG 9
+#include "op_template.c"
+#undef REG
+#define REG 10
+#include "op_template.c"
+#undef REG
+#define REG 11
+#include "op_template.c"
+#undef REG
+#define REG 12
+#include "op_template.c"
+#undef REG
+#define REG 13
+#include "op_template.c"
+#undef REG
+#define REG 14
+#include "op_template.c"
+#undef REG
+#define REG 15
+#include "op_template.c"
+#undef REG
+#define REG 16
+#include "op_template.c"
+#undef REG
+#define REG 17
+#include "op_template.c"
+#undef REG
+#define REG 18
+#include "op_template.c"
+#undef REG
+#define REG 19
+#include "op_template.c"
+#undef REG
+#define REG 20
+#include "op_template.c"
+#undef REG
+#define REG 21
+#include "op_template.c"
+#undef REG
+#define REG 22
+#include "op_template.c"
+#undef REG
+#define REG 23
+#include "op_template.c"
+#undef REG
+#define REG 24
+#include "op_template.c"
+#undef REG
+#define REG 25
+#include "op_template.c"
+#undef REG
+#define REG 26
+#include "op_template.c"
+#undef REG
+#define REG 27
+#include "op_template.c"
+#undef REG
+#define REG 28
+#include "op_template.c"
+#undef REG
+#define REG 29
+#include "op_template.c"
+#undef REG
+#define REG 30
+#include "op_template.c"
+#undef REG
+#define REG 31
+#include "op_template.c"
+#undef REG
+
+#define TN T0
+#include "op_template.c"
+#undef TN
+#define TN T1
+#include "op_template.c"
+#undef TN
+#define TN T2
+#include "op_template.c"
+#undef TN
+
+#ifdef MIPS_USES_FPU
+
+#define SFREG 0
+#define DFREG 0
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 1
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 2
+#define DFREG 2
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 3
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 4
+#define DFREG 4
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 5
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 6
+#define DFREG 6
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 7
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 8
+#define DFREG 8
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 9
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 10
+#define DFREG 10
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 11
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 12
+#define DFREG 12
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 13
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 14
+#define DFREG 14
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 15
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 16
+#define DFREG 16
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 17
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 18
+#define DFREG 18
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 19
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 20
+#define DFREG 20
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 21
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 22
+#define DFREG 22
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 23
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 24
+#define DFREG 24
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 25
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 26
+#define DFREG 26
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 27
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 28
+#define DFREG 28
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 29
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 30
+#define DFREG 30
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 31
+#include "fop_template.c"
+#undef SFREG
+
+#define FTN
+#include "fop_template.c"
+#undef FTN
+
+#endif
+
+void op_dup_T0 (void)
+{
+ T2 = T0;
+ RETURN();
+}
+
+void op_load_HI (void)
+{
+ T0 = env->HI;
+ RETURN();
+}
+
+void op_store_HI (void)
+{
+ env->HI = T0;
+ RETURN();
+}
+
+void op_load_LO (void)
+{
+ T0 = env->LO;
+ RETURN();
+}
+
+void op_store_LO (void)
+{
+ env->LO = T0;
+ RETURN();
+}
+
+/* Load and store */
+#define MEMSUFFIX _raw
+#include "op_mem.c"
+#undef MEMSUFFIX
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.c"
+#undef MEMSUFFIX
+
+#define MEMSUFFIX _kernel
+#include "op_mem.c"
+#undef MEMSUFFIX
+#endif
+
+/* Arithmetic */
+void op_add (void)
+{
+ T0 += T1;
+ RETURN();
+}
+
+void op_addo (void)
+{
+ target_ulong tmp;
+
+ tmp = T0;
+ T0 += T1;
+ if (((tmp ^ T1 ^ (-1)) & (T0 ^ T1)) >> 31) {
+ /* operands of same sign, result different sign */
+ CALL_FROM_TB1(do_raise_exception_direct, EXCP_OVERFLOW);
+ }
+ RETURN();
+}
+
+void op_sub (void)
+{
+ T0 -= T1;
+ RETURN();
+}
+
+void op_subo (void)
+{
+ target_ulong tmp;
+
+ tmp = T0;
+ T0 = (int32_t)T0 - (int32_t)T1;
+ if (((tmp ^ T1) & (tmp ^ T0)) >> 31) {
+ /* operands of different sign, first operand and result different sign */
+ CALL_FROM_TB1(do_raise_exception_direct, EXCP_OVERFLOW);
+ }
+ RETURN();
+}
+
+void op_mul (void)
+{
+ T0 = (int32_t)T0 * (int32_t)T1;
+ RETURN();
+}
+
+void op_div (void)
+{
+ if (T1 != 0) {
+ env->LO = (int32_t)T0 / (int32_t)T1;
+ env->HI = (int32_t)T0 % (int32_t)T1;
+ }
+ RETURN();
+}
+
+void op_divu (void)
+{
+ if (T1 != 0) {
+ env->LO = T0 / T1;
+ env->HI = T0 % T1;
+ }
+ RETURN();
+}
+
+/* Logical */
+void op_and (void)
+{
+ T0 &= T1;
+ RETURN();
+}
+
+void op_nor (void)
+{
+ T0 = ~(T0 | T1);
+ RETURN();
+}
+
+void op_or (void)
+{
+ T0 |= T1;
+ RETURN();
+}
+
+void op_xor (void)
+{
+ T0 ^= T1;
+ RETURN();
+}
+
+void op_sll (void)
+{
+ T0 = T0 << T1;
+ RETURN();
+}
+
+void op_sra (void)
+{
+ T0 = (int32_t)T0 >> T1;
+ RETURN();
+}
+
+void op_srl (void)
+{
+ T0 = T0 >> T1;
+ RETURN();
+}
+
+void op_sllv (void)
+{
+ T0 = T1 << (T0 & 0x1F);
+ RETURN();
+}
+
+void op_srav (void)
+{
+ T0 = (int32_t)T1 >> (T0 & 0x1F);
+ RETURN();
+}
+
+void op_srlv (void)
+{
+ T0 = T1 >> (T0 & 0x1F);
+ RETURN();
+}
+
+void op_clo (void)
+{
+ int n;
+
+ if (T0 == (target_ulong)-1) {
+ T0 = 32;
+ } else {
+ for (n = 0; n < 32; n++) {
+ if (!(T0 & (1 << 31)))
+ break;
+ T0 = T0 << 1;
+ }
+ T0 = n;
+ }
+ RETURN();
+}
+
+void op_clz (void)
+{
+ int n;
+
+ if (T0 == 0) {
+ T0 = 32;
+ } else {
+ for (n = 0; n < 32; n++) {
+ if (T0 & (1 << 31))
+ break;
+ T0 = T0 << 1;
+ }
+ T0 = n;
+ }
+ RETURN();
+}
+
+/* 64 bits arithmetic */
+#if (HOST_LONG_BITS == 64)
+static inline uint64_t get_HILO (void)
+{
+ return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
+}
+
+static inline void set_HILO (uint64_t HILO)
+{
+ env->LO = HILO & 0xFFFFFFFF;
+ env->HI = HILO >> 32;
+}
+
+void op_mult (void)
+{
+ set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+ RETURN();
+}
+
+void op_multu (void)
+{
+ set_HILO((uint64_t)T0 * (uint64_t)T1);
+ RETURN();
+}
+
+void op_madd (void)
+{
+ int64_t tmp;
+
+ tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+ set_HILO((int64_t)get_HILO() + tmp);
+ RETURN();
+}
+
+void op_maddu (void)
+{
+ uint64_t tmp;
+
+ tmp = ((uint64_t)T0 * (uint64_t)T1);
+ set_HILO(get_HILO() + tmp);
+ RETURN();
+}
+
+void op_msub (void)
+{
+ int64_t tmp;
+
+ tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+ set_HILO((int64_t)get_HILO() - tmp);
+ RETURN();
+}
+
+void op_msubu (void)
+{
+ uint64_t tmp;
+
+ tmp = ((uint64_t)T0 * (uint64_t)T1);
+ set_HILO(get_HILO() - tmp);
+ RETURN();
+}
+#else
+void op_mult (void)
+{
+ CALL_FROM_TB0(do_mult);
+ RETURN();
+}
+
+void op_multu (void)
+{
+ CALL_FROM_TB0(do_multu);
+ RETURN();
+}
+
+void op_madd (void)
+{
+ CALL_FROM_TB0(do_madd);
+ RETURN();
+}
+
+void op_maddu (void)
+{
+ CALL_FROM_TB0(do_maddu);
+ RETURN();
+}
+
+void op_msub (void)
+{
+ CALL_FROM_TB0(do_msub);
+ RETURN();
+}
+
+void op_msubu (void)
+{
+ CALL_FROM_TB0(do_msubu);
+ RETURN();
+}
+#endif
+
+/* Conditional moves */
+void op_movn (void)
+{
+ if (T1 != 0)
+ env->gpr[PARAM1] = T0;
+ RETURN();
+}
+
+void op_movz (void)
+{
+ if (T1 == 0)
+ env->gpr[PARAM1] = T0;
+ RETURN();
+}
+
+/* Tests */
+#define OP_COND(name, cond) \
+void glue(op_, name) (void) \
+{ \
+ if (cond) { \
+ T0 = 1; \
+ } else { \
+ T0 = 0; \
+ } \
+ RETURN(); \
+}
+
+OP_COND(eq, T0 == T1);
+OP_COND(ne, T0 != T1);
+OP_COND(ge, (int32_t)T0 >= (int32_t)T1);
+OP_COND(geu, T0 >= T1);
+OP_COND(lt, (int32_t)T0 < (int32_t)T1);
+OP_COND(ltu, T0 < T1);
+OP_COND(gez, (int32_t)T0 >= 0);
+OP_COND(gtz, (int32_t)T0 > 0);
+OP_COND(lez, (int32_t)T0 <= 0);
+OP_COND(ltz, (int32_t)T0 < 0);
+
+/* Branchs */
+//#undef USE_DIRECT_JUMP
+
+void OPPROTO op_goto_tb0(void)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+void OPPROTO op_goto_tb1(void)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+/* Branch to register */
+void op_save_breg_target (void)
+{
+ env->btarget = T2;
+}
+
+void op_restore_breg_target (void)
+{
+ T2 = env->btarget;
+}
+
+void op_breg (void)
+{
+ env->PC = T2;
+ RETURN();
+}
+
+void op_save_btarget (void)
+{
+ env->btarget = PARAM1;
+ RETURN();
+}
+
+/* Conditional branch */
+void op_set_bcond (void)
+{
+ T2 = T0;
+ RETURN();
+}
+
+void op_save_bcond (void)
+{
+ env->bcond = T2;
+ RETURN();
+}
+
+void op_restore_bcond (void)
+{
+ T2 = env->bcond;
+ RETURN();
+}
+
+void op_jnz_T2 (void)
+{
+ if (T2)
+ GOTO_LABEL_PARAM(1);
+ RETURN();
+}
+
+/* CP0 functions */
+void op_mfc0 (void)
+{
+ CALL_FROM_TB2(do_mfc0, PARAM1, PARAM2);
+ RETURN();
+}
+
+void op_mtc0 (void)
+{
+ CALL_FROM_TB2(do_mtc0, PARAM1, PARAM2);
+ RETURN();
+}
+
+#ifdef MIPS_USES_FPU
+
+#if 0
+# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
+#else
+# define DEBUG_FPU_STATE() do { } while(0)
+#endif
+
+void op_cp1_enabled(void)
+{
+ if (!(env->CP0_Status & (1 << CP0St_CU1))) {
+ CALL_FROM_TB2(do_raise_exception_err, EXCP_CpU, 1);
+ }
+ RETURN();
+}
+
+/* CP1 functions */
+void op_cfc1 (void)
+{
+ if (T1 == 0) {
+ T0 = env->fcr0;
+ }
+ else {
+ /* fetch fcr31, masking unused bits */
+ T0 = env->fcr31 & 0x0183FFFF;
+ }
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* convert MIPS rounding mode in FCR31 to IEEE library */
+unsigned int ieee_rm[] = {
+ float_round_nearest_even,
+ float_round_to_zero,
+ float_round_up,
+ float_round_down
+};
+
+#define RESTORE_ROUNDING_MODE \
+ set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status)
+
+void op_ctc1 (void)
+{
+ if (T1 == 0) {
+ /* XXX should this throw an exception?
+ * don't write to FCR0.
+ * env->fcr0 = T0;
+ */
+ }
+ else {
+ /* store new fcr31, masking unused bits */
+ env->fcr31 = T0 & 0x0183FFFF;
+
+ /* set rounding mode */
+ RESTORE_ROUNDING_MODE;
+
+#ifndef CONFIG_SOFTFLOAT
+ /* no floating point exception for native float */
+ SET_FP_ENABLE(env->fcr31, 0);
+#endif
+ }
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mfc1 (void)
+{
+ T0 = WT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mtc1 (void)
+{
+ WT0 = T0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* Float support.
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
+
+FLOAT_OP(cvtd, w)
+{
+ FDT2 = int32_to_float64(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvts, w)
+{
+ FST2 = int32_to_float32(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvtw, s)
+{
+ WT2 = float32_to_int32(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvtw, d)
+{
+ WT2 = float64_to_int32(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(roundw, d)
+{
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+ WT2 = float64_round_to_int(FDT0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(roundw, s)
+{
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(truncw, d)
+{
+ WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(truncw, s)
+{
+ WT2 = float32_to_int32_round_to_zero(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(ceilw, d)
+{
+ set_float_rounding_mode(float_round_up, &env->fp_status);
+ WT2 = float64_round_to_int(FDT0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(ceilw, s)
+{
+ set_float_rounding_mode(float_round_up, &env->fp_status);
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(floorw, d)
+{
+ set_float_rounding_mode(float_round_down, &env->fp_status);
+ WT2 = float64_round_to_int(FDT0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(floorw, s)
+{
+ set_float_rounding_mode(float_round_down, &env->fp_status);
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* binary operations */
+#define FLOAT_BINOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_BINOP(add)
+FLOAT_BINOP(sub)
+FLOAT_BINOP(mul)
+FLOAT_BINOP(div)
+#undef FLOAT_BINOP
+
+/* unary operations, modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(sqrt)
+#undef FLOAT_UNOP
+
+/* unary operations, not modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(abs)
+FLOAT_UNOP(chs)
+#undef FLOAT_UNOP
+
+FLOAT_OP(mov, d)
+{
+ FDT2 = FDT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(mov, s)
+{
+ FST2 = FST0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+#ifdef CONFIG_SOFTFLOAT
+#define clear_invalid() do { \
+ int flags = get_float_exception_flags(&env->fp_status); \
+ flags &= ~float_flag_invalid; \
+ set_float_exception_flags(flags, &env->fp_status); \
+} while(0)
+#else
+#define clear_invalid() do { } while(0)
+#endif
+
+extern void dump_fpu_s(CPUState *env);
+
+#define FOP_COND(fmt, op, sig, cond) \
+void op_cmp_ ## fmt ## _ ## op (void) \
+{ \
+ if (cond) \
+ SET_FP_COND(env->fcr31); \
+ else \
+ CLEAR_FP_COND(env->fcr31); \
+ if (!sig) \
+ clear_invalid(); \
+ /*CALL_FROM_TB1(dump_fpu_s, env);*/ \
+ DEBUG_FPU_STATE(); \
+ RETURN(); \
+}
+
+flag float64_is_unordered(float64 a, float64 b STATUS_PARAM)
+{
+ extern flag float64_is_nan( float64 a );
+ if (float64_is_nan(a) || float64_is_nan(b)) {
+ float_raise(float_flag_invalid, status);
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+FOP_COND(d, f, 0, 0)
+FOP_COND(d, un, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status))
+FOP_COND(d, eq, 0, float64_eq(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ueq, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, olt, 0, float64_lt(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ult, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ole, 0, float64_le(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ule, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called
+ */
+FOP_COND(d, sf, 1, (float64_is_unordered(FDT0, FDT1, &env->fp_status), 0))
+FOP_COND(d, ngle,1, float64_is_unordered(FDT1, FDT0, &env->fp_status))
+FOP_COND(d, seq, 1, float64_eq(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ngl, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, lt, 1, float64_lt(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, nge, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, le, 1, float64_le(FDT0, FDT1, &env->fp_status))
+FOP_COND(d, ngt, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
+
+flag float32_is_unordered(float32 a, float32 b STATUS_PARAM)
+{
+ extern flag float32_is_nan( float32 a );
+ if (float32_is_nan(a) || float32_is_nan(b)) {
+ float_raise(float_flag_invalid, status);
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called
+ */
+FOP_COND(s, f, 0, 0)
+FOP_COND(s, un, 0, float32_is_unordered(FST1, FST0, &env->fp_status))
+FOP_COND(s, eq, 0, float32_eq(FST0, FST1, &env->fp_status))
+FOP_COND(s, ueq, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
+FOP_COND(s, olt, 0, float32_lt(FST0, FST1, &env->fp_status))
+FOP_COND(s, ult, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
+FOP_COND(s, ole, 0, float32_le(FST0, FST1, &env->fp_status))
+FOP_COND(s, ule, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called
+ */
+FOP_COND(s, sf, 1, (float32_is_unordered(FST0, FST1, &env->fp_status), 0))
+FOP_COND(s, ngle,1, float32_is_unordered(FST1, FST0, &env->fp_status))
+FOP_COND(s, seq, 1, float32_eq(FST0, FST1, &env->fp_status))
+FOP_COND(s, ngl, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
+FOP_COND(s, lt, 1, float32_lt(FST0, FST1, &env->fp_status))
+FOP_COND(s, nge, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
+FOP_COND(s, le, 1, float32_le(FST0, FST1, &env->fp_status))
+FOP_COND(s, ngt, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
+
+void op_bc1f (void)
+{
+ T0 = ! IS_FP_COND_SET(env->fcr31);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_bc1t (void)
+{
+ T0 = IS_FP_COND_SET(env->fcr31);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+#endif /* MIPS_USES_FPU */
+
+#if defined(MIPS_USES_R4K_TLB)
+void op_tlbwi (void)
+{
+ CALL_FROM_TB0(do_tlbwi);
+ RETURN();
+}
+
+void op_tlbwr (void)
+{
+ CALL_FROM_TB0(do_tlbwr);
+ RETURN();
+}
+
+void op_tlbp (void)
+{
+ CALL_FROM_TB0(do_tlbp);
+ RETURN();
+}
+
+void op_tlbr (void)
+{
+ CALL_FROM_TB0(do_tlbr);
+ RETURN();
+}
+#endif
+
+/* Specials */
+void op_pmon (void)
+{
+ CALL_FROM_TB1(do_pmon, PARAM1);
+}
+
+void op_trap (void)
+{
+ if (T0) {
+ CALL_FROM_TB1(do_raise_exception_direct, EXCP_TRAP);
+ }
+ RETURN();
+}
+
+void op_debug (void)
+{
+ CALL_FROM_TB1(do_raise_exception, EXCP_DEBUG);
+}
+
+void op_set_lladdr (void)
+{
+ env->CP0_LLAddr = T2;
+}
+
+void debug_eret (void);
+void op_eret (void)
+{
+ CALL_FROM_TB0(debug_eret);
+ if (env->hflags & MIPS_HFLAG_ERL) {
+ env->PC = env->CP0_ErrorEPC;
+ env->hflags &= ~MIPS_HFLAG_ERL;
+ env->CP0_Status &= ~(1 << CP0St_ERL);
+ } else {
+ env->PC = env->CP0_EPC;
+ env->hflags &= ~MIPS_HFLAG_EXL;
+ env->CP0_Status &= ~(1 << CP0St_EXL);
+ }
+ env->CP0_LLAddr = 1;
+}
+
+void op_deret (void)
+{
+ CALL_FROM_TB0(debug_eret);
+ env->PC = env->CP0_DEPC;
+}
+
+void op_save_state (void)
+{
+ env->hflags = PARAM1;
+ RETURN();
+}
+
+void op_save_pc (void)
+{
+ env->PC = PARAM1;
+ RETURN();
+}
+
+void op_raise_exception (void)
+{
+ CALL_FROM_TB1(do_raise_exception, PARAM1);
+ RETURN();
+}
+
+void op_raise_exception_err (void)
+{
+ CALL_FROM_TB2(do_raise_exception_err, PARAM1, PARAM2);
+ RETURN();
+}
+
+void op_exit_tb (void)
+{
+ EXIT_TB();
+}
+
+void op_wait (void)
+{
+ env->halted = 1;
+ CALL_FROM_TB1(do_raise_exception, EXCP_HLT);
+}
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
new file mode 100644
index 0000000..bf397c9
--- /dev/null
+++ b/target-mips/op_helper.c
@@ -0,0 +1,786 @@
+/*
+ * MIPS emulation helpers for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+#define MIPS_DEBUG_DISAS
+
+#define GETPC() (__builtin_return_address(0))
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+void cpu_loop_exit(void)
+{
+ longjmp(env->jmp_env, 1);
+}
+
+void do_raise_exception_err (uint32_t exception, int error_code)
+{
+#if 1
+ if (logfile && exception < 0x100)
+ fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+ T0 = 0;
+ cpu_loop_exit();
+}
+
+void do_raise_exception (uint32_t exception)
+{
+ do_raise_exception_err(exception, 0);
+}
+
+void do_restore_state (void *pc_ptr)
+{
+ TranslationBlock *tb;
+ unsigned long pc = (unsigned long) pc_ptr;
+
+ tb = tb_find_pc (pc);
+ cpu_restore_state (tb, env, pc, NULL);
+}
+
+void do_raise_exception_direct (uint32_t exception)
+{
+ do_restore_state (GETPC ());
+ do_raise_exception_err (exception, 0);
+}
+
+#define MEMSUFFIX _raw
+#include "op_helper_mem.c"
+#undef MEMSUFFIX
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_helper_mem.c"
+#undef MEMSUFFIX
+#define MEMSUFFIX _kernel
+#include "op_helper_mem.c"
+#undef MEMSUFFIX
+#endif
+
+/* 64 bits arithmetic for 32 bits hosts */
+#if (HOST_LONG_BITS == 32)
+static inline uint64_t get_HILO (void)
+{
+ return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
+}
+
+static inline void set_HILO (uint64_t HILO)
+{
+ env->LO = HILO & 0xFFFFFFFF;
+ env->HI = HILO >> 32;
+}
+
+void do_mult (void)
+{
+ set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+}
+
+void do_multu (void)
+{
+ set_HILO((uint64_t)T0 * (uint64_t)T1);
+}
+
+void do_madd (void)
+{
+ int64_t tmp;
+
+ tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+ set_HILO((int64_t)get_HILO() + tmp);
+}
+
+void do_maddu (void)
+{
+ uint64_t tmp;
+
+ tmp = ((uint64_t)T0 * (uint64_t)T1);
+ set_HILO(get_HILO() + tmp);
+}
+
+void do_msub (void)
+{
+ int64_t tmp;
+
+ tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
+ set_HILO((int64_t)get_HILO() - tmp);
+}
+
+void do_msubu (void)
+{
+ uint64_t tmp;
+
+ tmp = ((uint64_t)T0 * (uint64_t)T1);
+ set_HILO(get_HILO() - tmp);
+}
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+void do_mfc0 (int reg, int sel)
+{
+ cpu_abort(env, "mfc0 reg=%d sel=%d\n", reg, sel);
+}
+void do_mtc0 (int reg, int sel)
+{
+ cpu_abort(env, "mtc0 reg=%d sel=%d\n", reg, sel);
+}
+
+void do_tlbwi (void)
+{
+ cpu_abort(env, "tlbwi\n");
+}
+
+void do_tlbwr (void)
+{
+ cpu_abort(env, "tlbwr\n");
+}
+
+void do_tlbp (void)
+{
+ cpu_abort(env, "tlbp\n");
+}
+
+void do_tlbr (void)
+{
+ cpu_abort(env, "tlbr\n");
+}
+#else
+
+/* CP0 helpers */
+void do_mfc0 (int reg, int sel)
+{
+ const unsigned char *rn;
+
+ if (sel != 0 && reg != 16 && reg != 28) {
+ rn = "invalid";
+ goto print;
+ }
+ switch (reg) {
+ case 0:
+ T0 = env->CP0_index;
+ rn = "Index";
+ break;
+ case 1:
+ T0 = cpu_mips_get_random(env);
+ rn = "Random";
+ break;
+ case 2:
+ T0 = env->CP0_EntryLo0;
+ rn = "EntryLo0";
+ break;
+ case 3:
+ T0 = env->CP0_EntryLo1;
+ rn = "EntryLo1";
+ break;
+ case 4:
+ T0 = env->CP0_Context;
+ rn = "Context";
+ break;
+ case 5:
+ T0 = env->CP0_PageMask;
+ rn = "PageMask";
+ break;
+ case 6:
+ T0 = env->CP0_Wired;
+ rn = "Wired";
+ break;
+ case 8:
+ T0 = env->CP0_BadVAddr;
+ rn = "BadVaddr";
+ break;
+ case 9:
+ T0 = cpu_mips_get_count(env);
+ rn = "Count";
+ break;
+ case 10:
+ T0 = env->CP0_EntryHi;
+ rn = "EntryHi";
+ break;
+ case 11:
+ T0 = env->CP0_Compare;
+ rn = "Compare";
+ break;
+ case 12:
+ T0 = env->CP0_Status;
+ if (env->hflags & MIPS_HFLAG_UM)
+ T0 |= (1 << CP0St_UM);
+ rn = "Status";
+ break;
+ case 13:
+ T0 = env->CP0_Cause;
+ rn = "Cause";
+ break;
+ case 14:
+ T0 = env->CP0_EPC;
+ rn = "EPC";
+ break;
+ case 15:
+ T0 = env->CP0_PRid;
+ rn = "PRid";
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+ T0 = env->CP0_Config0;
+ rn = "Config";
+ break;
+ case 1:
+ T0 = env->CP0_Config1;
+ rn = "Config1";
+ break;
+ default:
+ rn = "Unknown config register";
+ break;
+ }
+ break;
+ case 17:
+ T0 = env->CP0_LLAddr >> 4;
+ rn = "LLAddr";
+ break;
+ case 18:
+ T0 = env->CP0_WatchLo;
+ rn = "WatchLo";
+ break;
+ case 19:
+ T0 = env->CP0_WatchHi;
+ rn = "WatchHi";
+ break;
+ case 23:
+ T0 = env->CP0_Debug;
+ if (env->hflags & MIPS_HFLAG_DM)
+ T0 |= 1 << CP0DB_DM;
+ rn = "Debug";
+ break;
+ case 24:
+ T0 = env->CP0_DEPC;
+ rn = "DEPC";
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ T0 = env->CP0_TagLo;
+ rn = "TagLo";
+ break;
+ case 1:
+ T0 = env->CP0_DataLo;
+ rn = "DataLo";
+ break;
+ default:
+ rn = "unknown sel";
+ break;
+ }
+ break;
+ case 30:
+ T0 = env->CP0_ErrorEPC;
+ rn = "ErrorEPC";
+ break;
+ case 31:
+ T0 = env->CP0_DESAVE;
+ rn = "DESAVE";
+ break;
+ default:
+ rn = "unknown";
+ break;
+ }
+ print:
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
+ env->PC, rn, T0, reg, sel);
+ }
+#endif
+ return;
+}
+
+void do_mtc0 (int reg, int sel)
+{
+ const unsigned char *rn;
+ uint32_t val, old, mask;
+
+ if (sel != 0 && reg != 16 && reg != 28) {
+ val = -1;
+ old = -1;
+ rn = "invalid";
+ goto print;
+ }
+ switch (reg) {
+ case 0:
+ val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
+ old = env->CP0_index;
+ env->CP0_index = val;
+ rn = "Index";
+ break;
+ case 2:
+ val = T0 & 0x3FFFFFFF;
+ old = env->CP0_EntryLo0;
+ env->CP0_EntryLo0 = val;
+ rn = "EntryLo0";
+ break;
+ case 3:
+ val = T0 & 0x3FFFFFFF;
+ old = env->CP0_EntryLo1;
+ env->CP0_EntryLo1 = val;
+ rn = "EntryLo1";
+ break;
+ case 4:
+ val = (env->CP0_Context & 0xFF800000) | (T0 & 0x007FFFF0);
+ old = env->CP0_Context;
+ env->CP0_Context = val;
+ rn = "Context";
+ break;
+ case 5:
+ val = T0 & 0x01FFE000;
+ old = env->CP0_PageMask;
+ env->CP0_PageMask = val;
+ rn = "PageMask";
+ break;
+ case 6:
+ val = T0 & 0x0000000F;
+ old = env->CP0_Wired;
+ env->CP0_Wired = val;
+ rn = "Wired";
+ break;
+ case 9:
+ val = T0;
+ old = cpu_mips_get_count(env);
+ cpu_mips_store_count(env, val);
+ rn = "Count";
+ break;
+ case 10:
+ val = T0 & 0xFFFFE0FF;
+ old = env->CP0_EntryHi;
+ env->CP0_EntryHi = val;
+ /* If the ASID changes, flush qemu's TLB. */
+ if ((old & 0xFF) != (val & 0xFF))
+ tlb_flush (env, 1);
+ rn = "EntryHi";
+ break;
+ case 11:
+ val = T0;
+ old = env->CP0_Compare;
+ cpu_mips_store_compare(env, val);
+ rn = "Compare";
+ break;
+ case 12:
+ val = T0 & 0xFA78FF01;
+ if (T0 & (1 << CP0St_UM))
+ env->hflags |= MIPS_HFLAG_UM;
+ else
+ env->hflags &= ~MIPS_HFLAG_UM;
+ if (T0 & (1 << CP0St_ERL))
+ env->hflags |= MIPS_HFLAG_ERL;
+ else
+ env->hflags &= ~MIPS_HFLAG_ERL;
+ if (T0 & (1 << CP0St_EXL))
+ env->hflags |= MIPS_HFLAG_EXL;
+ else
+ env->hflags &= ~MIPS_HFLAG_EXL;
+ old = env->CP0_Status;
+ env->CP0_Status = val;
+ /* If we unmasked an asserted IRQ, raise it */
+ mask = 0x0000FF00;
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
+ old, val, env->CP0_Cause, old & mask, val & mask,
+ env->CP0_Cause & mask);
+ }
+ if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
+ !(env->hflags & MIPS_HFLAG_EXL) &&
+ !(env->hflags & MIPS_HFLAG_ERL) &&
+ !(env->hflags & MIPS_HFLAG_DM) &&
+ (env->CP0_Status & env->CP0_Cause & mask)) {
+ if (logfile)
+ fprintf(logfile, "Raise pending IRQs\n");
+ env->interrupt_request |= CPU_INTERRUPT_HARD;
+ } else if (!(val & (1 << CP0St_IE)) && (old & (1 << CP0St_IE))) {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ }
+ rn = "Status";
+ break;
+ case 13:
+ val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
+ old = env->CP0_Cause;
+ env->CP0_Cause = val;
+#if 0
+ {
+ int i;
+ /* Check if we ever asserted a software IRQ */
+ for (i = 0; i < 2; i++) {
+ mask = 0x100 << i;
+ if ((val & mask) & !(old & mask))
+ mips_set_irq(i);
+ }
+ }
+#endif
+ rn = "Cause";
+ break;
+ case 14:
+ val = T0;
+ old = env->CP0_EPC;
+ env->CP0_EPC = val;
+ rn = "EPC";
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+#if defined(MIPS_USES_R4K_TLB)
+ val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
+#else
+ val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
+#endif
+ old = env->CP0_Config0;
+ env->CP0_Config0 = val;
+ rn = "Config0";
+ break;
+ default:
+ val = -1;
+ old = -1;
+ rn = "bad config selector";
+ break;
+ }
+ break;
+ case 18:
+ val = T0;
+ old = env->CP0_WatchLo;
+ env->CP0_WatchLo = val;
+ rn = "WatchLo";
+ break;
+ case 19:
+ val = T0 & 0x40FF0FF8;
+ old = env->CP0_WatchHi;
+ env->CP0_WatchHi = val;
+ rn = "WatchHi";
+ break;
+ case 23:
+ val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
+ if (T0 & (1 << CP0DB_DM))
+ env->hflags |= MIPS_HFLAG_DM;
+ else
+ env->hflags &= ~MIPS_HFLAG_DM;
+ old = env->CP0_Debug;
+ env->CP0_Debug = val;
+ rn = "Debug";
+ break;
+ case 24:
+ val = T0;
+ old = env->CP0_DEPC;
+ env->CP0_DEPC = val;
+ rn = "DEPC";
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ val = T0 & 0xFFFFFCF6;
+ old = env->CP0_TagLo;
+ env->CP0_TagLo = val;
+ rn = "TagLo";
+ break;
+ default:
+ val = -1;
+ old = -1;
+ rn = "invalid sel";
+ break;
+ }
+ break;
+ case 30:
+ val = T0;
+ old = env->CP0_ErrorEPC;
+ env->CP0_ErrorEPC = val;
+ rn = "EPC";
+ break;
+ case 31:
+ val = T0;
+ old = env->CP0_DESAVE;
+ env->CP0_DESAVE = val;
+ rn = "DESAVE";
+ break;
+ default:
+ val = -1;
+ old = -1;
+ rn = "unknown";
+ break;
+ }
+ print:
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
+ env->PC, rn, T0, val, reg, sel, old);
+ }
+#endif
+ return;
+}
+
+#ifdef MIPS_USES_FPU
+#include "softfloat.h"
+
+void fpu_handle_exception(void)
+{
+#ifdef CONFIG_SOFTFLOAT
+ int flags = get_float_exception_flags(&env->fp_status);
+ unsigned int cpuflags = 0, enable, cause = 0;
+
+ enable = GET_FP_ENABLE(env->fcr31);
+
+ /* determine current flags */
+ if (flags & float_flag_invalid) {
+ cpuflags |= FP_INVALID;
+ cause |= FP_INVALID & enable;
+ }
+ if (flags & float_flag_divbyzero) {
+ cpuflags |= FP_DIV0;
+ cause |= FP_DIV0 & enable;
+ }
+ if (flags & float_flag_overflow) {
+ cpuflags |= FP_OVERFLOW;
+ cause |= FP_OVERFLOW & enable;
+ }
+ if (flags & float_flag_underflow) {
+ cpuflags |= FP_UNDERFLOW;
+ cause |= FP_UNDERFLOW & enable;
+ }
+ if (flags & float_flag_inexact) {
+ cpuflags |= FP_INEXACT;
+ cause |= FP_INEXACT & enable;
+ }
+ SET_FP_FLAGS(env->fcr31, cpuflags);
+ SET_FP_CAUSE(env->fcr31, cause);
+#else
+ SET_FP_FLAGS(env->fcr31, 0);
+ SET_FP_CAUSE(env->fcr31, 0);
+#endif
+}
+#endif /* MIPS_USES_FPU */
+
+/* TLB management */
+#if defined(MIPS_USES_R4K_TLB)
+static void invalidate_tlb (int idx)
+{
+ tlb_t *tlb;
+ target_ulong addr;
+
+ tlb = &env->tlb[idx];
+ if (tlb->V0) {
+ tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
+ addr = tlb->VPN;
+ while (addr < tlb->end) {
+ tlb_flush_page (env, addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+ }
+ if (tlb->V1) {
+ tb_invalidate_page_range(tlb->PFN[1], tlb->end2 - tlb->end);
+ addr = tlb->end;
+ while (addr < tlb->end2) {
+ tlb_flush_page (env, addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+ }
+}
+
+static void fill_tlb (int idx)
+{
+ tlb_t *tlb;
+ int size;
+
+ /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
+ tlb = &env->tlb[idx];
+ tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
+ tlb->ASID = env->CP0_EntryHi & 0xFF;
+ size = env->CP0_PageMask >> 13;
+ size = 4 * (size + 1);
+ tlb->end = tlb->VPN + (1 << (8 + size));
+ tlb->end2 = tlb->end + (1 << (8 + size));
+ tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
+ tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
+ tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
+ tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
+ tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
+ tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
+ tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
+ tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
+ tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
+}
+
+void do_tlbwi (void)
+{
+ /* Wildly undefined effects for CP0_index containing a too high value and
+ MIPS_TLB_NB not being a power of two. But so does real silicon. */
+ invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
+ fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
+}
+
+void do_tlbwr (void)
+{
+ int r = cpu_mips_get_random(env);
+
+ invalidate_tlb(r);
+ fill_tlb(r);
+}
+
+void do_tlbp (void)
+{
+ tlb_t *tlb;
+ target_ulong tag;
+ uint8_t ASID;
+ int i;
+
+ tag = env->CP0_EntryHi & 0xFFFFE000;
+ ASID = env->CP0_EntryHi & 0xFF;
+ for (i = 0; i < MIPS_TLB_NB; i++) {
+ tlb = &env->tlb[i];
+ /* Check ASID, virtual page number & size */
+ if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
+ /* TLB match */
+ env->CP0_index = i;
+ break;
+ }
+ }
+ if (i == MIPS_TLB_NB) {
+ env->CP0_index |= 0x80000000;
+ }
+}
+
+void do_tlbr (void)
+{
+ tlb_t *tlb;
+ uint8_t ASID;
+ int size;
+
+ ASID = env->CP0_EntryHi & 0xFF;
+ tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
+
+ /* If this will change the current ASID, flush qemu's TLB. */
+ if (ASID != tlb->ASID && tlb->G != 1)
+ tlb_flush (env, 1);
+
+ env->CP0_EntryHi = tlb->VPN | tlb->ASID;
+ size = (tlb->end - tlb->VPN) >> 12;
+ env->CP0_PageMask = (size - 1) << 13;
+ env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2)
+ | (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
+ env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2)
+ | (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
+}
+#endif
+
+#endif /* !CONFIG_USER_ONLY */
+
+void op_dump_ldst (const unsigned char *func)
+{
+ if (loglevel)
+ fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
+}
+
+void dump_sc (void)
+{
+ if (loglevel) {
+ fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
+ T1, T0, env->CP0_LLAddr);
+ }
+}
+
+void debug_eret (void)
+{
+ if (loglevel) {
+ fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
+ env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
+ env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
+ }
+}
+
+void do_pmon (int function)
+{
+ function /= 2;
+ switch (function) {
+ case 2: /* TODO: char inbyte(int waitflag); */
+ if (env->gpr[4] == 0)
+ env->gpr[2] = -1;
+ /* Fall through */
+ case 11: /* TODO: char inbyte (void); */
+ env->gpr[2] = -1;
+ break;
+ case 3:
+ case 12:
+ printf("%c", env->gpr[4] & 0xFF);
+ break;
+ case 17:
+ break;
+ case 158:
+ {
+ unsigned char *fmt = (void *)env->gpr[4];
+ printf("%s", fmt);
+ }
+ break;
+ }
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
+
+#define MMUSUFFIX _mmu
+#define ALIGNED_ONLY
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ env->CP0_BadVAddr = addr;
+ do_restore_state (retaddr);
+ do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
+}
+
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ do_raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-mips/op_helper_mem.c b/target-mips/op_helper_mem.c
new file mode 100644
index 0000000..4711f7a
--- /dev/null
+++ b/target-mips/op_helper_mem.c
@@ -0,0 +1,141 @@
+#ifdef TARGET_WORDS_BIGENDIAN
+#define GET_LMASK(v) ((v) & 3)
+#else
+#define GET_LMASK(v) (((v) & 3) ^ 3)
+#endif
+
+void glue(do_lwl, MEMSUFFIX) (uint32_t tmp)
+{
+#if defined (DEBUG_OP)
+ target_ulong sav = T0;
+#endif
+
+ /* XXX: this is valid only in big-endian mode
+ * should be reverted for little-endian...
+ */
+ switch (GET_LMASK(T0)) {
+ case 0:
+ T0 = tmp;
+ break;
+ case 1:
+ T0 = (tmp << 8) | (T1 & 0x000000FF);
+ break;
+ case 2:
+ T0 = (tmp << 16) | (T1 & 0x0000FFFF);
+ break;
+ case 3:
+ T0 = (tmp << 24) | (T1 & 0x00FFFFFF);
+ break;
+ }
+#if defined (DEBUG_OP)
+ if (logfile) {
+ fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
+ __func__, sav, tmp, T1, T0);
+ }
+#endif
+ RETURN();
+}
+
+void glue(do_lwr, MEMSUFFIX) (uint32_t tmp)
+{
+#if defined (DEBUG_OP)
+ target_ulong sav = T0;
+#endif
+
+ /* XXX: this is valid only in big-endian mode
+ * should be reverted for little-endian...
+ */
+ switch (GET_LMASK(T0)) {
+ case 0:
+ T0 = (tmp >> 24) | (T1 & 0xFFFFFF00);
+ break;
+ case 1:
+ T0 = (tmp >> 16) | (T1 & 0xFFFF0000);
+ break;
+ case 2:
+ T0 = (tmp >> 8) | (T1 & 0xFF000000);
+ break;
+ case 3:
+ T0 = tmp;
+ break;
+ }
+#if defined (DEBUG_OP)
+ if (logfile) {
+ fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
+ __func__, sav, tmp, T1, T0);
+ }
+#endif
+ RETURN();
+}
+
+uint32_t glue(do_swl, MEMSUFFIX) (uint32_t tmp)
+{
+#if defined (DEBUG_OP)
+ target_ulong sav;
+#endif
+
+#if defined (DEBUG_OP)
+ sav = tmp;
+#endif
+ /* XXX: this is valid only in big-endian mode
+ * should be reverted for little-endian...
+ */
+ switch (GET_LMASK(T0)) {
+ case 0:
+ tmp = T1;
+ break;
+ case 1:
+ tmp = (tmp & 0xFF000000) | (T1 >> 8);
+ break;
+ case 2:
+ tmp = (tmp & 0xFFFF0000) | (T1 >> 16);
+ break;
+ case 3:
+ tmp = (tmp & 0xFFFFFF00) | (T1 >> 24);
+ break;
+ }
+#if defined (DEBUG_OP)
+ if (logfile) {
+ fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
+ __func__, T0, sav, T1, tmp);
+ }
+#endif
+ RETURN();
+ return tmp;
+}
+
+uint32_t glue(do_swr, MEMSUFFIX) (uint32_t tmp)
+{
+#if defined (DEBUG_OP)
+ target_ulong sav;
+#endif
+
+#if defined (DEBUG_OP)
+ sav = tmp;
+#endif
+ /* XXX: this is valid only in big-endian mode
+ * should be reverted for little-endian...
+ */
+ switch (GET_LMASK(T0)) {
+ case 0:
+ tmp = (tmp & 0x00FFFFFF) | (T1 << 24);
+ break;
+ case 1:
+ tmp = (tmp & 0x0000FFFF) | (T1 << 16);
+ break;
+ case 2:
+ tmp = (tmp & 0x000000FF) | (T1 << 8);
+ break;
+ case 3:
+ tmp = T1;
+ break;
+ }
+#if defined (DEBUG_OP)
+ if (logfile) {
+ fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
+ __func__, T0, sav, T1, tmp);
+ }
+#endif
+ RETURN();
+ return tmp;
+}
diff --git a/target-mips/op_mem.c b/target-mips/op_mem.c
new file mode 100644
index 0000000..35ccd44
--- /dev/null
+++ b/target-mips/op_mem.c
@@ -0,0 +1,149 @@
+/*
+ * MIPS emulation memory micro-operations for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Standard loads and stores */
+void glue(op_lb, MEMSUFFIX) (void)
+{
+ T0 = glue(ldsb, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_lbu, MEMSUFFIX) (void)
+{
+ T0 = glue(ldub, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_sb, MEMSUFFIX) (void)
+{
+ glue(stb, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+void glue(op_lh, MEMSUFFIX) (void)
+{
+ T0 = glue(ldsw, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_lhu, MEMSUFFIX) (void)
+{
+ T0 = glue(lduw, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_sh, MEMSUFFIX) (void)
+{
+ glue(stw, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+void glue(op_lw, MEMSUFFIX) (void)
+{
+ T0 = glue(ldl, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_lwu, MEMSUFFIX) (void)
+{
+ T0 = glue(ldl, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+void glue(op_sw, MEMSUFFIX) (void)
+{
+ glue(stl, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+/* "half" load and stores. We must do the memory access inline,
+ or fault handling won't work. */
+void glue(op_lwl, MEMSUFFIX) (void)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
+ CALL_FROM_TB1(glue(do_lwl, MEMSUFFIX), tmp);
+ RETURN();
+}
+
+void glue(op_lwr, MEMSUFFIX) (void)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
+ CALL_FROM_TB1(glue(do_lwr, MEMSUFFIX), tmp);
+ RETURN();
+}
+
+void glue(op_swl, MEMSUFFIX) (void)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
+ tmp = CALL_FROM_TB1(glue(do_swl, MEMSUFFIX), tmp);
+ glue(stl, MEMSUFFIX)(T0 & ~3, tmp);
+ RETURN();
+}
+
+void glue(op_swr, MEMSUFFIX) (void)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
+ tmp = CALL_FROM_TB1(glue(do_swr, MEMSUFFIX), tmp);
+ glue(stl, MEMSUFFIX)(T0 & ~3, tmp);
+ RETURN();
+}
+
+void glue(op_ll, MEMSUFFIX) (void)
+{
+ T1 = T0;
+ T0 = glue(ldl, MEMSUFFIX)(T0);
+ env->CP0_LLAddr = T1;
+ RETURN();
+}
+
+void glue(op_sc, MEMSUFFIX) (void)
+{
+ CALL_FROM_TB0(dump_sc);
+ if (T0 == env->CP0_LLAddr) {
+ glue(stl, MEMSUFFIX)(T0, T1);
+ T0 = 1;
+ } else {
+ T0 = 0;
+ }
+ RETURN();
+}
+
+#ifdef MIPS_USES_FPU
+void glue(op_lwc1, MEMSUFFIX) (void)
+{
+ WT0 = glue(ldl, MEMSUFFIX)(T0);
+ RETURN();
+}
+void glue(op_swc1, MEMSUFFIX) (void)
+{
+ glue(stl, MEMSUFFIX)(T0, WT0);
+ RETURN();
+}
+void glue(op_ldc1, MEMSUFFIX) (void)
+{
+ DT0 = glue(ldq, MEMSUFFIX)(T0);
+ RETURN();
+}
+void glue(op_sdc1, MEMSUFFIX) (void)
+{
+ glue(stq, MEMSUFFIX)(T0, DT0);
+ RETURN();
+}
+#endif
diff --git a/target-mips/op_template.c b/target-mips/op_template.c
new file mode 100644
index 0000000..9314c95
--- /dev/null
+++ b/target-mips/op_template.c
@@ -0,0 +1,65 @@
+/*
+ * MIPS emulation micro-operations templates for reg load & store for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(REG)
+void glue(op_load_gpr_T0_gpr, REG) (void)
+{
+ T0 = env->gpr[REG];
+ RETURN();
+}
+
+void glue(op_store_T0_gpr_gpr, REG) (void)
+{
+ env->gpr[REG] = T0;
+ RETURN();
+}
+
+void glue(op_load_gpr_T1_gpr, REG) (void)
+{
+ T1 = env->gpr[REG];
+ RETURN();
+}
+
+void glue(op_store_T1_gpr_gpr, REG) (void)
+{
+ env->gpr[REG] = T1;
+ RETURN();
+}
+
+void glue(op_load_gpr_T2_gpr, REG) (void)
+{
+ T2 = env->gpr[REG];
+ RETURN();
+}
+#endif
+
+#if defined (TN)
+void glue(op_set_, TN) (void)
+{
+ TN = PARAM1;
+ RETURN();
+}
+
+void glue (op_reset_, TN) (void)
+{
+ TN = 0;
+ RETURN();
+}
+#endif
diff --git a/target-mips/translate.c b/target-mips/translate.c
new file mode 100644
index 0000000..7ad8ebd
--- /dev/null
+++ b/target-mips/translate.c
@@ -0,0 +1,2443 @@
+/*
+ * MIPS32 emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+//#define MIPS_DEBUG_DISAS
+//#define MIPS_SINGLE_STEP
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+
+#include "gen-op.h"
+
+/* MIPS opcodes */
+#define EXT_SPECIAL 0x100
+#define EXT_SPECIAL2 0x200
+#define EXT_REGIMM 0x300
+#define EXT_CP0 0x400
+#define EXT_CP1 0x500
+#define EXT_CP2 0x600
+#define EXT_CP3 0x700
+
+enum {
+ /* indirect opcode tables */
+ OPC_SPECIAL = 0x00,
+ OPC_BREGIMM = 0x01,
+ OPC_CP0 = 0x10,
+ OPC_CP1 = 0x11,
+ OPC_CP2 = 0x12,
+ OPC_CP3 = 0x13,
+ OPC_SPECIAL2 = 0x1C,
+ /* arithmetic with immediate */
+ OPC_ADDI = 0x08,
+ OPC_ADDIU = 0x09,
+ OPC_SLTI = 0x0A,
+ OPC_SLTIU = 0x0B,
+ OPC_ANDI = 0x0C,
+ OPC_ORI = 0x0D,
+ OPC_XORI = 0x0E,
+ OPC_LUI = 0x0F,
+ /* Jump and branches */
+ OPC_J = 0x02,
+ OPC_JAL = 0x03,
+ OPC_BEQ = 0x04, /* Unconditional if rs = rt = 0 (B) */
+ OPC_BEQL = 0x14,
+ OPC_BNE = 0x05,
+ OPC_BNEL = 0x15,
+ OPC_BLEZ = 0x06,
+ OPC_BLEZL = 0x16,
+ OPC_BGTZ = 0x07,
+ OPC_BGTZL = 0x17,
+ OPC_JALX = 0x1D, /* MIPS 16 only */
+ /* Load and stores */
+ OPC_LB = 0x20,
+ OPC_LH = 0x21,
+ OPC_LWL = 0x22,
+ OPC_LW = 0x23,
+ OPC_LBU = 0x24,
+ OPC_LHU = 0x25,
+ OPC_LWR = 0x26,
+ OPC_LWU = 0x27,
+ OPC_SB = 0x28,
+ OPC_SH = 0x29,
+ OPC_SWL = 0x2A,
+ OPC_SW = 0x2B,
+ OPC_SWR = 0x2E,
+ OPC_LL = 0x30,
+ OPC_SC = 0x38,
+ /* Floating point load/store */
+ OPC_LWC1 = 0x31,
+ OPC_LWC2 = 0x32,
+ OPC_LDC1 = 0x35,
+ OPC_LDC2 = 0x36,
+ OPC_SWC1 = 0x39,
+ OPC_SWC2 = 0x3A,
+ OPC_SDC1 = 0x3D,
+ OPC_SDC2 = 0x3E,
+ /* Cache and prefetch */
+ OPC_CACHE = 0x2F,
+ OPC_PREF = 0x33,
+};
+
+/* MIPS special opcodes */
+enum {
+ /* Shifts */
+ OPC_SLL = 0x00 | EXT_SPECIAL,
+ /* NOP is SLL r0, r0, 0 */
+ /* SSNOP is SLL r0, r0, 1 */
+ OPC_SRL = 0x02 | EXT_SPECIAL,
+ OPC_SRA = 0x03 | EXT_SPECIAL,
+ OPC_SLLV = 0x04 | EXT_SPECIAL,
+ OPC_SRLV = 0x06 | EXT_SPECIAL,
+ OPC_SRAV = 0x07 | EXT_SPECIAL,
+ /* Multiplication / division */
+ OPC_MULT = 0x18 | EXT_SPECIAL,
+ OPC_MULTU = 0x19 | EXT_SPECIAL,
+ OPC_DIV = 0x1A | EXT_SPECIAL,
+ OPC_DIVU = 0x1B | EXT_SPECIAL,
+ /* 2 registers arithmetic / logic */
+ OPC_ADD = 0x20 | EXT_SPECIAL,
+ OPC_ADDU = 0x21 | EXT_SPECIAL,
+ OPC_SUB = 0x22 | EXT_SPECIAL,
+ OPC_SUBU = 0x23 | EXT_SPECIAL,
+ OPC_AND = 0x24 | EXT_SPECIAL,
+ OPC_OR = 0x25 | EXT_SPECIAL,
+ OPC_XOR = 0x26 | EXT_SPECIAL,
+ OPC_NOR = 0x27 | EXT_SPECIAL,
+ OPC_SLT = 0x2A | EXT_SPECIAL,
+ OPC_SLTU = 0x2B | EXT_SPECIAL,
+ /* Jumps */
+ OPC_JR = 0x08 | EXT_SPECIAL,
+ OPC_JALR = 0x09 | EXT_SPECIAL,
+ /* Traps */
+ OPC_TGE = 0x30 | EXT_SPECIAL,
+ OPC_TGEU = 0x31 | EXT_SPECIAL,
+ OPC_TLT = 0x32 | EXT_SPECIAL,
+ OPC_TLTU = 0x33 | EXT_SPECIAL,
+ OPC_TEQ = 0x34 | EXT_SPECIAL,
+ OPC_TNE = 0x36 | EXT_SPECIAL,
+ /* HI / LO registers load & stores */
+ OPC_MFHI = 0x10 | EXT_SPECIAL,
+ OPC_MTHI = 0x11 | EXT_SPECIAL,
+ OPC_MFLO = 0x12 | EXT_SPECIAL,
+ OPC_MTLO = 0x13 | EXT_SPECIAL,
+ /* Conditional moves */
+ OPC_MOVZ = 0x0A | EXT_SPECIAL,
+ OPC_MOVN = 0x0B | EXT_SPECIAL,
+
+ OPC_MOVCI = 0x01 | EXT_SPECIAL,
+
+ /* Special */
+ OPC_PMON = 0x05 | EXT_SPECIAL,
+ OPC_SYSCALL = 0x0C | EXT_SPECIAL,
+ OPC_BREAK = 0x0D | EXT_SPECIAL,
+ OPC_SYNC = 0x0F | EXT_SPECIAL,
+};
+
+enum {
+ /* Mutiply & xxx operations */
+ OPC_MADD = 0x00 | EXT_SPECIAL2,
+ OPC_MADDU = 0x01 | EXT_SPECIAL2,
+ OPC_MUL = 0x02 | EXT_SPECIAL2,
+ OPC_MSUB = 0x04 | EXT_SPECIAL2,
+ OPC_MSUBU = 0x05 | EXT_SPECIAL2,
+ /* Misc */
+ OPC_CLZ = 0x20 | EXT_SPECIAL2,
+ OPC_CLO = 0x21 | EXT_SPECIAL2,
+ /* Special */
+ OPC_SDBBP = 0x3F | EXT_SPECIAL2,
+};
+
+/* Branch REGIMM */
+enum {
+ OPC_BLTZ = 0x00 | EXT_REGIMM,
+ OPC_BLTZL = 0x02 | EXT_REGIMM,
+ OPC_BGEZ = 0x01 | EXT_REGIMM,
+ OPC_BGEZL = 0x03 | EXT_REGIMM,
+ OPC_BLTZAL = 0x10 | EXT_REGIMM,
+ OPC_BLTZALL = 0x12 | EXT_REGIMM,
+ OPC_BGEZAL = 0x11 | EXT_REGIMM,
+ OPC_BGEZALL = 0x13 | EXT_REGIMM,
+ OPC_TGEI = 0x08 | EXT_REGIMM,
+ OPC_TGEIU = 0x09 | EXT_REGIMM,
+ OPC_TLTI = 0x0A | EXT_REGIMM,
+ OPC_TLTIU = 0x0B | EXT_REGIMM,
+ OPC_TEQI = 0x0C | EXT_REGIMM,
+ OPC_TNEI = 0x0E | EXT_REGIMM,
+};
+
+enum {
+ /* Coprocessor 0 (MMU) */
+ OPC_MFC0 = 0x00 | EXT_CP0,
+ OPC_MTC0 = 0x04 | EXT_CP0,
+ OPC_TLBR = 0x01 | EXT_CP0,
+ OPC_TLBWI = 0x02 | EXT_CP0,
+ OPC_TLBWR = 0x06 | EXT_CP0,
+ OPC_TLBP = 0x08 | EXT_CP0,
+ OPC_ERET = 0x18 | EXT_CP0,
+ OPC_DERET = 0x1F | EXT_CP0,
+ OPC_WAIT = 0x20 | EXT_CP0,
+};
+
+#ifdef MIPS_USES_FPU
+enum {
+ /* Coprocessor 1 (FPU) */
+ OPC_MFC1 = 0x00 | EXT_CP1,
+ OPC_MTC1 = 0x04 | EXT_CP1,
+ OPC_CFC1 = 0x02 | EXT_CP1,
+ OPC_CTC1 = 0x06 | EXT_CP1,
+};
+#endif
+
+const unsigned char *regnames[] =
+ { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", };
+
+/* Warning: no function for r0 register (hard wired to zero) */
+#define GEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NULL, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+/* General purpose registers moves */
+GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr);
+GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr);
+GEN32(gen_op_load_gpr_T2, gen_op_load_gpr_T2_gpr);
+
+GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr);
+GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr);
+
+#ifdef MIPS_USES_FPU
+const unsigned char *fregnames[] =
+ { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
+
+# define SFGEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+# define DFGEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NAME ## 0, 0, NAME ## 2, 0, \
+NAME ## 4, 0, NAME ## 6, 0, \
+NAME ## 8, 0, NAME ## 10, 0, \
+NAME ## 12, 0, NAME ## 14, 0, \
+NAME ## 16, 0, NAME ## 18, 0, \
+NAME ## 20, 0, NAME ## 22, 0, \
+NAME ## 24, 0, NAME ## 26, 0, \
+NAME ## 28, 0, NAME ## 30, 0, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+SFGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr);
+SFGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr);
+
+SFGEN32(gen_op_load_fpr_WT1, gen_op_load_fpr_WT1_fpr);
+SFGEN32(gen_op_store_fpr_WT1, gen_op_store_fpr_WT1_fpr);
+
+SFGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr);
+SFGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_fpr);
+
+DFGEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fpr);
+DFGEN32(gen_op_store_fpr_DT0, gen_op_store_fpr_DT0_fpr);
+
+DFGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr);
+DFGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_fpr);
+
+DFGEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fpr);
+DFGEN32(gen_op_store_fpr_DT2, gen_op_store_fpr_DT2_fpr);
+
+#define FOP_CONDS(fmt) \
+static GenOpFunc * cond_ ## fmt ## _table[16] = { \
+ gen_op_cmp_ ## fmt ## _f, \
+ gen_op_cmp_ ## fmt ## _un, \
+ gen_op_cmp_ ## fmt ## _eq, \
+ gen_op_cmp_ ## fmt ## _ueq, \
+ gen_op_cmp_ ## fmt ## _olt, \
+ gen_op_cmp_ ## fmt ## _ult, \
+ gen_op_cmp_ ## fmt ## _ole, \
+ gen_op_cmp_ ## fmt ## _ule, \
+ gen_op_cmp_ ## fmt ## _sf, \
+ gen_op_cmp_ ## fmt ## _ngle, \
+ gen_op_cmp_ ## fmt ## _seq, \
+ gen_op_cmp_ ## fmt ## _ngl, \
+ gen_op_cmp_ ## fmt ## _lt, \
+ gen_op_cmp_ ## fmt ## _nge, \
+ gen_op_cmp_ ## fmt ## _le, \
+ gen_op_cmp_ ## fmt ## _ngt, \
+}; \
+static inline void gen_cmp_ ## fmt(int n) \
+{ \
+ cond_ ## fmt ## _table[n](); \
+}
+
+FOP_CONDS(d)
+FOP_CONDS(s)
+
+#endif
+
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc, saved_pc;
+ uint32_t opcode;
+ /* Routine used to access memory */
+ int mem_idx;
+ uint32_t hflags, saved_hflags;
+ uint32_t CP0_Status;
+ int bstate;
+ target_ulong btarget;
+} DisasContext;
+
+enum {
+ BS_NONE = 0, /* We go out of the TB without reaching a branch or an
+ * exception condition
+ */
+ BS_STOP = 1, /* We want to stop translation for any reason */
+ BS_BRANCH = 2, /* We reached a branch condition */
+ BS_EXCP = 3, /* We reached an exception condition */
+};
+
+#if defined MIPS_DEBUG_DISAS
+#define MIPS_DEBUG(fmt, args...) \
+do { \
+ if (loglevel & CPU_LOG_TB_IN_ASM) { \
+ fprintf(logfile, "%08x: %08x " fmt "\n", \
+ ctx->pc, ctx->opcode , ##args); \
+ } \
+} while (0)
+#else
+#define MIPS_DEBUG(fmt, args...) do { } while(0)
+#endif
+
+#define MIPS_INVAL(op) \
+do { \
+ MIPS_DEBUG("Invalid %s %03x %03x %03x", op, ctx->opcode >> 26, \
+ ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \
+} while (0)
+
+#define GEN_LOAD_REG_TN(Tn, Rn) \
+do { \
+ if (Rn == 0) { \
+ glue(gen_op_reset_, Tn)(); \
+ } else { \
+ glue(gen_op_load_gpr_, Tn)(Rn); \
+ } \
+} while (0)
+
+#define GEN_LOAD_IMM_TN(Tn, Imm) \
+do { \
+ if (Imm == 0) { \
+ glue(gen_op_reset_, Tn)(); \
+ } else { \
+ glue(gen_op_set_, Tn)(Imm); \
+ } \
+} while (0)
+
+#define GEN_STORE_TN_REG(Rn, Tn) \
+do { \
+ if (Rn != 0) { \
+ glue(glue(gen_op_store_, Tn),_gpr)(Rn); \
+ } \
+} while (0)
+
+#ifdef MIPS_USES_FPU
+
+# define GEN_LOAD_FREG_FTN(FTn, Fn) \
+do { \
+ glue(gen_op_load_fpr_, FTn)(Fn); \
+} while (0)
+
+#define GEN_STORE_FTN_FREG(Fn, FTn) \
+do { \
+ glue(gen_op_store_fpr_, FTn)(Fn); \
+} while (0)
+
+#endif
+
+static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
+{
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "hflags %08x saved %08x\n",
+ ctx->hflags, ctx->saved_hflags);
+ }
+#endif
+ if (do_save_pc && ctx->pc != ctx->saved_pc) {
+ gen_op_save_pc(ctx->pc);
+ ctx->saved_pc = ctx->pc;
+ }
+ if (ctx->hflags != ctx->saved_hflags) {
+ gen_op_save_state(ctx->hflags);
+ ctx->saved_hflags = ctx->hflags;
+ if (ctx->hflags & MIPS_HFLAG_BR) {
+ gen_op_save_breg_target();
+ } else if (ctx->hflags & MIPS_HFLAG_B) {
+ gen_op_save_btarget(ctx->btarget);
+ } else if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ gen_op_save_bcond();
+ gen_op_save_btarget(ctx->btarget);
+ }
+ }
+}
+
+static inline void generate_exception_err (DisasContext *ctx, int excp, int err)
+{
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM)
+ fprintf(logfile, "%s: raise exception %d\n", __func__, excp);
+#endif
+ save_cpu_state(ctx, 1);
+ if (err == 0)
+ gen_op_raise_exception(excp);
+ else
+ gen_op_raise_exception_err(excp, err);
+ ctx->bstate = BS_EXCP;
+}
+
+static inline void generate_exception (DisasContext *ctx, int excp)
+{
+ generate_exception_err (ctx, excp, 0);
+}
+
+#if defined(CONFIG_USER_ONLY)
+#define op_ldst(name) gen_op_##name##_raw()
+#define OP_LD_TABLE(width)
+#define OP_ST_TABLE(width)
+#else
+#define op_ldst(name) (*gen_op_##name[ctx->mem_idx])()
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_l##width[] = { \
+ &gen_op_l##width##_user, \
+ &gen_op_l##width##_kernel, \
+}
+#define OP_ST_TABLE(width) \
+static GenOpFunc *gen_op_s##width[] = { \
+ &gen_op_s##width##_user, \
+ &gen_op_s##width##_kernel, \
+}
+#endif
+
+#ifdef TARGET_MIPS64
+OP_LD_TABLE(d);
+OP_LD_TABLE(dl);
+OP_LD_TABLE(dr);
+OP_ST_TABLE(d);
+OP_ST_TABLE(dl);
+OP_ST_TABLE(dr);
+#endif
+OP_LD_TABLE(w);
+OP_LD_TABLE(wu);
+OP_LD_TABLE(wl);
+OP_LD_TABLE(wr);
+OP_ST_TABLE(w);
+OP_ST_TABLE(wl);
+OP_ST_TABLE(wr);
+OP_LD_TABLE(h);
+OP_LD_TABLE(hu);
+OP_ST_TABLE(h);
+OP_LD_TABLE(b);
+OP_LD_TABLE(bu);
+OP_ST_TABLE(b);
+OP_LD_TABLE(l);
+OP_ST_TABLE(c);
+#ifdef MIPS_USES_FPU
+OP_LD_TABLE(wc1);
+OP_ST_TABLE(wc1);
+OP_LD_TABLE(dc1);
+OP_ST_TABLE(dc1);
+#endif
+
+/* Load and store */
+static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
+ int base, int16_t offset)
+{
+ const unsigned char *opn = "unk";
+
+ if (base == 0) {
+ GEN_LOAD_IMM_TN(T0, offset);
+ } else if (offset == 0) {
+ gen_op_load_gpr_T0(base);
+ } else {
+ gen_op_load_gpr_T0(base);
+ gen_op_set_T1(offset);
+ gen_op_add();
+ }
+ /* Don't do NOP if destination is zero: we must perform the actual
+ * memory access
+ */
+ switch (opc) {
+#if defined(TARGET_MIPS64)
+ case OPC_LD:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_ULD:
+#endif
+ op_ldst(ld);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "ld";
+ break;
+ case OPC_SD:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_USD:
+#endif
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sd);
+ opn = "sd";
+ break;
+ case OPC_LDL:
+ op_ldst(ldl);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "ldl";
+ break;
+ case OPC_SDL:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sdl);
+ opn = "sdl";
+ break;
+ case OPC_LDR:
+ op_ldst(ldr);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "ldr";
+ break;
+ case OPC_SDR:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sdr);
+ opn = "sdr";
+ break;
+#endif
+ case OPC_LW:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_ULW:
+#endif
+ op_ldst(lw);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lw";
+ break;
+ case OPC_LWU:
+ op_ldst(lwu);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lwu";
+ break;
+ case OPC_SW:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_USW:
+#endif
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sw);
+ opn = "sw";
+ break;
+ case OPC_LH:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_ULH:
+#endif
+ op_ldst(lh);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lh";
+ break;
+ case OPC_SH:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_USH:
+#endif
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sh);
+ opn = "sh";
+ break;
+ case OPC_LHU:
+#if defined (MIPS_HAS_UNALIGNED_LS)
+ case OPC_ULHU:
+#endif
+ op_ldst(lhu);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lhu";
+ break;
+ case OPC_LB:
+ op_ldst(lb);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lb";
+ break;
+ case OPC_SB:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sb);
+ opn = "sb";
+ break;
+ case OPC_LBU:
+ op_ldst(lbu);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lbu";
+ break;
+ case OPC_LWL:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(lwl);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lwl";
+ break;
+ case OPC_SWL:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(swl);
+ opn = "swr";
+ break;
+ case OPC_LWR:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(lwr);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "lwr";
+ break;
+ case OPC_SWR:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(swr);
+ opn = "swr";
+ break;
+ case OPC_LL:
+ op_ldst(ll);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "ll";
+ break;
+ case OPC_SC:
+ GEN_LOAD_REG_TN(T1, rt);
+ op_ldst(sc);
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "sc";
+ break;
+ default:
+ MIPS_INVAL("load/store");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
+}
+
+#ifdef MIPS_USES_FPU
+
+/* Load and store */
+static void gen_flt_ldst (DisasContext *ctx, uint16_t opc, int ft,
+ int base, int16_t offset)
+{
+ const unsigned char *opn = "unk";
+
+ if (base == 0) {
+ GEN_LOAD_IMM_TN(T0, offset);
+ } else if (offset == 0) {
+ gen_op_load_gpr_T0(base);
+ } else {
+ gen_op_load_gpr_T0(base);
+ gen_op_set_T1(offset);
+ gen_op_add();
+ }
+ /* Don't do NOP if destination is zero: we must perform the actual
+ * memory access
+ */
+ switch (opc) {
+ case OPC_LWC1:
+ op_ldst(lwc1);
+ GEN_STORE_FTN_FREG(ft, WT0);
+ opn = "lwc1";
+ break;
+ case OPC_SWC1:
+ GEN_LOAD_FREG_FTN(WT0, ft);
+ op_ldst(swc1);
+ opn = "swc1";
+ break;
+ case OPC_LDC1:
+ op_ldst(ldc1);
+ GEN_STORE_FTN_FREG(ft, DT0);
+ opn = "ldc1";
+ break;
+ case OPC_SDC1:
+ GEN_LOAD_FREG_FTN(DT0, ft);
+ op_ldst(sdc1);
+ opn = "sdc1";
+ break;
+ default:
+ MIPS_INVAL("float load/store");
+ generate_exception(ctx, EXCP_CpU);
+ return;
+ }
+ MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]);
+}
+#endif
+
+/* Arithmetic with immediate operand */
+static void gen_arith_imm (DisasContext *ctx, uint16_t opc, int rt,
+ int rs, int16_t imm)
+{
+ uint32_t uimm;
+ const unsigned char *opn = "unk";
+
+ if (rt == 0 && opc != OPC_ADDI) {
+ /* if no destination, treat it as a NOP
+ * For addi, we must generate the overflow exception when needed.
+ */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ if (opc == OPC_ADDI || opc == OPC_ADDIU ||
+ opc == OPC_SLTI || opc == OPC_SLTIU)
+ uimm = (int32_t)imm; /* Sign extent to 32 bits */
+ else
+ uimm = (uint16_t)imm;
+ if (opc != OPC_LUI) {
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_IMM_TN(T1, uimm);
+ } else {
+ uimm = uimm << 16;
+ GEN_LOAD_IMM_TN(T0, uimm);
+ }
+ switch (opc) {
+ case OPC_ADDI:
+ save_cpu_state(ctx, 1);
+ gen_op_addo();
+ opn = "addi";
+ break;
+ case OPC_ADDIU:
+ gen_op_add();
+ opn = "addiu";
+ break;
+ case OPC_SLTI:
+ gen_op_lt();
+ opn = "slti";
+ break;
+ case OPC_SLTIU:
+ gen_op_ltu();
+ opn = "sltiu";
+ break;
+ case OPC_ANDI:
+ gen_op_and();
+ opn = "andi";
+ break;
+ case OPC_ORI:
+ gen_op_or();
+ opn = "ori";
+ break;
+ case OPC_XORI:
+ gen_op_xor();
+ opn = "xori";
+ break;
+ case OPC_LUI:
+ opn = "lui";
+ break;
+ case OPC_SLL:
+ gen_op_sll();
+ opn = "sll";
+ break;
+ case OPC_SRA:
+ gen_op_sra();
+ opn = "sra";
+ break;
+ case OPC_SRL:
+ gen_op_srl();
+ opn = "srl";
+ break;
+ default:
+ MIPS_INVAL("imm arith");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ GEN_STORE_TN_REG(rt, T0);
+ MIPS_DEBUG("%s %s, %s, %x", opn, regnames[rt], regnames[rs], uimm);
+}
+
+/* Arithmetic */
+static void gen_arith (DisasContext *ctx, uint16_t opc,
+ int rd, int rs, int rt)
+{
+ const unsigned char *opn = "unk";
+
+ if (rd == 0 && opc != OPC_ADD && opc != OPC_SUB) {
+ /* if no destination, treat it as a NOP
+ * For add & sub, we must generate the overflow exception when needed.
+ */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_REG_TN(T1, rt);
+ switch (opc) {
+ case OPC_ADD:
+ save_cpu_state(ctx, 1);
+ gen_op_addo();
+ opn = "add";
+ break;
+ case OPC_ADDU:
+ gen_op_add();
+ opn = "addu";
+ break;
+ case OPC_SUB:
+ save_cpu_state(ctx, 1);
+ gen_op_subo();
+ opn = "sub";
+ break;
+ case OPC_SUBU:
+ gen_op_sub();
+ opn = "subu";
+ break;
+ case OPC_SLT:
+ gen_op_lt();
+ opn = "slt";
+ break;
+ case OPC_SLTU:
+ gen_op_ltu();
+ opn = "sltu";
+ break;
+ case OPC_AND:
+ gen_op_and();
+ opn = "and";
+ break;
+ case OPC_NOR:
+ gen_op_nor();
+ opn = "nor";
+ break;
+ case OPC_OR:
+ gen_op_or();
+ opn = "or";
+ break;
+ case OPC_XOR:
+ gen_op_xor();
+ opn = "xor";
+ break;
+ case OPC_MUL:
+ gen_op_mul();
+ opn = "mul";
+ break;
+ case OPC_MOVN:
+ gen_op_movn(rd);
+ opn = "movn";
+ goto print;
+ case OPC_MOVZ:
+ gen_op_movz(rd);
+ opn = "movz";
+ goto print;
+ case OPC_SLLV:
+ gen_op_sllv();
+ opn = "sllv";
+ break;
+ case OPC_SRAV:
+ gen_op_srav();
+ opn = "srav";
+ break;
+ case OPC_SRLV:
+ gen_op_srlv();
+ opn = "srlv";
+ break;
+ default:
+ MIPS_INVAL("arith");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ GEN_STORE_TN_REG(rd, T0);
+ print:
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+}
+
+/* Arithmetic on HI/LO registers */
+static void gen_HILO (DisasContext *ctx, uint16_t opc, int reg)
+{
+ const unsigned char *opn = "unk";
+
+ if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) {
+ /* Treat as a NOP */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ switch (opc) {
+ case OPC_MFHI:
+ gen_op_load_HI();
+ GEN_STORE_TN_REG(reg, T0);
+ opn = "mfhi";
+ break;
+ case OPC_MFLO:
+ gen_op_load_LO();
+ GEN_STORE_TN_REG(reg, T0);
+ opn = "mflo";
+ break;
+ case OPC_MTHI:
+ GEN_LOAD_REG_TN(T0, reg);
+ gen_op_store_HI();
+ opn = "mthi";
+ break;
+ case OPC_MTLO:
+ GEN_LOAD_REG_TN(T0, reg);
+ gen_op_store_LO();
+ opn = "mtlo";
+ break;
+ default:
+ MIPS_INVAL("HILO");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ MIPS_DEBUG("%s %s", opn, regnames[reg]);
+}
+
+static void gen_muldiv (DisasContext *ctx, uint16_t opc,
+ int rs, int rt)
+{
+ const unsigned char *opn = "unk";
+
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_REG_TN(T1, rt);
+ switch (opc) {
+ case OPC_DIV:
+ gen_op_div();
+ opn = "div";
+ break;
+ case OPC_DIVU:
+ gen_op_divu();
+ opn = "divu";
+ break;
+ case OPC_MULT:
+ gen_op_mult();
+ opn = "mult";
+ break;
+ case OPC_MULTU:
+ gen_op_multu();
+ opn = "multu";
+ break;
+ case OPC_MADD:
+ gen_op_madd();
+ opn = "madd";
+ break;
+ case OPC_MADDU:
+ gen_op_maddu();
+ opn = "maddu";
+ break;
+ case OPC_MSUB:
+ gen_op_msub();
+ opn = "msub";
+ break;
+ case OPC_MSUBU:
+ gen_op_msubu();
+ opn = "msubu";
+ break;
+ default:
+ MIPS_INVAL("mul/div");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]);
+}
+
+static void gen_cl (DisasContext *ctx, uint16_t opc,
+ int rd, int rs)
+{
+ const unsigned char *opn = "unk";
+ if (rd == 0) {
+ /* Treat as a NOP */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ GEN_LOAD_REG_TN(T0, rs);
+ switch (opc) {
+ case OPC_CLO:
+ /* CLO */
+ gen_op_clo();
+ opn = "clo";
+ break;
+ case OPC_CLZ:
+ /* CLZ */
+ gen_op_clz();
+ opn = "clz";
+ break;
+ default:
+ MIPS_INVAL("CLx");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ gen_op_store_T0_gpr(rd);
+ MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
+}
+
+/* Traps */
+static void gen_trap (DisasContext *ctx, uint16_t opc,
+ int rs, int rt, int16_t imm)
+{
+ int cond;
+
+ cond = 0;
+ /* Load needed operands */
+ switch (opc) {
+ case OPC_TEQ:
+ case OPC_TGE:
+ case OPC_TGEU:
+ case OPC_TLT:
+ case OPC_TLTU:
+ case OPC_TNE:
+ /* Compare two registers */
+ if (rs != rt) {
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_REG_TN(T1, rt);
+ cond = 1;
+ }
+ case OPC_TEQI:
+ case OPC_TGEI:
+ case OPC_TGEIU:
+ case OPC_TLTI:
+ case OPC_TLTIU:
+ case OPC_TNEI:
+ /* Compare register to immediate */
+ if (rs != 0 || imm != 0) {
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_IMM_TN(T1, (int32_t)imm);
+ cond = 1;
+ }
+ break;
+ }
+ if (cond == 0) {
+ switch (opc) {
+ case OPC_TEQ: /* rs == rs */
+ case OPC_TEQI: /* r0 == 0 */
+ case OPC_TGE: /* rs >= rs */
+ case OPC_TGEI: /* r0 >= 0 */
+ case OPC_TGEU: /* rs >= rs unsigned */
+ case OPC_TGEIU: /* r0 >= 0 unsigned */
+ /* Always trap */
+ gen_op_set_T0(1);
+ break;
+ case OPC_TLT: /* rs < rs */
+ case OPC_TLTI: /* r0 < 0 */
+ case OPC_TLTU: /* rs < rs unsigned */
+ case OPC_TLTIU: /* r0 < 0 unsigned */
+ case OPC_TNE: /* rs != rs */
+ case OPC_TNEI: /* r0 != 0 */
+ /* Never trap: treat as NOP */
+ return;
+ default:
+ MIPS_INVAL("TRAP");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ } else {
+ switch (opc) {
+ case OPC_TEQ:
+ case OPC_TEQI:
+ gen_op_eq();
+ break;
+ case OPC_TGE:
+ case OPC_TGEI:
+ gen_op_ge();
+ break;
+ case OPC_TGEU:
+ case OPC_TGEIU:
+ gen_op_geu();
+ break;
+ case OPC_TLT:
+ case OPC_TLTI:
+ gen_op_lt();
+ break;
+ case OPC_TLTU:
+ case OPC_TLTIU:
+ gen_op_ltu();
+ break;
+ case OPC_TNE:
+ case OPC_TNEI:
+ gen_op_ne();
+ break;
+ default:
+ MIPS_INVAL("TRAP");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ }
+ save_cpu_state(ctx, 1);
+ gen_op_trap();
+ ctx->bstate = BS_STOP;
+}
+
+static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ if (n == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_op_save_pc(dest);
+ gen_op_set_T0((long)tb + n);
+ gen_op_exit_tb();
+ } else {
+ gen_op_save_pc(dest);
+ gen_op_set_T0(0);
+ gen_op_exit_tb();
+ }
+}
+
+/* Branches (before delay slot) */
+static void gen_compute_branch (DisasContext *ctx, uint16_t opc,
+ int rs, int rt, int32_t offset)
+{
+ target_ulong btarget;
+ int blink, bcond;
+
+ btarget = -1;
+ blink = 0;
+ bcond = 0;
+ /* Load needed operands */
+ switch (opc) {
+ case OPC_BEQ:
+ case OPC_BEQL:
+ case OPC_BNE:
+ case OPC_BNEL:
+ /* Compare two registers */
+ if (rs != rt) {
+ GEN_LOAD_REG_TN(T0, rs);
+ GEN_LOAD_REG_TN(T1, rt);
+ bcond = 1;
+ }
+ btarget = ctx->pc + 4 + offset;
+ break;
+ case OPC_BGEZ:
+ case OPC_BGEZAL:
+ case OPC_BGEZALL:
+ case OPC_BGEZL:
+ case OPC_BGTZ:
+ case OPC_BGTZL:
+ case OPC_BLEZ:
+ case OPC_BLEZL:
+ case OPC_BLTZ:
+ case OPC_BLTZAL:
+ case OPC_BLTZALL:
+ case OPC_BLTZL:
+ /* Compare to zero */
+ if (rs != 0) {
+ gen_op_load_gpr_T0(rs);
+ bcond = 1;
+ }
+ btarget = ctx->pc + 4 + offset;
+ break;
+ case OPC_J:
+ case OPC_JAL:
+ /* Jump to immediate */
+ btarget = ((ctx->pc + 4) & 0xF0000000) | offset;
+ break;
+ case OPC_JR:
+ case OPC_JALR:
+ /* Jump to register */
+ if (offset != 0) {
+ /* Only hint = 0 is valid */
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ GEN_LOAD_REG_TN(T2, rs);
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ if (bcond == 0) {
+ /* No condition to be computed */
+ switch (opc) {
+ case OPC_BEQ: /* rx == rx */
+ case OPC_BEQL: /* rx == rx likely */
+ case OPC_BGEZ: /* 0 >= 0 */
+ case OPC_BGEZL: /* 0 >= 0 likely */
+ case OPC_BLEZ: /* 0 <= 0 */
+ case OPC_BLEZL: /* 0 <= 0 likely */
+ /* Always take */
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("balways");
+ break;
+ case OPC_BGEZAL: /* 0 >= 0 */
+ case OPC_BGEZALL: /* 0 >= 0 likely */
+ /* Always take and link */
+ blink = 31;
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("balways and link");
+ break;
+ case OPC_BNE: /* rx != rx */
+ case OPC_BGTZ: /* 0 > 0 */
+ case OPC_BLTZ: /* 0 < 0 */
+ /* Treated as NOP */
+ MIPS_DEBUG("bnever (NOP)");
+ return;
+ case OPC_BLTZAL: /* 0 < 0 */
+ gen_op_set_T0(ctx->pc + 8);
+ gen_op_store_T0_gpr(31);
+ return;
+ case OPC_BLTZALL: /* 0 < 0 likely */
+ gen_op_set_T0(ctx->pc + 8);
+ gen_op_store_T0_gpr(31);
+ gen_goto_tb(ctx, 0, ctx->pc + 4);
+ return;
+ case OPC_BNEL: /* rx != rx likely */
+ case OPC_BGTZL: /* 0 > 0 likely */
+ case OPC_BLTZL: /* 0 < 0 likely */
+ /* Skip the instruction in the delay slot */
+ MIPS_DEBUG("bnever and skip");
+ gen_goto_tb(ctx, 0, ctx->pc + 4);
+ return;
+ case OPC_J:
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("j %08x", btarget);
+ break;
+ case OPC_JAL:
+ blink = 31;
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("jal %08x", btarget);
+ break;
+ case OPC_JR:
+ ctx->hflags |= MIPS_HFLAG_BR;
+ MIPS_DEBUG("jr %s", regnames[rs]);
+ break;
+ case OPC_JALR:
+ blink = rt;
+ ctx->hflags |= MIPS_HFLAG_BR;
+ MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]);
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ } else {
+ switch (opc) {
+ case OPC_BEQ:
+ gen_op_eq();
+ MIPS_DEBUG("beq %s, %s, %08x",
+ regnames[rs], regnames[rt], btarget);
+ goto not_likely;
+ case OPC_BEQL:
+ gen_op_eq();
+ MIPS_DEBUG("beql %s, %s, %08x",
+ regnames[rs], regnames[rt], btarget);
+ goto likely;
+ case OPC_BNE:
+ gen_op_ne();
+ MIPS_DEBUG("bne %s, %s, %08x",
+ regnames[rs], regnames[rt], btarget);
+ goto not_likely;
+ case OPC_BNEL:
+ gen_op_ne();
+ MIPS_DEBUG("bnel %s, %s, %08x",
+ regnames[rs], regnames[rt], btarget);
+ goto likely;
+ case OPC_BGEZ:
+ gen_op_gez();
+ MIPS_DEBUG("bgez %s, %08x", regnames[rs], btarget);
+ goto not_likely;
+ case OPC_BGEZL:
+ gen_op_gez();
+ MIPS_DEBUG("bgezl %s, %08x", regnames[rs], btarget);
+ goto likely;
+ case OPC_BGEZAL:
+ gen_op_gez();
+ MIPS_DEBUG("bgezal %s, %08x", regnames[rs], btarget);
+ blink = 31;
+ goto not_likely;
+ case OPC_BGEZALL:
+ gen_op_gez();
+ blink = 31;
+ MIPS_DEBUG("bgezall %s, %08x", regnames[rs], btarget);
+ goto likely;
+ case OPC_BGTZ:
+ gen_op_gtz();
+ MIPS_DEBUG("bgtz %s, %08x", regnames[rs], btarget);
+ goto not_likely;
+ case OPC_BGTZL:
+ gen_op_gtz();
+ MIPS_DEBUG("bgtzl %s, %08x", regnames[rs], btarget);
+ goto likely;
+ case OPC_BLEZ:
+ gen_op_lez();
+ MIPS_DEBUG("blez %s, %08x", regnames[rs], btarget);
+ goto not_likely;
+ case OPC_BLEZL:
+ gen_op_lez();
+ MIPS_DEBUG("blezl %s, %08x", regnames[rs], btarget);
+ goto likely;
+ case OPC_BLTZ:
+ gen_op_ltz();
+ MIPS_DEBUG("bltz %s, %08x", regnames[rs], btarget);
+ goto not_likely;
+ case OPC_BLTZL:
+ gen_op_ltz();
+ MIPS_DEBUG("bltzl %s, %08x", regnames[rs], btarget);
+ goto likely;
+ case OPC_BLTZAL:
+ gen_op_ltz();
+ blink = 31;
+ MIPS_DEBUG("bltzal %s, %08x", regnames[rs], btarget);
+ not_likely:
+ ctx->hflags |= MIPS_HFLAG_BC;
+ break;
+ case OPC_BLTZALL:
+ gen_op_ltz();
+ blink = 31;
+ MIPS_DEBUG("bltzall %s, %08x", regnames[rs], btarget);
+ likely:
+ ctx->hflags |= MIPS_HFLAG_BL;
+ break;
+ }
+ gen_op_set_bcond();
+ }
+ MIPS_DEBUG("enter ds: link %d cond %02x target %08x",
+ blink, ctx->hflags, btarget);
+ ctx->btarget = btarget;
+ if (blink > 0) {
+ gen_op_set_T0(ctx->pc + 8);
+ gen_op_store_T0_gpr(blink);
+ }
+ return;
+}
+
+/* CP0 (MMU and control) */
+static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd)
+{
+ const unsigned char *opn = "unk";
+
+ if (!(ctx->CP0_Status & (1 << CP0St_CU0)) &&
+ (ctx->hflags & MIPS_HFLAG_UM) &&
+ !(ctx->hflags & MIPS_HFLAG_ERL) &&
+ !(ctx->hflags & MIPS_HFLAG_EXL)) {
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "CP0 is not usable\n");
+ }
+ generate_exception_err (ctx, EXCP_CpU, 0);
+ return;
+ }
+
+ switch (opc) {
+ case OPC_MFC0:
+ if (rt == 0) {
+ /* Treat as NOP */
+ return;
+ }
+ gen_op_mfc0(rd, ctx->opcode & 0x7);
+ gen_op_store_T0_gpr(rt);
+ opn = "mfc0";
+ break;
+ case OPC_MTC0:
+ /* If we get an exception, we want to restart at next instruction */
+ ctx->pc += 4;
+ save_cpu_state(ctx, 1);
+ ctx->pc -= 4;
+ GEN_LOAD_REG_TN(T0, rt);
+ gen_op_mtc0(rd, ctx->opcode & 0x7);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ opn = "mtc0";
+ break;
+#if defined(MIPS_USES_R4K_TLB)
+ case OPC_TLBWI:
+ gen_op_tlbwi();
+ opn = "tlbwi";
+ break;
+ case OPC_TLBWR:
+ gen_op_tlbwr();
+ opn = "tlbwr";
+ break;
+ case OPC_TLBP:
+ gen_op_tlbp();
+ opn = "tlbp";
+ break;
+ case OPC_TLBR:
+ gen_op_tlbr();
+ opn = "tlbr";
+ break;
+#endif
+ case OPC_ERET:
+ opn = "eret";
+ save_cpu_state(ctx, 0);
+ gen_op_eret();
+ ctx->bstate = BS_EXCP;
+ break;
+ case OPC_DERET:
+ opn = "deret";
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_RI);
+ } else {
+ save_cpu_state(ctx, 0);
+ gen_op_deret();
+ ctx->bstate = BS_EXCP;
+ }
+ break;
+ case OPC_WAIT:
+ opn = "wait";
+ /* If we get an exception, we want to restart at next instruction */
+ ctx->pc += 4;
+ save_cpu_state(ctx, 1);
+ ctx->pc -= 4;
+ gen_op_wait();
+ ctx->bstate = BS_EXCP;
+ break;
+ default:
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "Invalid CP0 opcode: %08x %03x %03x %03x\n",
+ ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+ ((ctx->opcode >> 16) & 0x1F));
+ }
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd);
+}
+
+#ifdef MIPS_USES_FPU
+/* CP1 Branches (before delay slot) */
+static void gen_compute_branch1 (DisasContext *ctx, uint16_t cond,
+ int32_t offset)
+{
+ target_ulong btarget;
+
+ btarget = ctx->pc + 4 + offset;
+
+ switch (cond) {
+ case 0x0000: /* bc1f */
+ gen_op_bc1f();
+ MIPS_DEBUG("bc1f %08x", btarget);
+ goto not_likely;
+ case 0x0002: /* bc1fl */
+ gen_op_bc1f();
+ MIPS_DEBUG("bc1fl %08x", btarget);
+ goto likely;
+ case 0x0001: /* bc1t */
+ gen_op_bc1t();
+ MIPS_DEBUG("bc1t %08x", btarget);
+ not_likely:
+ ctx->hflags |= MIPS_HFLAG_BC;
+ break;
+ case 0x0003: /* bc1tl */
+ gen_op_bc1t();
+ MIPS_DEBUG("bc1tl %08x", btarget);
+ likely:
+ ctx->hflags |= MIPS_HFLAG_BL;
+ break;
+ default:
+ MIPS_INVAL("cp1 branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ gen_op_set_bcond();
+
+ MIPS_DEBUG("enter ds: cond %02x target %08x",
+ ctx->hflags, btarget);
+ ctx->btarget = btarget;
+
+ return;
+}
+
+/* Coprocessor 1 (FPU) */
+static void gen_cp1 (DisasContext *ctx, uint16_t opc, int rt, int fs)
+{
+ const unsigned char *opn = "unk";
+
+ switch (opc) {
+ case OPC_MFC1:
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_mfc1();
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "mfc1";
+ break;
+ case OPC_MTC1:
+ GEN_LOAD_REG_TN(T0, rt);
+ gen_op_mtc1();
+ GEN_STORE_FTN_FREG(fs, WT0);
+ opn = "mtc1";
+ break;
+ case OPC_CFC1:
+ if (fs != 0 && fs != 31) {
+ MIPS_INVAL("cfc1 freg");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ GEN_LOAD_IMM_TN(T1, fs);
+ gen_op_cfc1();
+ GEN_STORE_TN_REG(rt, T0);
+ opn = "cfc1";
+ break;
+ case OPC_CTC1:
+ if (fs != 0 && fs != 31) {
+ MIPS_INVAL("ctc1 freg");
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ GEN_LOAD_IMM_TN(T1, fs);
+ GEN_LOAD_REG_TN(T0, rt);
+ gen_op_ctc1();
+ opn = "ctc1";
+ break;
+ default:
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "Invalid CP1 opcode: %08x %03x %03x %03x\n",
+ ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+ ((ctx->opcode >> 16) & 0x1F));
+ }
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
+}
+
+/* verify if floating point register is valid; an operation is not defined
+ * if bit 0 of any register specification is set and the FR bit in the
+ * Status register equals zero, since the register numbers specify an
+ * even-odd pair of adjacent coprocessor general registers. When the FR bit
+ * in the Status register equals one, both even and odd register numbers
+ * are valid.
+ *
+ * Multiple float registers can be checked by calling
+ * CHECK_FR(ctx, freg1 | freg2 | ... | fregN);
+ */
+#define CHECK_FR(ctx, freg) do { \
+ if (!((ctx)->CP0_Status & (1<<CP0St_FR)) && ((freg) & 1)) { \
+ generate_exception(ctx, EXCP_RI); \
+ return; \
+ } \
+ } while(0)
+
+#define FOP(func, fmt) (((fmt) << 21) | (func))
+
+static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, int func)
+{
+ const unsigned char *opn = "unk";
+ const char *condnames[] = {
+ "c.f",
+ "c.un",
+ "c.eq",
+ "c.ueq",
+ "c.olt",
+ "c.ult",
+ "c.ole",
+ "c.ule",
+ "c.sf",
+ "c.ngle",
+ "c.seq",
+ "c.ngl",
+ "c.lt",
+ "c.nge",
+ "c.le",
+ "c.ngt",
+ };
+ int binary = 0;
+
+ switch (ctx->opcode & FOP(0x3f, 0x1f)) {
+ case FOP(0, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_add_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "add.d";
+ binary = 1;
+ break;
+ case FOP(1, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_sub_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sub.d";
+ binary = 1;
+ break;
+ case FOP(2, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_mul_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mul.d";
+ binary = 1;
+ break;
+ case FOP(3, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_div_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "div.d";
+ binary = 1;
+ break;
+ case FOP(4, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_sqrt_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sqrt.d";
+ break;
+ case FOP(5, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_abs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "abs.d";
+ break;
+ case FOP(6, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_mov_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mov.d";
+ break;
+ case FOP(7, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_chs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "neg.d";
+ break;
+ /* 8 - round.l */
+ /* 9 - trunc.l */
+ /* 10 - ceil.l */
+ /* 11 - floor.l */
+ case FOP(12, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_roundw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "round.w.d";
+ break;
+ case FOP(13, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_truncw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.d";
+ break;
+ case FOP(14, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_ceilw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "ceil.w.d";
+ break;
+ case FOP(15, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_floorw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "ceil.w.d";
+ break;
+ case FOP(33, 20): /* cvt.d.w */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvtd_w();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "cvt.d.w";
+ break;
+ case FOP(48, 17):
+ case FOP(49, 17):
+ case FOP(50, 17):
+ case FOP(51, 17):
+ case FOP(52, 17):
+ case FOP(53, 17):
+ case FOP(54, 17):
+ case FOP(55, 17):
+ case FOP(56, 17):
+ case FOP(57, 17):
+ case FOP(58, 17):
+ case FOP(59, 17):
+ case FOP(60, 17):
+ case FOP(61, 17):
+ case FOP(62, 17):
+ case FOP(63, 17):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_cmp_d(func-48);
+ opn = condnames[func-48];
+ break;
+ case FOP(0, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_op_float_add_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "add.s";
+ binary = 1;
+ break;
+ case FOP(1, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_op_float_sub_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "sub.s";
+ binary = 1;
+ break;
+ case FOP(2, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_op_float_mul_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "mul.s";
+ binary = 1;
+ break;
+ case FOP(3, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_op_float_div_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "div.s";
+ binary = 1;
+ break;
+ case FOP(4, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_sqrt_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "sqrt.s";
+ break;
+ case FOP(5, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_abs_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "abs.s";
+ break;
+ case FOP(6, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_mov_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "mov.s";
+ break;
+ case FOP(7, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_chs_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "neg.s";
+ break;
+ case FOP(12, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_roundw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "round.w.s";
+ break;
+ case FOP(13, 16):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_truncw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.s";
+ break;
+ case FOP(32, 20): /* cvt.s.w */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvts_w();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.s.w";
+ break;
+ case FOP(36, 16): /* cvt.w.s */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvtw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.w.s";
+ break;
+ case FOP(36, 17): /* cvt.w.d */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvtw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.w.d";
+ break;
+ case FOP(48, 16):
+ case FOP(49, 16):
+ case FOP(50, 16):
+ case FOP(51, 16):
+ case FOP(52, 16):
+ case FOP(53, 16):
+ case FOP(54, 16):
+ case FOP(55, 16):
+ case FOP(56, 16):
+ case FOP(57, 16):
+ case FOP(58, 16):
+ case FOP(59, 16):
+ case FOP(60, 16):
+ case FOP(61, 16):
+ case FOP(62, 16):
+ case FOP(63, 16):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_cmp_s(func-48);
+ opn = condnames[func-48];
+ break;
+ default:
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "Invalid arith function: %08x %03x %03x %03x\n",
+ ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+ ((ctx->opcode >> 16) & 0x1F));
+ }
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ if (binary)
+ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
+ else
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
+}
+#endif
+
+/* ISA extensions */
+/* MIPS16 extension to MIPS32 */
+/* SmartMIPS extension to MIPS32 */
+
+#ifdef TARGET_MIPS64
+static void gen_arith64 (DisasContext *ctx, uint16_t opc)
+{
+ if (func == 0x02 && rd == 0) {
+ /* NOP */
+ return;
+ }
+ if (rs == 0 || rt == 0) {
+ gen_op_reset_T0();
+ gen_op_save64();
+ } else {
+ gen_op_load_gpr_T0(rs);
+ gen_op_load_gpr_T1(rt);
+ gen_op_save64();
+ if (func & 0x01)
+ gen_op_mul64u();
+ else
+ gen_op_mul64s();
+ }
+ if (func & 0x02)
+ gen_op_add64();
+ else
+ gen_op_sub64();
+}
+
+/* Coprocessor 3 (FPU) */
+
+/* MDMX extension to MIPS64 */
+/* MIPS-3D extension to MIPS64 */
+
+#endif
+
+static void gen_blikely(DisasContext *ctx)
+{
+ int l1;
+ l1 = gen_new_label();
+ gen_op_jnz_T2(l1);
+ gen_op_save_state(ctx->hflags & ~MIPS_HFLAG_BMASK);
+ gen_goto_tb(ctx, 1, ctx->pc + 4);
+ gen_set_label(l1);
+}
+
+static void decode_opc (DisasContext *ctx)
+{
+ int32_t offset;
+ int rs, rt, rd, sa;
+ uint16_t op, op1;
+ int16_t imm;
+
+ /* make sure instructions are on a word boundary */
+ if (ctx->pc & 0x3) {
+ generate_exception(ctx, EXCP_AdEL);
+ return;
+ }
+
+ if ((ctx->hflags & MIPS_HFLAG_BMASK) == MIPS_HFLAG_BL) {
+ /* Handle blikely not taken case */
+ MIPS_DEBUG("blikely condition (%08x)", ctx->pc + 4);
+ gen_blikely(ctx);
+ }
+ op = ctx->opcode >> 26;
+ rs = ((ctx->opcode >> 21) & 0x1F);
+ rt = ((ctx->opcode >> 16) & 0x1F);
+ rd = ((ctx->opcode >> 11) & 0x1F);
+ sa = ((ctx->opcode >> 6) & 0x1F);
+ imm = (int16_t)ctx->opcode;
+ switch (op) {
+ case 0x00: /* Special opcode */
+ op1 = ctx->opcode & 0x3F;
+ switch (op1) {
+ case 0x00: /* Arithmetic with immediate */
+ case 0x02 ... 0x03:
+ gen_arith_imm(ctx, op1 | EXT_SPECIAL, rd, rt, sa);
+ break;
+ case 0x04: /* Arithmetic */
+ case 0x06 ... 0x07:
+ case 0x0A ... 0x0B:
+ case 0x20 ... 0x27:
+ case 0x2A ... 0x2B:
+ gen_arith(ctx, op1 | EXT_SPECIAL, rd, rs, rt);
+ break;
+ case 0x18 ... 0x1B: /* MULT / DIV */
+ gen_muldiv(ctx, op1 | EXT_SPECIAL, rs, rt);
+ break;
+ case 0x08 ... 0x09: /* Jumps */
+ gen_compute_branch(ctx, op1 | EXT_SPECIAL, rs, rd, sa);
+ return;
+ case 0x30 ... 0x34: /* Traps */
+ case 0x36:
+ gen_trap(ctx, op1 | EXT_SPECIAL, rs, rt, -1);
+ break;
+ case 0x10: /* Move from HI/LO */
+ case 0x12:
+ gen_HILO(ctx, op1 | EXT_SPECIAL, rd);
+ break;
+ case 0x11:
+ case 0x13: /* Move to HI/LO */
+ gen_HILO(ctx, op1 | EXT_SPECIAL, rs);
+ break;
+ case 0x0C: /* SYSCALL */
+ generate_exception(ctx, EXCP_SYSCALL);
+ break;
+ case 0x0D: /* BREAK */
+ generate_exception(ctx, EXCP_BREAK);
+ break;
+ case 0x0F: /* SYNC */
+ /* Treat as a noop */
+ break;
+ case 0x05: /* Pmon entry point */
+ gen_op_pmon((ctx->opcode >> 6) & 0x1F);
+ break;
+
+ case 0x01: /* MOVCI */
+#if defined (MIPS_HAS_MOVCI)
+ /* XXX */
+#else
+ /* Not implemented */
+ generate_exception_err (ctx, EXCP_CpU, 1);
+#endif
+ break;
+
+#if defined (TARGET_MIPS64)
+ case 0x14: /* MIPS64 specific opcodes */
+ case 0x16:
+ case 0x17:
+ case 0x1C ... 0x1F:
+ case 0x2C ... 0x2F:
+ case 0x37:
+ case 0x39 ... 0x3B:
+ case 0x3E ... 0x3F:
+#endif
+ default: /* Invalid */
+ MIPS_INVAL("special");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case 0x1C: /* Special2 opcode */
+ op1 = ctx->opcode & 0x3F;
+ switch (op1) {
+#if defined (MIPS_USES_R4K_EXT)
+ /* Those instructions are not part of MIPS32 core */
+ case 0x00 ... 0x01: /* Multiply and add/sub */
+ case 0x04 ... 0x05:
+ gen_muldiv(ctx, op1 | EXT_SPECIAL2, rs, rt);
+ break;
+ case 0x02: /* MUL */
+ gen_arith(ctx, op1 | EXT_SPECIAL2, rd, rs, rt);
+ break;
+ case 0x20 ... 0x21: /* CLO / CLZ */
+ gen_cl(ctx, op1 | EXT_SPECIAL2, rd, rs);
+ break;
+#endif
+ case 0x3F: /* SDBBP */
+ /* XXX: not clear which exception should be raised
+ * when in debug mode...
+ */
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_DBp);
+ } else {
+ generate_exception(ctx, EXCP_DBp);
+ }
+ /* Treat as a noop */
+ break;
+ default: /* Invalid */
+ MIPS_INVAL("special2");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case 0x01: /* B REGIMM opcode */
+ op1 = ((ctx->opcode >> 16) & 0x1F);
+ switch (op1) {
+ case 0x00 ... 0x03: /* REGIMM branches */
+ case 0x10 ... 0x13:
+ gen_compute_branch(ctx, op1 | EXT_REGIMM, rs, -1, imm << 2);
+ return;
+ case 0x08 ... 0x0C: /* Traps */
+ case 0x0E:
+ gen_trap(ctx, op1 | EXT_REGIMM, rs, -1, imm);
+ break;
+ default: /* Invalid */
+ MIPS_INVAL("REGIMM");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case 0x10: /* CP0 opcode */
+ op1 = ((ctx->opcode >> 21) & 0x1F);
+ switch (op1) {
+ case 0x00:
+ case 0x04:
+ gen_cp0(ctx, op1 | EXT_CP0, rt, rd);
+ break;
+ default:
+ gen_cp0(ctx, (ctx->opcode & 0x3F) | EXT_CP0, rt, rd);
+ break;
+ }
+ break;
+ case 0x08 ... 0x0F: /* Arithmetic with immediate opcode */
+ gen_arith_imm(ctx, op, rt, rs, imm);
+ break;
+ case 0x02 ... 0x03: /* Jump */
+ offset = (int32_t)(ctx->opcode & 0x03FFFFFF) << 2;
+ gen_compute_branch(ctx, op, rs, rt, offset);
+ return;
+ case 0x04 ... 0x07: /* Branch */
+ case 0x14 ... 0x17:
+ gen_compute_branch(ctx, op, rs, rt, imm << 2);
+ return;
+ case 0x20 ... 0x2E: /* Load and stores */
+ case 0x30:
+ case 0x38:
+ gen_ldst(ctx, op, rt, rs, imm);
+ break;
+ case 0x2F: /* Cache operation */
+ /* Treat as a noop */
+ break;
+ case 0x33: /* Prefetch */
+ /* Treat as a noop */
+ break;
+ case 0x3F: /* HACK */
+ break;
+
+ /* Floating point. */
+ case 0x31: /* LWC1 */
+ case 0x35: /* LDC1 */
+ case 0x39: /* SWC1 */
+ case 0x3D: /* SDC1 */
+#if defined(MIPS_USES_FPU)
+ gen_op_cp1_enabled();
+ gen_flt_ldst(ctx, op, rt, rs, imm);
+#else
+ generate_exception_err(ctx, EXCP_CpU, 1);
+#endif
+ break;
+
+ case 0x11: /* CP1 opcode */
+#if defined(MIPS_USES_FPU)
+ gen_op_cp1_enabled();
+ op1 = ((ctx->opcode >> 21) & 0x1F);
+ switch (op1) {
+ case 0x00: /* mfc1 */
+ case 0x02: /* cfc1 */
+ case 0x04: /* mtc1 */
+ case 0x06: /* ctc1 */
+ gen_cp1(ctx, op1 | EXT_CP1, rt, rd);
+ break;
+ case 0x08: /* bc */
+ gen_compute_branch1(ctx, rt, imm << 2);
+ return;
+ case 0x10: /* 16: fmt=single fp */
+ case 0x11: /* 17: fmt=double fp */
+ case 0x14: /* 20: fmt=32bit fixed */
+ case 0x15: /* 21: fmt=64bit fixed */
+ gen_farith(ctx, op1, rt, rd, sa, ctx->opcode & 0x3f);
+ break;
+ default:
+ generate_exception_err(ctx, EXCP_RI, 1);
+ break;
+ }
+ break;
+#else
+ generate_exception_err(ctx, EXCP_CpU, 1);
+#endif
+ break;
+
+ /* COP2. */
+ case 0x32: /* LWC2 */
+ case 0x36: /* LDC2 */
+ case 0x3A: /* SWC2 */
+ case 0x3E: /* SDC2 */
+ case 0x12: /* CP2 opcode */
+ /* Not implemented */
+ generate_exception_err(ctx, EXCP_CpU, 2);
+ break;
+
+ case 0x13: /* CP3 opcode */
+ /* Not implemented */
+ generate_exception_err(ctx, EXCP_CpU, 3);
+ break;
+
+#if defined (TARGET_MIPS64)
+ case 0x18 ... 0x1B:
+ case 0x27:
+ case 0x34:
+ case 0x37:
+ /* MIPS64 opcodes */
+#endif
+#if defined (MIPS_HAS_JALX)
+ case 0x1D:
+ /* JALX: not implemented */
+#endif
+ case 0x1E:
+ /* ASE specific */
+ default: /* Invalid */
+ MIPS_INVAL("");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ int hflags = ctx->hflags;
+ /* Branches completion */
+ ctx->hflags &= ~MIPS_HFLAG_BMASK;
+ ctx->bstate = BS_BRANCH;
+ save_cpu_state(ctx, 0);
+ switch (hflags & MIPS_HFLAG_BMASK) {
+ case MIPS_HFLAG_B:
+ /* unconditional branch */
+ MIPS_DEBUG("unconditional branch");
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ break;
+ case MIPS_HFLAG_BL:
+ /* blikely taken case */
+ MIPS_DEBUG("blikely branch taken");
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ break;
+ case MIPS_HFLAG_BC:
+ /* Conditional branch */
+ MIPS_DEBUG("conditional branch");
+ {
+ int l1;
+ l1 = gen_new_label();
+ gen_op_jnz_T2(l1);
+ gen_goto_tb(ctx, 1, ctx->pc + 4);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ }
+ break;
+ case MIPS_HFLAG_BR:
+ /* unconditional branch to register */
+ MIPS_DEBUG("branch to register");
+ gen_op_breg();
+ break;
+ default:
+ MIPS_DEBUG("unknown branch");
+ break;
+ }
+ }
+}
+
+int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx, *ctxp = &ctx;
+ target_ulong pc_start;
+ uint16_t *gen_opc_end;
+ int j, lj = -1;
+
+ if (search_pc && loglevel)
+ fprintf (logfile, "search pc %d\n", search_pc);
+
+ pc_start = tb->pc;
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ nb_gen_labels = 0;
+ ctx.pc = pc_start;
+ ctx.saved_pc = -1;
+ ctx.tb = tb;
+ ctx.bstate = BS_NONE;
+ /* Restore delay slot state from the tb context. */
+ ctx.hflags = tb->flags;
+ ctx.saved_hflags = ctx.hflags;
+ if (ctx.hflags & MIPS_HFLAG_BR) {
+ gen_op_restore_breg_target();
+ } else if (ctx.hflags & MIPS_HFLAG_B) {
+ ctx.btarget = env->btarget;
+ } else if (ctx.hflags & MIPS_HFLAG_BMASK) {
+ /* If we are in the delay slot of a conditional branch,
+ * restore the branch condition from env->bcond to T2
+ */
+ ctx.btarget = env->btarget;
+ gen_op_restore_bcond();
+ }
+#if defined(CONFIG_USER_ONLY)
+ ctx.mem_idx = 0;
+#else
+ ctx.mem_idx = !((ctx.hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
+#endif
+ ctx.CP0_Status = env->CP0_Status;
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_CPU) {
+ fprintf(logfile, "------------------------------------------------\n");
+ /* FIXME: This may print out stale hflags from env... */
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM)
+ fprintf(logfile, "\ntb %p super %d cond %04x\n",
+ tb, ctx.mem_idx, ctx.hflags);
+#endif
+ while (ctx.bstate == BS_NONE && gen_opc_ptr < gen_opc_end) {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == ctx.pc) {
+ save_cpu_state(ctxp, 1);
+ ctx.bstate = BS_BRANCH;
+ gen_op_debug();
+ goto done_generating;
+ }
+ }
+ }
+
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+ gen_opc_pc[lj] = ctx.pc;
+ gen_opc_hflags[lj] = ctx.hflags & MIPS_HFLAG_BMASK;
+ gen_opc_instr_start[lj] = 1;
+ }
+ ctx.opcode = ldl_code(ctx.pc);
+ decode_opc(&ctx);
+ ctx.pc += 4;
+
+ if (env->singlestep_enabled)
+ break;
+
+ if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+
+#if defined (MIPS_SINGLE_STEP)
+ break;
+#endif
+ }
+ if (env->singlestep_enabled) {
+ save_cpu_state(ctxp, ctx.bstate == BS_NONE);
+ gen_op_debug();
+ goto done_generating;
+ }
+ else if (ctx.bstate != BS_BRANCH && ctx.bstate != BS_EXCP) {
+ save_cpu_state(ctxp, 0);
+ gen_goto_tb(&ctx, 0, ctx.pc);
+ }
+ gen_op_reset_T0();
+ /* Generate the return instruction */
+ gen_op_exit_tb();
+done_generating:
+ *gen_opc_ptr = INDEX_op_end;
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ tb->size = 0;
+ } else {
+ tb->size = ctx.pc - pc_start;
+ }
+#ifdef DEBUG_DISAS
+#if defined MIPS_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM)
+ fprintf(logfile, "\n");
+#endif
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+ target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
+ fprintf(logfile, "\n");
+ }
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+ if (loglevel & CPU_LOG_TB_CPU) {
+ fprintf(logfile, "---------------- %d %08x\n", ctx.bstate, ctx.hflags);
+ }
+#endif
+
+ return 0;
+}
+
+int gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
+
+#ifdef MIPS_USES_FPU
+void fpu_dump_state(CPUState *env, FILE *f,
+ int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ int i;
+
+# define printfpr(fp) do { \
+ fpu_fprintf(f, "w:%08x d:%08lx%08lx fd:%g fs:%g\n", \
+ (fp)->w[FP_ENDIAN_IDX], (fp)->w[0], (fp)->w[1], (fp)->fd, (fp)->fs[FP_ENDIAN_IDX]); \
+ } while(0)
+
+ fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d\n",
+ env->fcr0, env->fcr31,
+ (env->CP0_Status & (1<<CP0St_FR)) != 0);
+ fpu_fprintf(f, "FT0: "); printfpr(&env->ft0);
+ fpu_fprintf(f, "FT1: "); printfpr(&env->ft1);
+ fpu_fprintf(f, "FT2: "); printfpr(&env->ft2);
+ for(i=0; i < 32; i+=2) {
+ fpu_fprintf(f, "f%02d: ", i);
+ printfpr(FPR(env, i));
+ }
+
+#undef printfpr
+}
+
+void dump_fpu(CPUState *env)
+{
+ if (loglevel) {
+ fprintf(logfile, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
+ env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
+ fpu_dump_state(env, logfile, fprintf, 0);
+ }
+}
+#endif /* MIPS_USES_FPU */
+
+void cpu_dump_state (CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ uint32_t c0_status;
+ int i;
+
+ cpu_fprintf(f, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
+ env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
+ for (i = 0; i < 32; i++) {
+ if ((i & 3) == 0)
+ cpu_fprintf(f, "GPR%02d:", i);
+ cpu_fprintf(f, " %s %08x", regnames[i], env->gpr[i]);
+ if ((i & 3) == 3)
+ cpu_fprintf(f, "\n");
+ }
+
+ c0_status = env->CP0_Status;
+ if (env->hflags & MIPS_HFLAG_UM)
+ c0_status |= (1 << CP0St_UM);
+ if (env->hflags & MIPS_HFLAG_ERL)
+ c0_status |= (1 << CP0St_ERL);
+ if (env->hflags & MIPS_HFLAG_EXL)
+ c0_status |= (1 << CP0St_EXL);
+
+ cpu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x%08x\n",
+ c0_status, env->CP0_Cause, env->CP0_EPC);
+ cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%08x\n",
+ env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr);
+#ifdef MIPS_USES_FPU
+ fpu_dump_state(env, f, cpu_fprintf, flags);
+#endif
+}
+
+CPUMIPSState *cpu_mips_init (void)
+{
+ CPUMIPSState *env;
+
+ env = qemu_mallocz(sizeof(CPUMIPSState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ tlb_flush(env, 1);
+ /* Minimal init */
+ env->PC = 0xBFC00000;
+#if defined (MIPS_USES_R4K_TLB)
+ env->CP0_random = MIPS_TLB_NB - 1;
+#endif
+ env->CP0_Wired = 0;
+ env->CP0_Config0 = MIPS_CONFIG0;
+#if defined (MIPS_CONFIG1)
+ env->CP0_Config1 = MIPS_CONFIG1;
+#endif
+#if defined (MIPS_CONFIG2)
+ env->CP0_Config2 = MIPS_CONFIG2;
+#endif
+#if defined (MIPS_CONFIG3)
+ env->CP0_Config3 = MIPS_CONFIG3;
+#endif
+ env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV);
+ env->CP0_WatchLo = 0;
+ env->hflags = MIPS_HFLAG_ERL;
+ /* Count register increments in debug mode, EJTAG version 1 */
+ env->CP0_Debug = (1 << CP0DB_CNT) | (0x1 << CP0DB_VER);
+ env->CP0_PRid = MIPS_CPU;
+ env->exception_index = EXCP_NONE;
+#if defined(CONFIG_USER_ONLY)
+ env->hflags |= MIPS_HFLAG_UM;
+#endif
+#ifdef MIPS_USES_FPU
+ env->fcr0 = MIPS_FCR0;
+#endif
+ return env;
+}
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
new file mode 100644
index 0000000..88d9135
--- /dev/null
+++ b/target-ppc/cpu.h
@@ -0,0 +1,1025 @@
+/*
+ * PowerPC emulation cpu definitions for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if !defined (__CPU_PPC_H__)
+#define __CPU_PPC_H__
+
+#include "config.h"
+
+#define TARGET_LONG_BITS 32
+
+#include "cpu-defs.h"
+
+#include <setjmp.h>
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+/* XXX: this should be tunable: PowerPC 601 & 64 bits PowerPC
+ * have different cache line sizes
+ */
+#define ICACHE_LINE_SIZE 32
+#define DCACHE_LINE_SIZE 32
+
+/* XXX: put this in a common place */
+#define likely(x) __builtin_expect(!!(x), 1)
+
+/*****************************************************************************/
+/* PVR definitions for most known PowerPC */
+enum {
+ /* PowerPC 401 cores */
+ CPU_PPC_401A1 = 0x00210000,
+ CPU_PPC_401B2 = 0x00220000,
+ CPU_PPC_401C2 = 0x00230000,
+ CPU_PPC_401D2 = 0x00240000,
+ CPU_PPC_401E2 = 0x00250000,
+ CPU_PPC_401F2 = 0x00260000,
+ CPU_PPC_401G2 = 0x00270000,
+ CPU_PPC_IOP480 = 0x40100000,
+ /* PowerPC 403 cores */
+ CPU_PPC_403GA = 0x00200000,
+ CPU_PPC_403GB = 0x00200100,
+ CPU_PPC_403GC = 0x00200200,
+ CPU_PPC_403GCX = 0x00201400,
+ /* PowerPC 405 cores */
+ CPU_PPC_405 = 0x40110000,
+ CPU_PPC_405EP = 0x51210000,
+ CPU_PPC_405GPR = 0x50910000,
+ CPU_PPC_405D2 = 0x20010000,
+ CPU_PPC_405D4 = 0x41810000,
+ CPU_PPC_NPE405H = 0x41410000,
+ CPU_PPC_NPE405L = 0x41610000,
+#if 0
+ CPU_PPC_STB02 = xxx,
+#endif
+ CPU_PPC_STB03 = 0x40310000,
+#if 0
+ CPU_PPC_STB04 = xxx,
+#endif
+ CPU_PPC_STB25 = 0x51510000,
+#if 0
+ CPU_PPC_STB130 = xxx,
+#endif
+ /* PowerPC 440 cores */
+ CPU_PPC_440EP = 0x42220000,
+ CPU_PPC_440GP = 0x40120400,
+ CPU_PPC_440GX = 0x51B20000,
+ /* PowerPC MPC 8xx cores */
+ CPU_PPC_8540 = 0x80200000,
+ CPU_PPC_8xx = 0x00500000,
+ CPU_PPC_8240 = 0x00810100,
+ CPU_PPC_8245 = 0x00811014,
+ /* PowerPC 6xx cores */
+ CPU_PPC_601 = 0x00010000,
+ CPU_PPC_602 = 0x00050000,
+ CPU_PPC_603 = 0x00030000,
+ CPU_PPC_603E = 0x00060000,
+ CPU_PPC_603EV = 0x00070000,
+ CPU_PPC_603R = 0x00071000,
+ CPU_PPC_G2 = 0x80810000,
+ CPU_PPC_G2LE = 0x80820000,
+ CPU_PPC_604 = 0x00040000,
+ CPU_PPC_604E = 0x00090000,
+ CPU_PPC_604R = 0x000a0000,
+ /* PowerPC 74x/75x cores (aka G3) */
+ CPU_PPC_74x = 0x00080000,
+ CPU_PPC_755 = 0x00083000,
+ CPU_PPC_74xP = 0x10080000,
+ CPU_PPC_750CXE22 = 0x00082202,
+ CPU_PPC_750CXE24 = 0x00082214,
+ CPU_PPC_750CXE24b = 0x00083214,
+ CPU_PPC_750CXE31 = 0x00083211,
+ CPU_PPC_750CXE31b = 0x00083311,
+#define CPU_PPC_750CXE CPU_PPC_750CXE31b
+ CPU_PPC_750FX = 0x70000000,
+ CPU_PPC_750GX = 0x70020000,
+ /* PowerPC 74xx cores (aka G4) */
+ CPU_PPC_7400 = 0x000C0000,
+ CPU_PPC_7410 = 0x800C0000,
+ CPU_PPC_7441 = 0x80000200,
+ CPU_PPC_7450 = 0x80000000,
+ CPU_PPC_7451 = 0x80000203,
+ CPU_PPC_7455 = 0x80010000,
+ CPU_PPC_7457 = 0x80020000,
+ CPU_PPC_7457A = 0x80030000,
+ /* 64 bits PowerPC */
+ CPU_PPC_620 = 0x00140000,
+ CPU_PPC_630 = 0x00400000,
+ CPU_PPC_631 = 0x00410000,
+ CPU_PPC_POWER4 = 0x00350000,
+ CPU_PPC_POWER4P = 0x00380000,
+ CPU_PPC_POWER5 = 0x003A0000,
+ CPU_PPC_POWER5P = 0x003B0000,
+ CPU_PPC_970 = 0x00390000,
+ CPU_PPC_970FX = 0x003C0000,
+ CPU_PPC_RS64 = 0x00330000,
+ CPU_PPC_RS64II = 0x00340000,
+ CPU_PPC_RS64III = 0x00360000,
+ CPU_PPC_RS64IV = 0x00370000,
+ /* Original POWER */
+ /* XXX: should be POWER (RIOS), RSC3308, RSC4608,
+ * POWER2 (RIOS2) & RSC2 (P2SC) here
+ */
+#if 0
+ CPU_POWER = xxx,
+#endif
+#if 0
+ CPU_POWER2 = xxx,
+#endif
+};
+
+/* System version register (used on MPC 8xx) */
+enum {
+ PPC_SVR_8540 = 0x80300000,
+ PPC_SVR_8541E = 0x807A0000,
+ PPC_SVR_8555E = 0x80790000,
+ PPC_SVR_8560 = 0x80700000,
+};
+
+/*****************************************************************************/
+/* Instruction types */
+enum {
+ PPC_NONE = 0x00000000,
+ /* integer operations instructions */
+ /* flow control instructions */
+ /* virtual memory instructions */
+ /* ld/st with reservation instructions */
+ /* cache control instructions */
+ /* spr/msr access instructions */
+ PPC_INSNS_BASE = 0x00000001,
+#define PPC_INTEGER PPC_INSNS_BASE
+#define PPC_FLOW PPC_INSNS_BASE
+#define PPC_MEM PPC_INSNS_BASE
+#define PPC_RES PPC_INSNS_BASE
+#define PPC_CACHE PPC_INSNS_BASE
+#define PPC_MISC PPC_INSNS_BASE
+ /* floating point operations instructions */
+ PPC_FLOAT = 0x00000002,
+ /* more floating point operations instructions */
+ PPC_FLOAT_EXT = 0x00000004,
+ /* external control instructions */
+ PPC_EXTERN = 0x00000008,
+ /* segment register access instructions */
+ PPC_SEGMENT = 0x00000010,
+ /* Optional cache control instructions */
+ PPC_CACHE_OPT = 0x00000020,
+ /* Optional floating point op instructions */
+ PPC_FLOAT_OPT = 0x00000040,
+ /* Optional memory control instructions */
+ PPC_MEM_TLBIA = 0x00000080,
+ PPC_MEM_TLBIE = 0x00000100,
+ PPC_MEM_TLBSYNC = 0x00000200,
+ /* eieio & sync */
+ PPC_MEM_SYNC = 0x00000400,
+ /* PowerPC 6xx TLB management instructions */
+ PPC_6xx_TLB = 0x00000800,
+ /* Altivec support */
+ PPC_ALTIVEC = 0x00001000,
+ /* Time base support */
+ PPC_TB = 0x00002000,
+ /* Embedded PowerPC dedicated instructions */
+ PPC_4xx_COMMON = 0x00004000,
+ /* PowerPC 40x exception model */
+ PPC_40x_EXCP = 0x00008000,
+ /* PowerPC 40x specific instructions */
+ PPC_40x_SPEC = 0x00010000,
+ /* PowerPC 405 Mac instructions */
+ PPC_405_MAC = 0x00020000,
+ /* PowerPC 440 specific instructions */
+ PPC_440_SPEC = 0x00040000,
+ /* Specific extensions */
+ /* Power-to-PowerPC bridge (601) */
+ PPC_POWER_BR = 0x00080000,
+ /* PowerPC 602 specific */
+ PPC_602_SPEC = 0x00100000,
+ /* Deprecated instructions */
+ /* Original POWER instruction set */
+ PPC_POWER = 0x00200000,
+ /* POWER2 instruction set extension */
+ PPC_POWER2 = 0x00400000,
+ /* Power RTC support */
+ PPC_POWER_RTC = 0x00800000,
+ /* 64 bits PowerPC instructions */
+ /* 64 bits PowerPC instruction set */
+ PPC_64B = 0x01000000,
+ /* 64 bits hypervisor extensions */
+ PPC_64H = 0x02000000,
+ /* 64 bits PowerPC "bridge" features */
+ PPC_64_BRIDGE = 0x04000000,
+};
+
+/* CPU run-time flags (MMU and exception model) */
+enum {
+ /* MMU model */
+#define PPC_FLAGS_MMU_MASK (0x0000000F)
+ /* Standard 32 bits PowerPC MMU */
+ PPC_FLAGS_MMU_32B = 0x00000000,
+ /* Standard 64 bits PowerPC MMU */
+ PPC_FLAGS_MMU_64B = 0x00000001,
+ /* PowerPC 601 MMU */
+ PPC_FLAGS_MMU_601 = 0x00000002,
+ /* PowerPC 6xx MMU with software TLB */
+ PPC_FLAGS_MMU_SOFT_6xx = 0x00000003,
+ /* PowerPC 4xx MMU with software TLB */
+ PPC_FLAGS_MMU_SOFT_4xx = 0x00000004,
+ /* PowerPC 403 MMU */
+ PPC_FLAGS_MMU_403 = 0x00000005,
+ /* Exception model */
+#define PPC_FLAGS_EXCP_MASK (0x000000F0)
+ /* Standard PowerPC exception model */
+ PPC_FLAGS_EXCP_STD = 0x00000000,
+ /* PowerPC 40x exception model */
+ PPC_FLAGS_EXCP_40x = 0x00000010,
+ /* PowerPC 601 exception model */
+ PPC_FLAGS_EXCP_601 = 0x00000020,
+ /* PowerPC 602 exception model */
+ PPC_FLAGS_EXCP_602 = 0x00000030,
+ /* PowerPC 603 exception model */
+ PPC_FLAGS_EXCP_603 = 0x00000040,
+ /* PowerPC 604 exception model */
+ PPC_FLAGS_EXCP_604 = 0x00000050,
+ /* PowerPC 7x0 exception model */
+ PPC_FLAGS_EXCP_7x0 = 0x00000060,
+ /* PowerPC 7x5 exception model */
+ PPC_FLAGS_EXCP_7x5 = 0x00000070,
+ /* PowerPC 74xx exception model */
+ PPC_FLAGS_EXCP_74xx = 0x00000080,
+ /* PowerPC 970 exception model */
+ PPC_FLAGS_EXCP_970 = 0x00000090,
+};
+
+#define PPC_MMU(env) (env->flags & PPC_FLAGS_MMU_MASK)
+#define PPC_EXCP(env) (env->flags & PPC_FLAGS_EXCP_MASK)
+
+/*****************************************************************************/
+/* Supported instruction set definitions */
+/* This generates an empty opcode table... */
+#define PPC_INSNS_TODO (PPC_NONE)
+#define PPC_FLAGS_TODO (0x00000000)
+
+/* PowerPC 40x instruction set */
+#define PPC_INSNS_4xx (PPC_INSNS_BASE | PPC_MEM_TLBSYNC | PPC_4xx_COMMON)
+/* PowerPC 401 */
+#define PPC_INSNS_401 (PPC_INSNS_TODO)
+#define PPC_FLAGS_401 (PPC_FLAGS_TODO)
+/* PowerPC 403 */
+#define PPC_INSNS_403 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_MEM_TLBIA | \
+ PPC_40x_EXCP | PPC_40x_SPEC)
+#define PPC_FLAGS_403 (PPC_FLAGS_MMU_403 | PPC_FLAGS_EXCP_40x)
+/* PowerPC 405 */
+#define PPC_INSNS_405 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_CACHE_OPT | \
+ PPC_MEM_TLBIA | PPC_TB | PPC_40x_SPEC | PPC_40x_EXCP | \
+ PPC_405_MAC)
+#define PPC_FLAGS_405 (PPC_FLAGS_MMU_SOFT_4xx | PPC_FLAGS_EXCP_40x)
+/* PowerPC 440 */
+#define PPC_INSNS_440 (PPC_INSNS_4xx | PPC_CACHE_OPT | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define PPC_FLAGS_440 (PPC_FLAGS_TODO)
+/* Non-embedded PowerPC */
+#define PPC_INSNS_COMMON (PPC_INSNS_BASE | PPC_FLOAT | PPC_MEM_SYNC | \
+ PPC_SEGMENT | PPC_MEM_TLBIE)
+/* PowerPC 601 */
+#define PPC_INSNS_601 (PPC_INSNS_COMMON | PPC_EXTERN | PPC_POWER_BR)
+#define PPC_FLAGS_601 (PPC_FLAGS_MMU_601 | PPC_FLAGS_EXCP_601)
+/* PowerPC 602 */
+#define PPC_INSNS_602 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_602 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_602)
+/* PowerPC 603 */
+#define PPC_INSNS_603 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB)
+#define PPC_FLAGS_603 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603)
+/* PowerPC G2 */
+#define PPC_INSNS_G2 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB)
+#define PPC_FLAGS_G2 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603)
+/* PowerPC 604 */
+#define PPC_INSNS_604 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_604 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_604)
+/* PowerPC 740/750 (aka G3) */
+#define PPC_INSNS_7x0 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_7x0 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_7x0)
+/* PowerPC 745/755 */
+#define PPC_INSNS_7x5 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB | PPC_6xx_TLB)
+#define PPC_FLAGS_7x5 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_7x5)
+/* PowerPC 74xx (aka G4) */
+#define PPC_INSNS_74xx (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_ALTIVEC | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_74xx (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_74xx)
+
+/* Default PowerPC will be 604/970 */
+#define PPC_INSNS_PPC32 PPC_INSNS_604
+#define PPC_FLAGS_PPC32 PPC_FLAGS_604
+#if 0
+#define PPC_INSNS_PPC64 PPC_INSNS_970
+#define PPC_FLAGS_PPC64 PPC_FLAGS_970
+#endif
+#define PPC_INSNS_DEFAULT PPC_INSNS_604
+#define PPC_FLAGS_DEFAULT PPC_FLAGS_604
+typedef struct ppc_def_t ppc_def_t;
+
+/*****************************************************************************/
+/* Types used to describe some PowerPC registers */
+typedef struct CPUPPCState CPUPPCState;
+typedef struct opc_handler_t opc_handler_t;
+typedef struct ppc_tb_t ppc_tb_t;
+typedef struct ppc_spr_t ppc_spr_t;
+typedef struct ppc_dcr_t ppc_dcr_t;
+typedef struct ppc_avr_t ppc_avr_t;
+
+/* SPR access micro-ops generations callbacks */
+struct ppc_spr_t {
+ void (*uea_read)(void *opaque, int spr_num);
+ void (*uea_write)(void *opaque, int spr_num);
+ void (*oea_read)(void *opaque, int spr_num);
+ void (*oea_write)(void *opaque, int spr_num);
+ const unsigned char *name;
+};
+
+/* Altivec registers (128 bits) */
+struct ppc_avr_t {
+ uint32_t u[4];
+};
+
+/* Software TLB cache */
+typedef struct ppc_tlb_t ppc_tlb_t;
+struct ppc_tlb_t {
+ /* Physical page number */
+ target_phys_addr_t RPN;
+ /* Virtual page number */
+ target_ulong VPN;
+ /* Page size */
+ target_ulong size;
+ /* Protection bits */
+ int prot;
+ int is_user;
+ uint32_t private;
+ uint32_t flags;
+};
+
+/*****************************************************************************/
+/* Machine state register bits definition */
+#define MSR_SF 63 /* Sixty-four-bit mode */
+#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */
+#define MSR_HV 60 /* hypervisor state */
+#define MSR_VR 25 /* altivec available */
+#define MSR_AP 23 /* Access privilege state on 602 */
+#define MSR_SA 22 /* Supervisor access mode on 602 */
+#define MSR_KEY 19 /* key bit on 603e */
+#define MSR_POW 18 /* Power management */
+#define MSR_WE 18 /* Wait state enable on embedded PowerPC */
+#define MSR_TGPR 17 /* TGPR usage on 602/603 */
+#define MSR_TLB 17 /* TLB on ? */
+#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC */
+#define MSR_ILE 16 /* Interrupt little-endian mode */
+#define MSR_EE 15 /* External interrupt enable */
+#define MSR_PR 14 /* Problem state */
+#define MSR_FP 13 /* Floating point available */
+#define MSR_ME 12 /* Machine check interrupt enable */
+#define MSR_FE0 11 /* Floating point exception mode 0 */
+#define MSR_SE 10 /* Single-step trace enable */
+#define MSR_DWE 10 /* Debug wait enable on 405 */
+#define MSR_BE 9 /* Branch trace enable */
+#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC */
+#define MSR_FE1 8 /* Floating point exception mode 1 */
+#define MSR_AL 7 /* AL bit on POWER */
+#define MSR_IP 6 /* Interrupt prefix */
+#define MSR_IR 5 /* Instruction relocate */
+#define MSR_IS 5 /* Instruction address space on embedded PowerPC */
+#define MSR_DR 4 /* Data relocate */
+#define MSR_DS 4 /* Data address space on embedded PowerPC */
+#define MSR_PE 3 /* Protection enable on 403 */
+#define MSR_EP 3 /* Exception prefix on 601 */
+#define MSR_PX 2 /* Protection exclusive on 403 */
+#define MSR_PMM 2 /* Performance monitor mark on POWER */
+#define MSR_RI 1 /* Recoverable interrupt */
+#define MSR_LE 0 /* Little-endian mode */
+#define msr_sf env->msr[MSR_SF]
+#define msr_isf env->msr[MSR_ISF]
+#define msr_hv env->msr[MSR_HV]
+#define msr_vr env->msr[MSR_VR]
+#define msr_ap env->msr[MSR_AP]
+#define msr_sa env->msr[MSR_SA]
+#define msr_key env->msr[MSR_KEY]
+#define msr_pow env->msr[MSR_POW]
+#define msr_we env->msr[MSR_WE]
+#define msr_tgpr env->msr[MSR_TGPR]
+#define msr_tlb env->msr[MSR_TLB]
+#define msr_ce env->msr[MSR_CE]
+#define msr_ile env->msr[MSR_ILE]
+#define msr_ee env->msr[MSR_EE]
+#define msr_pr env->msr[MSR_PR]
+#define msr_fp env->msr[MSR_FP]
+#define msr_me env->msr[MSR_ME]
+#define msr_fe0 env->msr[MSR_FE0]
+#define msr_se env->msr[MSR_SE]
+#define msr_dwe env->msr[MSR_DWE]
+#define msr_be env->msr[MSR_BE]
+#define msr_de env->msr[MSR_DE]
+#define msr_fe1 env->msr[MSR_FE1]
+#define msr_al env->msr[MSR_AL]
+#define msr_ip env->msr[MSR_IP]
+#define msr_ir env->msr[MSR_IR]
+#define msr_is env->msr[MSR_IS]
+#define msr_dr env->msr[MSR_DR]
+#define msr_ds env->msr[MSR_DS]
+#define msr_pe env->msr[MSR_PE]
+#define msr_ep env->msr[MSR_EP]
+#define msr_px env->msr[MSR_PX]
+#define msr_pmm env->msr[MSR_PMM]
+#define msr_ri env->msr[MSR_RI]
+#define msr_le env->msr[MSR_LE]
+
+/*****************************************************************************/
+/* The whole PowerPC CPU context */
+struct CPUPPCState {
+ /* First are the most commonly used resources
+ * during translated code execution
+ */
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+ /* temporary fixed-point registers
+ * used to emulate 64 bits target on 32 bits hosts
+ */
+ target_ulong t0, t1, t2;
+#endif
+ /* general purpose registers */
+ target_ulong gpr[32];
+ /* LR */
+ target_ulong lr;
+ /* CTR */
+ target_ulong ctr;
+ /* condition register */
+ uint8_t crf[8];
+ /* XER */
+ /* XXX: We use only 5 fields, but we want to keep the structure aligned */
+ uint8_t xer[8];
+ /* Reservation address */
+ target_ulong reserve;
+
+ /* Those ones are used in supervisor mode only */
+ /* machine state register */
+ uint8_t msr[64];
+ /* temporary general purpose registers */
+ target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */
+
+ /* Floating point execution context */
+ /* temporary float registers */
+ float64 ft0;
+ float64 ft1;
+ float64 ft2;
+ float_status fp_status;
+ /* floating point registers */
+ float64 fpr[32];
+ /* floating point status and control register */
+ uint8_t fpscr[8];
+
+ CPU_COMMON
+
+ int halted; /* TRUE if the CPU is in suspend state */
+
+ int access_type; /* when a memory exception occurs, the access
+ type is stored here */
+
+ /* MMU context */
+ /* Address space register */
+ target_ulong asr;
+ /* segment registers */
+ target_ulong sdr1;
+ target_ulong sr[16];
+ /* BATs */
+ int nb_BATs;
+ target_ulong DBAT[2][8];
+ target_ulong IBAT[2][8];
+
+ /* Other registers */
+ /* Special purpose registers */
+ target_ulong spr[1024];
+ /* Altivec registers */
+ ppc_avr_t avr[32];
+ uint32_t vscr;
+
+ /* Internal devices resources */
+ /* Time base and decrementer */
+ ppc_tb_t *tb_env;
+ /* Device control registers */
+ int (*dcr_read)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong *val);
+ int (*dcr_write)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong val);
+ ppc_dcr_t *dcr_env;
+
+ /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
+ int nb_tlb;
+ int nb_ways, last_way;
+ ppc_tlb_t tlb[128];
+ /* Callbacks for specific checks on some implementations */
+ int (*tlb_check_more)(CPUPPCState *env, struct ppc_tlb_t *tlb, int *prot,
+ target_ulong vaddr, int rw, int acc_type,
+ int is_user);
+ /* 403 dedicated access protection registers */
+ target_ulong pb[4];
+
+ /* Those resources are used during exception processing */
+ /* CPU model definition */
+ uint64_t msr_mask;
+ uint32_t flags;
+
+ int exception_index;
+ int error_code;
+ int interrupt_request;
+
+ /* Those resources are used only during code translation */
+ /* Next instruction pointer */
+ target_ulong nip;
+ /* SPR translation callbacks */
+ ppc_spr_t spr_cb[1024];
+ /* opcode handlers */
+ opc_handler_t *opcodes[0x40];
+
+ /* Those resources are used only in Qemu core */
+ jmp_buf jmp_env;
+ int user_mode_only; /* user mode only simulation */
+ uint32_t hflags;
+
+ /* Power management */
+ int power_mode;
+
+ /* temporary hack to handle OSI calls (only used if non NULL) */
+ int (*osi_call)(struct CPUPPCState *env);
+};
+
+/*****************************************************************************/
+CPUPPCState *cpu_ppc_init(void);
+int cpu_ppc_exec(CPUPPCState *s);
+void cpu_ppc_close(CPUPPCState *s);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+struct siginfo;
+int cpu_ppc_signal_handler(int host_signum, struct siginfo *info,
+ void *puc);
+
+void do_interrupt (CPUPPCState *env);
+void cpu_loop_exit(void);
+
+void dump_stack (CPUPPCState *env);
+
+target_ulong do_load_ibatu (CPUPPCState *env, int nr);
+target_ulong do_load_ibatl (CPUPPCState *env, int nr);
+void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value);
+void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value);
+target_ulong do_load_dbatu (CPUPPCState *env, int nr);
+target_ulong do_load_dbatl (CPUPPCState *env, int nr);
+void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value);
+void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value);
+
+target_ulong do_load_nip (CPUPPCState *env);
+void do_store_nip (CPUPPCState *env, target_ulong value);
+target_ulong do_load_sdr1 (CPUPPCState *env);
+void do_store_sdr1 (CPUPPCState *env, target_ulong value);
+target_ulong do_load_asr (CPUPPCState *env);
+void do_store_asr (CPUPPCState *env, target_ulong value);
+target_ulong do_load_sr (CPUPPCState *env, int srnum);
+void do_store_sr (CPUPPCState *env, int srnum, target_ulong value);
+uint32_t do_load_cr (CPUPPCState *env);
+void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask);
+uint32_t do_load_xer (CPUPPCState *env);
+void do_store_xer (CPUPPCState *env, uint32_t value);
+target_ulong do_load_msr (CPUPPCState *env);
+void do_store_msr (CPUPPCState *env, target_ulong value);
+float64 do_load_fpscr (CPUPPCState *env);
+void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask);
+
+void do_compute_hflags (CPUPPCState *env);
+
+int ppc_find_by_name (const unsigned char *name, ppc_def_t **def);
+int ppc_find_by_pvr (uint32_t apvr, ppc_def_t **def);
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def);
+
+/* Time-base and decrementer management */
+#ifndef NO_CPU_IO_DEFS
+uint32_t cpu_ppc_load_tbl (CPUPPCState *env);
+uint32_t cpu_ppc_load_tbu (CPUPPCState *env);
+void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value);
+void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value);
+uint32_t cpu_ppc_load_decr (CPUPPCState *env);
+void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value);
+#endif
+
+#define TARGET_PAGE_BITS 12
+#include "cpu-all.h"
+
+/*****************************************************************************/
+/* Registers definitions */
+#define ugpr(n) (env->gpr[n])
+
+#define XER_SO 31
+#define XER_OV 30
+#define XER_CA 29
+#define XER_CMP 8
+#define XER_BC 0
+#define xer_so env->xer[4]
+#define xer_ov env->xer[6]
+#define xer_ca env->xer[2]
+#define xer_cmp env->xer[1]
+#define xer_bc env->xer[0]
+
+/* SPR definitions */
+#define SPR_MQ (0x000)
+#define SPR_XER (0x001)
+#define SPR_601_VRTCU (0x004)
+#define SPR_601_VRTCL (0x005)
+#define SPR_601_UDECR (0x006)
+#define SPR_LR (0x008)
+#define SPR_CTR (0x009)
+#define SPR_DSISR (0x012)
+#define SPR_DAR (0x013)
+#define SPR_601_RTCU (0x014)
+#define SPR_601_RTCL (0x015)
+#define SPR_DECR (0x016)
+#define SPR_SDR1 (0x019)
+#define SPR_SRR0 (0x01A)
+#define SPR_SRR1 (0x01B)
+#define SPR_440_PID (0x030)
+#define SPR_440_DECAR (0x036)
+#define SPR_CSRR0 (0x03A)
+#define SPR_CSRR1 (0x03B)
+#define SPR_440_DEAR (0x03D)
+#define SPR_440_ESR (0x03E)
+#define SPR_440_IVPR (0x03F)
+#define SPR_8xx_EIE (0x050)
+#define SPR_8xx_EID (0x051)
+#define SPR_8xx_NRE (0x052)
+#define SPR_58x_CMPA (0x090)
+#define SPR_58x_CMPB (0x091)
+#define SPR_58x_CMPC (0x092)
+#define SPR_58x_CMPD (0x093)
+#define SPR_58x_ICR (0x094)
+#define SPR_58x_DER (0x094)
+#define SPR_58x_COUNTA (0x096)
+#define SPR_58x_COUNTB (0x097)
+#define SPR_58x_CMPE (0x098)
+#define SPR_58x_CMPF (0x099)
+#define SPR_58x_CMPG (0x09A)
+#define SPR_58x_CMPH (0x09B)
+#define SPR_58x_LCTRL1 (0x09C)
+#define SPR_58x_LCTRL2 (0x09D)
+#define SPR_58x_ICTRL (0x09E)
+#define SPR_58x_BAR (0x09F)
+#define SPR_VRSAVE (0x100)
+#define SPR_USPRG0 (0x100)
+#define SPR_USPRG4 (0x104)
+#define SPR_USPRG5 (0x105)
+#define SPR_USPRG6 (0x106)
+#define SPR_USPRG7 (0x107)
+#define SPR_VTBL (0x10C)
+#define SPR_VTBU (0x10D)
+#define SPR_SPRG0 (0x110)
+#define SPR_SPRG1 (0x111)
+#define SPR_SPRG2 (0x112)
+#define SPR_SPRG3 (0x113)
+#define SPR_SPRG4 (0x114)
+#define SPR_SCOMC (0x114)
+#define SPR_SPRG5 (0x115)
+#define SPR_SCOMD (0x115)
+#define SPR_SPRG6 (0x116)
+#define SPR_SPRG7 (0x117)
+#define SPR_ASR (0x118)
+#define SPR_EAR (0x11A)
+#define SPR_TBL (0x11C)
+#define SPR_TBU (0x11D)
+#define SPR_SVR (0x11E)
+#define SPR_440_PIR (0x11E)
+#define SPR_PVR (0x11F)
+#define SPR_HSPRG0 (0x130)
+#define SPR_440_DBSR (0x130)
+#define SPR_HSPRG1 (0x131)
+#define SPR_440_DBCR0 (0x134)
+#define SPR_IBCR (0x135)
+#define SPR_440_DBCR1 (0x135)
+#define SPR_DBCR (0x136)
+#define SPR_HDEC (0x136)
+#define SPR_440_DBCR2 (0x136)
+#define SPR_HIOR (0x137)
+#define SPR_MBAR (0x137)
+#define SPR_RMOR (0x138)
+#define SPR_440_IAC1 (0x138)
+#define SPR_HRMOR (0x139)
+#define SPR_440_IAC2 (0x139)
+#define SPR_HSSR0 (0x13A)
+#define SPR_440_IAC3 (0x13A)
+#define SPR_HSSR1 (0x13B)
+#define SPR_440_IAC4 (0x13B)
+#define SPR_LPCR (0x13C)
+#define SPR_440_DAC1 (0x13C)
+#define SPR_LPIDR (0x13D)
+#define SPR_DABR2 (0x13D)
+#define SPR_440_DAC2 (0x13D)
+#define SPR_440_DVC1 (0x13E)
+#define SPR_440_DVC2 (0x13F)
+#define SPR_440_TSR (0x150)
+#define SPR_440_TCR (0x154)
+#define SPR_440_IVOR0 (0x190)
+#define SPR_440_IVOR1 (0x191)
+#define SPR_440_IVOR2 (0x192)
+#define SPR_440_IVOR3 (0x193)
+#define SPR_440_IVOR4 (0x194)
+#define SPR_440_IVOR5 (0x195)
+#define SPR_440_IVOR6 (0x196)
+#define SPR_440_IVOR7 (0x197)
+#define SPR_440_IVOR8 (0x198)
+#define SPR_440_IVOR9 (0x199)
+#define SPR_440_IVOR10 (0x19A)
+#define SPR_440_IVOR11 (0x19B)
+#define SPR_440_IVOR12 (0x19C)
+#define SPR_440_IVOR13 (0x19D)
+#define SPR_440_IVOR14 (0x19E)
+#define SPR_440_IVOR15 (0x19F)
+#define SPR_IBAT0U (0x210)
+#define SPR_IBAT0L (0x211)
+#define SPR_IBAT1U (0x212)
+#define SPR_IBAT1L (0x213)
+#define SPR_IBAT2U (0x214)
+#define SPR_IBAT2L (0x215)
+#define SPR_IBAT3U (0x216)
+#define SPR_IBAT3L (0x217)
+#define SPR_DBAT0U (0x218)
+#define SPR_DBAT0L (0x219)
+#define SPR_DBAT1U (0x21A)
+#define SPR_DBAT1L (0x21B)
+#define SPR_DBAT2U (0x21C)
+#define SPR_DBAT2L (0x21D)
+#define SPR_DBAT3U (0x21E)
+#define SPR_DBAT3L (0x21F)
+#define SPR_IBAT4U (0x230)
+#define SPR_IBAT4L (0x231)
+#define SPR_IBAT5U (0x232)
+#define SPR_IBAT5L (0x233)
+#define SPR_IBAT6U (0x234)
+#define SPR_IBAT6L (0x235)
+#define SPR_IBAT7U (0x236)
+#define SPR_IBAT7L (0x237)
+#define SPR_DBAT4U (0x238)
+#define SPR_DBAT4L (0x239)
+#define SPR_DBAT5U (0x23A)
+#define SPR_DBAT5L (0x23B)
+#define SPR_DBAT6U (0x23C)
+#define SPR_DBAT6L (0x23D)
+#define SPR_DBAT7U (0x23E)
+#define SPR_DBAT7L (0x23F)
+#define SPR_440_INV0 (0x370)
+#define SPR_440_INV1 (0x371)
+#define SPR_440_INV2 (0x372)
+#define SPR_440_INV3 (0x373)
+#define SPR_440_IVT0 (0x374)
+#define SPR_440_IVT1 (0x375)
+#define SPR_440_IVT2 (0x376)
+#define SPR_440_IVT3 (0x377)
+#define SPR_440_DNV0 (0x390)
+#define SPR_440_DNV1 (0x391)
+#define SPR_440_DNV2 (0x392)
+#define SPR_440_DNV3 (0x393)
+#define SPR_440_DVT0 (0x394)
+#define SPR_440_DVT1 (0x395)
+#define SPR_440_DVT2 (0x396)
+#define SPR_440_DVT3 (0x397)
+#define SPR_440_DVLIM (0x398)
+#define SPR_440_IVLIM (0x399)
+#define SPR_440_RSTCFG (0x39B)
+#define SPR_440_DCBTRL (0x39C)
+#define SPR_440_DCBTRH (0x39D)
+#define SPR_440_ICBTRL (0x39E)
+#define SPR_440_ICBTRH (0x39F)
+#define SPR_UMMCR0 (0x3A8)
+#define SPR_UPMC1 (0x3A9)
+#define SPR_UPMC2 (0x3AA)
+#define SPR_USIA (0x3AB)
+#define SPR_UMMCR1 (0x3AC)
+#define SPR_UPMC3 (0x3AD)
+#define SPR_UPMC4 (0x3AE)
+#define SPR_USDA (0x3AF)
+#define SPR_40x_ZPR (0x3B0)
+#define SPR_40x_PID (0x3B1)
+#define SPR_440_MMUCR (0x3B2)
+#define SPR_4xx_CCR0 (0x3B3)
+#define SPR_405_IAC3 (0x3B4)
+#define SPR_405_IAC4 (0x3B5)
+#define SPR_405_DVC1 (0x3B6)
+#define SPR_405_DVC2 (0x3B7)
+#define SPR_MMCR0 (0x3B8)
+#define SPR_PMC1 (0x3B9)
+#define SPR_40x_SGR (0x3B9)
+#define SPR_PMC2 (0x3BA)
+#define SPR_40x_DCWR (0x3BA)
+#define SPR_SIA (0x3BB)
+#define SPR_405_SLER (0x3BB)
+#define SPR_MMCR1 (0x3BC)
+#define SPR_405_SU0R (0x3BC)
+#define SPR_PMC3 (0x3BD)
+#define SPR_405_DBCR1 (0x3BD)
+#define SPR_PMC4 (0x3BE)
+#define SPR_SDA (0x3BF)
+#define SPR_403_VTBL (0x3CC)
+#define SPR_403_VTBU (0x3CD)
+#define SPR_DMISS (0x3D0)
+#define SPR_DCMP (0x3D1)
+#define SPR_DHASH1 (0x3D2)
+#define SPR_DHASH2 (0x3D3)
+#define SPR_4xx_ICDBDR (0x3D3)
+#define SPR_IMISS (0x3D4)
+#define SPR_40x_ESR (0x3D4)
+#define SPR_ICMP (0x3D5)
+#define SPR_40x_DEAR (0x3D5)
+#define SPR_RPA (0x3D6)
+#define SPR_40x_EVPR (0x3D6)
+#define SPR_403_CDBCR (0x3D7)
+#define SPR_TCR (0x3D8)
+#define SPR_40x_TSR (0x3D8)
+#define SPR_IBR (0x3DA)
+#define SPR_40x_TCR (0x3DA)
+#define SPR_ESASR (0x3DB)
+#define SPR_40x_PIT (0x3DB)
+#define SPR_403_TBL (0x3DC)
+#define SPR_403_TBU (0x3DD)
+#define SPR_SEBR (0x3DE)
+#define SPR_40x_SRR2 (0x3DE)
+#define SPR_SER (0x3DF)
+#define SPR_40x_SRR3 (0x3DF)
+#define SPR_HID0 (0x3F0)
+#define SPR_40x_DBSR (0x3F0)
+#define SPR_HID1 (0x3F1)
+#define SPR_IABR (0x3F2)
+#define SPR_40x_DBCR0 (0x3F2)
+#define SPR_601_HID2 (0x3F2)
+#define SPR_HID2 (0x3F3)
+#define SPR_440_DBDR (0x3F3)
+#define SPR_40x_IAC1 (0x3F4)
+#define SPR_DABR (0x3F5)
+#define DABR_MASK (~(target_ulong)0x7)
+#define SPR_40x_IAC2 (0x3F5)
+#define SPR_601_HID5 (0x3F5)
+#define SPR_40x_DAC1 (0x3F6)
+#define SPR_40x_DAC2 (0x3F7)
+#define SPR_L2PM (0x3F8)
+#define SPR_750_HID2 (0x3F8)
+#define SPR_L2CR (0x3F9)
+#define SPR_IABR2 (0x3FA)
+#define SPR_40x_DCCR (0x3FA)
+#define SPR_ICTC (0x3FB)
+#define SPR_40x_ICCR (0x3FB)
+#define SPR_THRM1 (0x3FC)
+#define SPR_403_PBL1 (0x3FC)
+#define SPR_SP (0x3FD)
+#define SPR_THRM2 (0x3FD)
+#define SPR_403_PBU1 (0x3FD)
+#define SPR_LT (0x3FE)
+#define SPR_THRM3 (0x3FE)
+#define SPR_FPECR (0x3FE)
+#define SPR_403_PBL2 (0x3FE)
+#define SPR_PIR (0x3FF)
+#define SPR_403_PBU2 (0x3FF)
+#define SPR_601_HID15 (0x3FF)
+
+/* Memory access type :
+ * may be needed for precise access rights control and precise exceptions.
+ */
+enum {
+ /* 1 bit to define user level / supervisor access */
+ ACCESS_USER = 0x00,
+ ACCESS_SUPER = 0x01,
+ /* Type of instruction that generated the access */
+ ACCESS_CODE = 0x10, /* Code fetch access */
+ ACCESS_INT = 0x20, /* Integer load/store access */
+ ACCESS_FLOAT = 0x30, /* floating point load/store access */
+ ACCESS_RES = 0x40, /* load/store with reservation */
+ ACCESS_EXT = 0x50, /* external access */
+ ACCESS_CACHE = 0x60, /* Cache manipulation */
+};
+
+/*****************************************************************************/
+/* Exceptions */
+#define EXCP_NONE -1
+/* PowerPC hardware exceptions : exception vectors defined in PowerPC book 3 */
+#define EXCP_RESET 0x0100 /* System reset */
+#define EXCP_MACHINE_CHECK 0x0200 /* Machine check exception */
+#define EXCP_DSI 0x0300 /* Data storage exception */
+#define EXCP_DSEG 0x0380 /* Data segment exception */
+#define EXCP_ISI 0x0400 /* Instruction storage exception */
+#define EXCP_ISEG 0x0480 /* Instruction segment exception */
+#define EXCP_EXTERNAL 0x0500 /* External interruption */
+#define EXCP_ALIGN 0x0600 /* Alignment exception */
+#define EXCP_PROGRAM 0x0700 /* Program exception */
+#define EXCP_NO_FP 0x0800 /* Floating point unavailable exception */
+#define EXCP_DECR 0x0900 /* Decrementer exception */
+#define EXCP_HDECR 0x0980 /* Hypervisor decrementer exception */
+#define EXCP_SYSCALL 0x0C00 /* System call */
+#define EXCP_TRACE 0x0D00 /* Trace exception */
+#define EXCP_PERF 0x0F00 /* Performance monitor exception */
+/* Exceptions defined in PowerPC 32 bits programming environment manual */
+#define EXCP_FP_ASSIST 0x0E00 /* Floating-point assist */
+/* Implementation specific exceptions */
+/* 40x exceptions */
+#define EXCP_40x_PIT 0x1000 /* Programmable interval timer interrupt */
+#define EXCP_40x_FIT 0x1010 /* Fixed interval timer interrupt */
+#define EXCP_40x_WATCHDOG 0x1020 /* Watchdog timer exception */
+#define EXCP_40x_DTLBMISS 0x1100 /* Data TLB miss exception */
+#define EXCP_40x_ITLBMISS 0x1200 /* Instruction TLB miss exception */
+#define EXCP_40x_DEBUG 0x2000 /* Debug exception */
+/* 405 specific exceptions */
+#define EXCP_405_APU 0x0F20 /* APU unavailable exception */
+/* TLB assist exceptions (602/603) */
+#define EXCP_I_TLBMISS 0x1000 /* Instruction TLB miss */
+#define EXCP_DL_TLBMISS 0x1100 /* Data load TLB miss */
+#define EXCP_DS_TLBMISS 0x1200 /* Data store TLB miss */
+/* Breakpoint exceptions (602/603/604/620/740/745/750/755...) */
+#define EXCP_IABR 0x1300 /* Instruction address breakpoint */
+#define EXCP_SMI 0x1400 /* System management interrupt */
+/* Altivec related exceptions */
+#define EXCP_VPU 0x0F20 /* VPU unavailable exception */
+/* 601 specific exceptions */
+#define EXCP_601_IO 0x0600 /* IO error exception */
+#define EXCP_601_RUNM 0x2000 /* Run mode exception */
+/* 602 specific exceptions */
+#define EXCP_602_WATCHDOG 0x1500 /* Watchdog exception */
+#define EXCP_602_EMUL 0x1600 /* Emulation trap exception */
+/* G2 specific exceptions */
+#define EXCP_G2_CRIT 0x0A00 /* Critical interrupt */
+/* MPC740/745/750 & IBM 750 specific exceptions */
+#define EXCP_THRM 0x1700 /* Thermal management interrupt */
+/* 74xx specific exceptions */
+#define EXCP_74xx_VPUA 0x1600 /* VPU assist exception */
+/* 970FX specific exceptions */
+#define EXCP_970_SOFTP 0x1500 /* Soft patch exception */
+#define EXCP_970_MAINT 0x1600 /* Maintenance exception */
+#define EXCP_970_THRM 0x1800 /* Thermal exception */
+#define EXCP_970_VPUA 0x1700 /* VPU assist exception */
+/* End of exception vectors area */
+#define EXCP_PPC_MAX 0x4000
+/* Qemu exceptions: special cases we want to stop translation */
+#define EXCP_MTMSR 0x11000 /* mtmsr instruction: */
+ /* may change privilege level */
+#define EXCP_BRANCH 0x11001 /* branch instruction */
+#define EXCP_SYSCALL_USER 0x12000 /* System call in user mode only */
+#define EXCP_INTERRUPT_CRITICAL 0x13000 /* critical IRQ */
+
+/* Error codes */
+enum {
+ /* Exception subtypes for EXCP_ALIGN */
+ EXCP_ALIGN_FP = 0x01, /* FP alignment exception */
+ EXCP_ALIGN_LST = 0x02, /* Unaligned mult/extern load/store */
+ EXCP_ALIGN_LE = 0x03, /* Multiple little-endian access */
+ EXCP_ALIGN_PROT = 0x04, /* Access cross protection boundary */
+ EXCP_ALIGN_BAT = 0x05, /* Access cross a BAT/seg boundary */
+ EXCP_ALIGN_CACHE = 0x06, /* Impossible dcbz access */
+ /* Exception subtypes for EXCP_PROGRAM */
+ /* FP exceptions */
+ EXCP_FP = 0x10,
+ EXCP_FP_OX = 0x01, /* FP overflow */
+ EXCP_FP_UX = 0x02, /* FP underflow */
+ EXCP_FP_ZX = 0x03, /* FP divide by zero */
+ EXCP_FP_XX = 0x04, /* FP inexact */
+ EXCP_FP_VXNAN = 0x05, /* FP invalid SNaN op */
+ EXCP_FP_VXISI = 0x06, /* FP invalid infinite substraction */
+ EXCP_FP_VXIDI = 0x07, /* FP invalid infinite divide */
+ EXCP_FP_VXZDZ = 0x08, /* FP invalid zero divide */
+ EXCP_FP_VXIMZ = 0x09, /* FP invalid infinite * zero */
+ EXCP_FP_VXVC = 0x0A, /* FP invalid compare */
+ EXCP_FP_VXSOFT = 0x0B, /* FP invalid operation */
+ EXCP_FP_VXSQRT = 0x0C, /* FP invalid square root */
+ EXCP_FP_VXCVI = 0x0D, /* FP invalid integer conversion */
+ /* Invalid instruction */
+ EXCP_INVAL = 0x20,
+ EXCP_INVAL_INVAL = 0x01, /* Invalid instruction */
+ EXCP_INVAL_LSWX = 0x02, /* Invalid lswx instruction */
+ EXCP_INVAL_SPR = 0x03, /* Invalid SPR access */
+ EXCP_INVAL_FP = 0x04, /* Unimplemented mandatory fp instr */
+ /* Privileged instruction */
+ EXCP_PRIV = 0x30,
+ EXCP_PRIV_OPC = 0x01,
+ EXCP_PRIV_REG = 0x02,
+ /* Trap */
+ EXCP_TRAP = 0x40,
+};
+
+/*****************************************************************************/
+
+#endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/exec.h b/target-ppc/exec.h
new file mode 100644
index 0000000..3ef0968
--- /dev/null
+++ b/target-ppc/exec.h
@@ -0,0 +1,90 @@
+/*
+ * PowerPC emulation definitions for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if !defined (__PPC_H__)
+#define __PPC_H__
+
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#define TARGET_LONG_BITS 32
+
+register struct CPUPPCState *env asm(AREG0);
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+register uint32_t T2 asm(AREG3);
+
+#define PARAM(n) ((uint32_t)PARAM##n)
+#define SPARAM(n) ((int32_t)PARAM##n)
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define FT2 (env->ft2)
+
+#if defined (DEBUG_OP)
+#define RETURN() __asm__ __volatile__("nop");
+#else
+#define RETURN() __asm__ __volatile__("");
+#endif
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline uint32_t rotl (uint32_t i, int n)
+{
+ return ((i << n) | (i >> (32 - n)));
+}
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+void do_raise_exception_err (uint32_t exception, int error_code);
+void do_raise_exception (uint32_t exception);
+
+void do_sraw(void);
+
+void do_fctiw (void);
+void do_fctiwz (void);
+void do_fnmadd (void);
+void do_fnmsub (void);
+void do_fsqrt (void);
+void do_fres (void);
+void do_frsqrte (void);
+void do_fsel (void);
+void do_fcmpu (void);
+void do_fcmpo (void);
+
+void do_check_reservation (void);
+void do_icbi (void);
+void do_tlbia (void);
+void do_tlbie (void);
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu);
+
+#endif /* !defined (__PPC_H__) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
new file mode 100644
index 0000000..3f7a708
--- /dev/null
+++ b/target-ppc/helper.c
@@ -0,0 +1,1458 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_BATS
+//#define DEBUG_EXCEPTIONS
+//#define FLUSH_ALL_TLBS
+
+/*****************************************************************************/
+/* PowerPC MMU emulation */
+
+#if defined(CONFIG_USER_ONLY)
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu)
+{
+ int exception, error_code;
+
+ if (rw == 2) {
+ exception = EXCP_ISI;
+ error_code = 0;
+ } else {
+ exception = EXCP_DSI;
+ error_code = 0;
+ if (rw)
+ error_code |= 0x02000000;
+ env->spr[SPR_DAR] = address;
+ env->spr[SPR_DSISR] = error_code;
+ }
+ env->exception_index = exception;
+ env->error_code = error_code;
+ return 1;
+}
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+#else
+/* Perform BAT hit & translation */
+static int get_bat (CPUState *env, uint32_t *real, int *prot,
+ uint32_t virtual, int rw, int type)
+{
+ uint32_t *BATlt, *BATut, *BATu, *BATl;
+ uint32_t base, BEPIl, BEPIu, bl;
+ int i;
+ int ret = -1;
+
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: %cBAT v 0x%08x\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', virtual);
+ }
+#endif
+ switch (type) {
+ case ACCESS_CODE:
+ BATlt = env->IBAT[1];
+ BATut = env->IBAT[0];
+ break;
+ default:
+ BATlt = env->DBAT[1];
+ BATut = env->DBAT[0];
+ break;
+ }
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s...: %cBAT v 0x%08x\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', virtual);
+ }
+#endif
+ base = virtual & 0xFFFC0000;
+ for (i = 0; i < 4; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ bl = (*BATu & 0x00001FFC) << 15;
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x\n",
+ __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
+ *BATu, *BATl);
+ }
+#endif
+ if ((virtual & 0xF0000000) == BEPIu &&
+ ((virtual & 0x0FFE0000) & ~bl) == BEPIl) {
+ /* BAT matches */
+ if ((msr_pr == 0 && (*BATu & 0x00000002)) ||
+ (msr_pr == 1 && (*BATu & 0x00000001))) {
+ /* Get physical address */
+ *real = (*BATl & 0xF0000000) |
+ ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) |
+ (virtual & 0x0001F000);
+ if (*BATl & 0x00000001)
+ *prot = PAGE_READ;
+ if (*BATl & 0x00000002)
+ *prot = PAGE_WRITE | PAGE_READ;
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "BAT %d match: r 0x%08x prot=%c%c\n",
+ i, *real, *prot & PAGE_READ ? 'R' : '-',
+ *prot & PAGE_WRITE ? 'W' : '-');
+ }
+#endif
+ ret = 0;
+ break;
+ }
+ }
+ }
+ if (ret < 0) {
+#if defined (DEBUG_BATS)
+ printf("no BAT match for 0x%08x:\n", virtual);
+ for (i = 0; i < 4; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ bl = (*BATu & 0x00001FFC) << 15;
+ printf("%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x \n\t"
+ "0x%08x 0x%08x 0x%08x\n",
+ __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
+ *BATu, *BATl, BEPIu, BEPIl, bl);
+ }
+#endif
+ }
+ /* No hit */
+ return ret;
+}
+
+/* PTE table lookup */
+static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va,
+ int h, int key, int rw)
+{
+ uint32_t pte0, pte1, keep = 0, access = 0;
+ int i, good = -1, store = 0;
+ int ret = -1; /* No entry found */
+
+ for (i = 0; i < 8; i++) {
+ pte0 = ldl_phys(base + (i * 8));
+ pte1 = ldl_phys(base + (i * 8) + 4);
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x "
+ "%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1,
+ pte0 >> 31, h, (pte0 >> 6) & 1, va);
+ }
+#endif
+ /* Check validity and table match */
+ if (pte0 & 0x80000000 && (h == ((pte0 >> 6) & 1))) {
+ /* Check vsid & api */
+ if ((pte0 & 0x7FFFFFBF) == va) {
+ if (good == -1) {
+ good = i;
+ keep = pte1;
+ } else {
+ /* All matches should have equal RPN, WIMG & PP */
+ if ((keep & 0xFFFFF07B) != (pte1 & 0xFFFFF07B)) {
+ if (loglevel > 0)
+ fprintf(logfile, "Bad RPN/WIMG/PP\n");
+ return -1;
+ }
+ }
+ /* Check access rights */
+ if (key == 0) {
+ access = PAGE_READ;
+ if ((pte1 & 0x00000003) != 0x3)
+ access |= PAGE_WRITE;
+ } else {
+ switch (pte1 & 0x00000003) {
+ case 0x0:
+ access = 0;
+ break;
+ case 0x1:
+ case 0x3:
+ access = PAGE_READ;
+ break;
+ case 0x2:
+ access = PAGE_READ | PAGE_WRITE;
+ break;
+ }
+ }
+ if (ret < 0) {
+ if ((rw == 0 && (access & PAGE_READ)) ||
+ (rw == 1 && (access & PAGE_WRITE))) {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "PTE access granted !\n");
+#endif
+ good = i;
+ keep = pte1;
+ ret = 0;
+ } else {
+ /* Access right violation */
+ ret = -2;
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "PTE access rejected\n");
+#endif
+ }
+ *prot = access;
+ }
+ }
+ }
+ }
+ if (good != -1) {
+ *RPN = keep & 0xFFFFF000;
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n",
+ *RPN, *prot, ret);
+ }
+#endif
+ /* Update page flags */
+ if (!(keep & 0x00000100)) {
+ /* Access flag */
+ keep |= 0x00000100;
+ store = 1;
+ }
+ if (!(keep & 0x00000080)) {
+ if (rw && ret == 0) {
+ /* Change flag */
+ keep |= 0x00000080;
+ store = 1;
+ } else {
+ /* Force page fault for first write access */
+ *prot &= ~PAGE_WRITE;
+ }
+ }
+ if (store) {
+ stl_phys_notdirty(base + (good * 8) + 4, keep);
+ }
+ }
+
+ return ret;
+}
+
+static inline uint32_t get_pgaddr (uint32_t sdr1, uint32_t hash, uint32_t mask)
+{
+ return (sdr1 & 0xFFFF0000) | (hash & mask);
+}
+
+/* Perform segment based translation */
+static int get_segment (CPUState *env, uint32_t *real, int *prot,
+ uint32_t virtual, int rw, int type)
+{
+ uint32_t pg_addr, sdr, ptem, vsid, pgidx;
+ uint32_t hash, mask;
+ uint32_t sr;
+ int key;
+ int ret = -1, ret2;
+
+ sr = env->sr[virtual >> 28];
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x "
+ "lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n",
+ virtual, virtual >> 28, sr, env->nip,
+ env->lr, msr_ir, msr_dr, msr_pr, rw, type);
+ }
+#endif
+ key = (((sr & 0x20000000) && msr_pr == 1) ||
+ ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
+ if ((sr & 0x80000000) == 0) {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "pte segment: key=%d n=0x%08x\n",
+ key, sr & 0x10000000);
+#endif
+ /* Check if instruction fetch is allowed, if needed */
+ if (type != ACCESS_CODE || (sr & 0x10000000) == 0) {
+ /* Page address translation */
+ vsid = sr & 0x00FFFFFF;
+ pgidx = (virtual >> 12) & 0xFFFF;
+ sdr = env->sdr1;
+ hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6;
+ mask = ((sdr & 0x000001FF) << 16) | 0xFFC0;
+ pg_addr = get_pgaddr(sdr, hash, mask);
+ ptem = (vsid << 7) | (pgidx >> 10);
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x "
+ "hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx, hash,
+ pg_addr);
+ }
+#endif
+ /* Primary table lookup */
+ ret = find_pte(real, prot, pg_addr, ptem, 0, key, rw);
+ if (ret < 0) {
+ /* Secondary table lookup */
+ hash = (~hash) & 0x01FFFFC0;
+ pg_addr = get_pgaddr(sdr, hash, mask);
+#if defined (DEBUG_MMU)
+ if (virtual != 0xEFFFFFFF && loglevel > 0) {
+ fprintf(logfile, "1 sdr1=0x%08x vsid=0x%06x api=0x%04x "
+ "hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, pgidx,
+ hash, pg_addr);
+ }
+#endif
+ ret2 = find_pte(real, prot, pg_addr, ptem, 1, key, rw);
+ if (ret2 != -1)
+ ret = ret2;
+ }
+ } else {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "No access allowed\n");
+#endif
+ ret = -3;
+ }
+ } else {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "direct store...\n");
+#endif
+ /* Direct-store segment : absolutely *BUGGY* for now */
+ switch (type) {
+ case ACCESS_INT:
+ /* Integer load/store : only access allowed */
+ break;
+ case ACCESS_CODE:
+ /* No code fetch is allowed in direct-store areas */
+ return -4;
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ return -4;
+ case ACCESS_RES:
+ /* lwarx, ldarx or srwcx. */
+ return -4;
+ case ACCESS_CACHE:
+ /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
+ /* Should make the instruction do no-op.
+ * As it already do no-op, it's quite easy :-)
+ */
+ *real = virtual;
+ return 0;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ return -4;
+ default:
+ if (logfile) {
+ fprintf(logfile, "ERROR: instruction should not need "
+ "address translation\n");
+ }
+ printf("ERROR: instruction should not need "
+ "address translation\n");
+ return -4;
+ }
+ if ((rw == 1 || key != 1) && (rw == 0 || key != 0)) {
+ *real = virtual;
+ ret = 2;
+ } else {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+static int get_physical_address (CPUState *env, uint32_t *physical, int *prot,
+ uint32_t address, int rw, int access_type)
+{
+ int ret;
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s\n", __func__);
+ }
+#endif
+ if ((access_type == ACCESS_CODE && msr_ir == 0) ||
+ (access_type != ACCESS_CODE && msr_dr == 0)) {
+ /* No address translation */
+ *physical = address & ~0xFFF;
+ *prot = PAGE_READ | PAGE_WRITE;
+ ret = 0;
+ } else {
+ /* Try to find a BAT */
+ ret = get_bat(env, physical, prot, address, rw, access_type);
+ if (ret < 0) {
+ /* We didn't match any BAT entry */
+ ret = get_segment(env, physical, prot, address, rw, access_type);
+ }
+ }
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s address %08x => %08x\n",
+ __func__, address, *physical);
+ }
+#endif
+ return ret;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ uint32_t phys_addr;
+ int prot;
+
+ if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
+ return -1;
+ return phys_addr;
+}
+
+/* Perform address translation */
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu)
+{
+ uint32_t physical;
+ int prot;
+ int exception = 0, error_code = 0;
+ int access_type;
+ int ret = 0;
+
+ if (rw == 2) {
+ /* code access */
+ rw = 0;
+ access_type = ACCESS_CODE;
+ } else {
+ /* data access */
+ /* XXX: put correct access by using cpu_restore_state()
+ correctly */
+ access_type = ACCESS_INT;
+ // access_type = env->access_type;
+ }
+ if (env->user_mode_only) {
+ /* user mode only emulation */
+ ret = -2;
+ goto do_fault;
+ }
+ ret = get_physical_address(env, &physical, &prot,
+ address, rw, access_type);
+ if (ret == 0) {
+ ret = tlb_set_page(env, address & ~0xFFF, physical, prot,
+ is_user, is_softmmu);
+ } else if (ret < 0) {
+ do_fault:
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#endif
+ if (access_type == ACCESS_CODE) {
+ exception = EXCP_ISI;
+ switch (ret) {
+ case -1:
+ /* No matches in page tables */
+ error_code = 0x40000000;
+ break;
+ case -2:
+ /* Access rights violation */
+ error_code = 0x08000000;
+ break;
+ case -3:
+ /* No execute protection violation */
+ error_code = 0x10000000;
+ break;
+ case -4:
+ /* Direct store exception */
+ /* No code fetch is allowed in direct-store areas */
+ error_code = 0x10000000;
+ break;
+ case -5:
+ /* No match in segment table */
+ exception = EXCP_ISEG;
+ error_code = 0;
+ break;
+ }
+ } else {
+ exception = EXCP_DSI;
+ switch (ret) {
+ case -1:
+ /* No matches in page tables */
+ error_code = 0x40000000;
+ break;
+ case -2:
+ /* Access rights violation */
+ error_code = 0x08000000;
+ break;
+ case -4:
+ /* Direct store exception */
+ switch (access_type) {
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ exception = EXCP_ALIGN;
+ error_code = EXCP_ALIGN_FP;
+ break;
+ case ACCESS_RES:
+ /* lwarx, ldarx or srwcx. */
+ error_code = 0x04000000;
+ break;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ error_code = 0x04100000;
+ break;
+ default:
+ printf("DSI: invalid exception (%d)\n", ret);
+ exception = EXCP_PROGRAM;
+ error_code = EXCP_INVAL | EXCP_INVAL_INVAL;
+ break;
+ }
+ break;
+ case -5:
+ /* No match in segment table */
+ exception = EXCP_DSEG;
+ error_code = 0;
+ break;
+ }
+ if (exception == EXCP_DSI && rw == 1)
+ error_code |= 0x02000000;
+ /* Store fault address */
+ env->spr[SPR_DAR] = address;
+ env->spr[SPR_DSISR] = error_code;
+ }
+#if 0
+ printf("%s: set exception to %d %02x\n",
+ __func__, exception, error_code);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+ ret = 1;
+ }
+ return ret;
+}
+#endif
+
+/*****************************************************************************/
+/* BATs management */
+#if !defined(FLUSH_ALL_TLBS)
+static inline void do_invalidate_BAT (CPUPPCState *env,
+ target_ulong BATu, target_ulong mask)
+{
+ target_ulong base, end, page;
+ base = BATu & ~0x0001FFFF;
+ end = base + mask + 0x00020000;
+#if defined (DEBUG_BATS)
+ if (loglevel != 0)
+ fprintf(logfile, "Flush BAT from %08x to %08x (%08x)\n", base, end, mask);
+#endif
+ for (page = base; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+#if defined (DEBUG_BATS)
+ if (loglevel != 0)
+ fprintf(logfile, "Flush done\n");
+#endif
+}
+#endif
+
+static inline void dump_store_bat (CPUPPCState *env, char ID, int ul, int nr,
+ target_ulong value)
+{
+#if defined (DEBUG_BATS)
+ if (loglevel != 0) {
+ fprintf(logfile, "Set %cBAT%d%c to 0x%08lx (0x%08lx)\n",
+ ID, nr, ul == 0 ? 'u' : 'l', (unsigned long)value,
+ (unsigned long)env->nip);
+ }
+#endif
+}
+
+target_ulong do_load_ibatu (CPUPPCState *env, int nr)
+{
+ return env->IBAT[0][nr];
+}
+
+target_ulong do_load_ibatl (CPUPPCState *env, int nr)
+{
+ return env->IBAT[1][nr];
+}
+
+void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'I', 0, nr, value);
+ if (env->IBAT[0][nr] != value) {
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#endif
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->IBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) |
+ (env->IBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#endif
+#if defined(FLUSH_ALL_TLBS)
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'I', 1, nr, value);
+ env->IBAT[1][nr] = value;
+}
+
+target_ulong do_load_dbatu (CPUPPCState *env, int nr)
+{
+ return env->DBAT[0][nr];
+}
+
+target_ulong do_load_dbatl (CPUPPCState *env, int nr)
+{
+ return env->DBAT[1][nr];
+}
+
+void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'D', 0, nr, value);
+ if (env->DBAT[0][nr] != value) {
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#endif
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->DBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) |
+ (env->DBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#else
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'D', 1, nr, value);
+ env->DBAT[1][nr] = value;
+}
+
+static inline void invalidate_all_tlbs (CPUPPCState *env)
+{
+ /* XXX: this needs to be completed for sotware driven TLB support */
+ tlb_flush(env, 1);
+}
+
+/*****************************************************************************/
+/* Special registers manipulation */
+target_ulong do_load_nip (CPUPPCState *env)
+{
+ return env->nip;
+}
+
+void do_store_nip (CPUPPCState *env, target_ulong value)
+{
+ env->nip = value;
+}
+
+target_ulong do_load_sdr1 (CPUPPCState *env)
+{
+ return env->sdr1;
+}
+
+void do_store_sdr1 (CPUPPCState *env, target_ulong value)
+{
+#if defined (DEBUG_MMU)
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: 0x%08lx\n", __func__, (unsigned long)value);
+ }
+#endif
+ if (env->sdr1 != value) {
+ env->sdr1 = value;
+ invalidate_all_tlbs(env);
+ }
+}
+
+target_ulong do_load_sr (CPUPPCState *env, int srnum)
+{
+ return env->sr[srnum];
+}
+
+void do_store_sr (CPUPPCState *env, int srnum, target_ulong value)
+{
+#if defined (DEBUG_MMU)
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: reg=%d 0x%08lx %08lx\n",
+ __func__, srnum, (unsigned long)value, env->sr[srnum]);
+ }
+#endif
+ if (env->sr[srnum] != value) {
+ env->sr[srnum] = value;
+#if !defined(FLUSH_ALL_TLBS) && 0
+ {
+ target_ulong page, end;
+ /* Invalidate 256 MB of virtual memory */
+ page = (16 << 20) * srnum;
+ end = page + (16 << 20);
+ for (; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+ }
+#else
+ invalidate_all_tlbs(env);
+#endif
+ }
+}
+
+uint32_t do_load_cr (CPUPPCState *env)
+{
+ return (env->crf[0] << 28) |
+ (env->crf[1] << 24) |
+ (env->crf[2] << 20) |
+ (env->crf[3] << 16) |
+ (env->crf[4] << 12) |
+ (env->crf[5] << 8) |
+ (env->crf[6] << 4) |
+ (env->crf[7] << 0);
+}
+
+void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask)
+{
+ int i, sh;
+
+ for (i = 0, sh = 7; i < 8; i++, sh --) {
+ if (mask & (1 << sh))
+ env->crf[i] = (value >> (sh * 4)) & 0xFUL;
+ }
+}
+
+uint32_t do_load_xer (CPUPPCState *env)
+{
+ return (xer_so << XER_SO) |
+ (xer_ov << XER_OV) |
+ (xer_ca << XER_CA) |
+ (xer_bc << XER_BC) |
+ (xer_cmp << XER_CMP);
+}
+
+void do_store_xer (CPUPPCState *env, uint32_t value)
+{
+ xer_so = (value >> XER_SO) & 0x01;
+ xer_ov = (value >> XER_OV) & 0x01;
+ xer_ca = (value >> XER_CA) & 0x01;
+ xer_cmp = (value >> XER_CMP) & 0xFF;
+ xer_bc = (value >> XER_BC) & 0x3F;
+}
+
+target_ulong do_load_msr (CPUPPCState *env)
+{
+ return (msr_vr << MSR_VR) |
+ (msr_ap << MSR_AP) |
+ (msr_sa << MSR_SA) |
+ (msr_key << MSR_KEY) |
+ (msr_pow << MSR_POW) |
+ (msr_tlb << MSR_TLB) |
+ (msr_ile << MSR_ILE) |
+ (msr_ee << MSR_EE) |
+ (msr_pr << MSR_PR) |
+ (msr_fp << MSR_FP) |
+ (msr_me << MSR_ME) |
+ (msr_fe0 << MSR_FE0) |
+ (msr_se << MSR_SE) |
+ (msr_be << MSR_BE) |
+ (msr_fe1 << MSR_FE1) |
+ (msr_al << MSR_AL) |
+ (msr_ip << MSR_IP) |
+ (msr_ir << MSR_IR) |
+ (msr_dr << MSR_DR) |
+ (msr_pe << MSR_PE) |
+ (msr_px << MSR_PX) |
+ (msr_ri << MSR_RI) |
+ (msr_le << MSR_LE);
+}
+
+void do_compute_hflags (CPUPPCState *env)
+{
+ /* Compute current hflags */
+ env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) |
+ (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) |
+ (msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) |
+ (msr_se << MSR_SE) | (msr_be << MSR_BE);
+}
+
+void do_store_msr (CPUPPCState *env, target_ulong value)
+{
+ int enter_pm;
+
+ value &= env->msr_mask;
+ if (((value >> MSR_IR) & 1) != msr_ir ||
+ ((value >> MSR_DR) & 1) != msr_dr) {
+ /* Flush all tlb when changing translation mode
+ * When using software driven TLB, we may also need to reload
+ * all defined TLBs
+ */
+ tlb_flush(env, 1);
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ }
+#if 0
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: T0 %08lx\n", __func__, value);
+ }
+#endif
+ msr_vr = (value >> MSR_VR) & 1;
+ msr_ap = (value >> MSR_AP) & 1;
+ msr_sa = (value >> MSR_SA) & 1;
+ msr_key = (value >> MSR_KEY) & 1;
+ msr_pow = (value >> MSR_POW) & 1;
+ msr_tlb = (value >> MSR_TLB) & 1;
+ msr_ile = (value >> MSR_ILE) & 1;
+ msr_ee = (value >> MSR_EE) & 1;
+ msr_pr = (value >> MSR_PR) & 1;
+ msr_fp = (value >> MSR_FP) & 1;
+ msr_me = (value >> MSR_ME) & 1;
+ msr_fe0 = (value >> MSR_FE0) & 1;
+ msr_se = (value >> MSR_SE) & 1;
+ msr_be = (value >> MSR_BE) & 1;
+ msr_fe1 = (value >> MSR_FE1) & 1;
+ msr_al = (value >> MSR_AL) & 1;
+ msr_ip = (value >> MSR_IP) & 1;
+ msr_ir = (value >> MSR_IR) & 1;
+ msr_dr = (value >> MSR_DR) & 1;
+ msr_pe = (value >> MSR_PE) & 1;
+ msr_px = (value >> MSR_PX) & 1;
+ msr_ri = (value >> MSR_RI) & 1;
+ msr_le = (value >> MSR_LE) & 1;
+ do_compute_hflags(env);
+
+ enter_pm = 0;
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_7x0:
+ if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0)
+ enter_pm = 1;
+ break;
+ default:
+ break;
+ }
+ if (enter_pm) {
+ /* power save: exit cpu loop */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+ }
+}
+
+float64 do_load_fpscr (CPUPPCState *env)
+{
+ /* The 32 MSB of the target fpr are undefined.
+ * They'll be zero...
+ */
+ union {
+ float64 d;
+ struct {
+ uint32_t u[2];
+ } s;
+ } u;
+ int i;
+
+#ifdef WORDS_BIGENDIAN
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+ u.s.u[WORD0] = 0;
+ u.s.u[WORD1] = 0;
+ for (i = 0; i < 8; i++)
+ u.s.u[WORD1] |= env->fpscr[i] << (4 * i);
+ return u.d;
+}
+
+void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask)
+{
+ /*
+ * We use only the 32 LSB of the incoming fpr
+ */
+ union {
+ double d;
+ struct {
+ uint32_t u[2];
+ } s;
+ } u;
+ int i, rnd_type;
+
+ u.d = f;
+ if (mask & 0x80)
+ env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9);
+ for (i = 1; i < 7; i++) {
+ if (mask & (1 << (7 - i)))
+ env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF;
+ }
+ /* TODO: update FEX & VX */
+ /* Set rounding mode */
+ switch (env->fpscr[0] & 0x3) {
+ case 0:
+ /* Best approximation (round to nearest) */
+ rnd_type = float_round_nearest_even;
+ break;
+ case 1:
+ /* Smaller magnitude (round toward zero) */
+ rnd_type = float_round_to_zero;
+ break;
+ case 2:
+ /* Round toward +infinite */
+ rnd_type = float_round_up;
+ break;
+ default:
+ case 3:
+ /* Round toward -infinite */
+ rnd_type = float_round_down;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+}
+
+/*****************************************************************************/
+/* Exception processing */
+#if defined (CONFIG_USER_ONLY)
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+#else
+static void dump_syscall(CPUState *env)
+{
+ fprintf(logfile, "syscall r0=0x%08x r3=0x%08x r4=0x%08x r5=0x%08x r6=0x%08x nip=0x%08x\n",
+ env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->nip);
+}
+
+void do_interrupt (CPUState *env)
+{
+ target_ulong msr, *srr_0, *srr_1, tmp;
+ int excp;
+
+ excp = env->exception_index;
+ msr = do_load_msr(env);
+ /* The default is to use SRR0 & SRR1 to save the exception context */
+ srr_0 = &env->spr[SPR_SRR0];
+ srr_1 = &env->spr[SPR_SRR1];
+#if defined (DEBUG_EXCEPTIONS)
+ if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) {
+ if (loglevel != 0) {
+ fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n",
+ (unsigned long)env->nip, excp, env->error_code);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+ }
+#endif
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n",
+ (unsigned long)env->nip, excp, env->error_code);
+ }
+ msr_pow = 0;
+ /* Generate informations in save/restore registers */
+ switch (excp) {
+ /* Generic PowerPC exceptions */
+ case EXCP_RESET: /* 0x0100 */
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) {
+ if (msr_ip)
+ excp += 0xFFC00;
+ excp |= 0xFFC00000;
+ } else {
+ srr_0 = &env->spr[SPR_40x_SRR2];
+ srr_1 = &env->spr[SPR_40x_SRR3];
+ }
+ goto store_next;
+ case EXCP_MACHINE_CHECK: /* 0x0200 */
+ if (msr_me == 0) {
+ cpu_abort(env, "Machine check exception while not allowed\n");
+ }
+ if (PPC_EXCP(env) == PPC_FLAGS_EXCP_40x) {
+ srr_0 = &env->spr[SPR_40x_SRR2];
+ srr_1 = &env->spr[SPR_40x_SRR3];
+ }
+ msr_me = 0;
+ break;
+ case EXCP_DSI: /* 0x0300 */
+ /* Store exception cause */
+ /* data location address has been stored
+ * when the fault has been detected
+ */
+ msr &= ~0xFFFF0000;
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel) {
+ fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
+ env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ } else {
+ printf("DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
+ env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ }
+#endif
+ goto store_next;
+ case EXCP_ISI: /* 0x0400 */
+ /* Store exception cause */
+ msr &= ~0xFFFF0000;
+ msr |= env->error_code;
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel != 0) {
+ fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n",
+ msr, env->nip);
+ }
+#endif
+ goto store_next;
+ case EXCP_EXTERNAL: /* 0x0500 */
+ if (msr_ee == 0) {
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel > 0) {
+ fprintf(logfile, "Skipping hardware interrupt\n");
+ }
+#endif
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_HARD;
+ return;
+ }
+ goto store_next;
+ case EXCP_ALIGN: /* 0x0600 */
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_601) {
+ /* Store exception cause */
+ /* Get rS/rD and rA from faulting opcode */
+ env->spr[SPR_DSISR] |=
+ (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
+ /* data location address has been stored
+ * when the fault has been detected
+ */
+ } else {
+ /* IO error exception on PowerPC 601 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "601 IO error exception is not implemented yet !\n");
+ }
+ goto store_current;
+ case EXCP_PROGRAM: /* 0x0700 */
+ msr &= ~0xFFFF0000;
+ switch (env->error_code & ~0xF) {
+ case EXCP_FP:
+ if (msr_fe0 == 0 && msr_fe1 == 0) {
+#if defined (DEBUG_EXCEPTIONS)
+ printf("Ignore floating point exception\n");
+#endif
+ return;
+ }
+ msr |= 0x00100000;
+ /* Set FX */
+ env->fpscr[7] |= 0x8;
+ /* Finally, update FEX */
+ if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
+ ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
+ env->fpscr[7] |= 0x4;
+ break;
+ case EXCP_INVAL:
+ // printf("Invalid instruction at 0x%08x\n", env->nip);
+ msr |= 0x00080000;
+ break;
+ case EXCP_PRIV:
+ msr |= 0x00040000;
+ break;
+ case EXCP_TRAP:
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ break;
+ }
+ msr |= 0x00010000;
+ goto store_current;
+ case EXCP_NO_FP: /* 0x0800 */
+ msr &= ~0xFFFF0000;
+ goto store_current;
+ case EXCP_DECR:
+ if (msr_ee == 0) {
+#if 1
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_TIMER;
+#endif
+ return;
+ }
+ goto store_next;
+ case EXCP_SYSCALL: /* 0x0C00 */
+ /* NOTE: this is a temporary hack to support graphics OSI
+ calls from the MOL driver */
+ if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
+ env->osi_call) {
+ if (env->osi_call(env) != 0)
+ return;
+ }
+ if (loglevel & CPU_LOG_INT) {
+ dump_syscall(env);
+ }
+ goto store_next;
+ case EXCP_TRACE: /* 0x0D00 */
+ /* XXX: TODO */
+ cpu_abort(env, "Trace exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_PERF: /* 0x0F00 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Performance counter exception is not implemented yet !\n");
+ goto store_next;
+ /* 32 bits PowerPC specific exceptions */
+ case EXCP_FP_ASSIST: /* 0x0E00 */
+ /* XXX: TODO */
+ cpu_abort(env, "Floating point assist exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ /* 64 bits PowerPC exceptions */
+ case EXCP_DSEG: /* 0x0380 */
+ /* XXX: TODO */
+ cpu_abort(env, "Data segment exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_ISEG: /* 0x0480 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Instruction segment exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_HDECR: /* 0x0980 */
+ if (msr_ee == 0) {
+#if 1
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_TIMER;
+#endif
+ return;
+ }
+ cpu_abort(env,
+ "Hypervisor decrementer exception is not implemented yet !\n");
+ goto store_next;
+ /* Implementation specific exceptions */
+ case 0x0A00:
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_602) {
+ /* Critical interrupt on G2 */
+ /* XXX: TODO */
+ cpu_abort(env, "G2 critical interrupt is not implemented yet !\n");
+ goto store_next;
+ } else {
+ cpu_abort(env, "Invalid exception 0x0A00 !\n");
+ }
+ return;
+ case 0x0F20:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* APU unavailable on 405 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "APU unavailable exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_74xx:
+ /* Altivec unavailable */
+ /* XXX: TODO */
+ cpu_abort(env, "Altivec unavailable exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x0F20 !\n");
+ break;
+ }
+ return;
+ case 0x1000:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* PIT on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env, "40x PIT exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* ITLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ goto store_gprs;
+ default:
+ cpu_abort(env, "Invalid exception 0x1000 !\n");
+ break;
+ }
+ return;
+ case 0x1010:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* FIT on 4xx */
+ cpu_abort(env, "40x FIT exception is not implemented yet !\n");
+ /* XXX: TODO */
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1010 !\n");
+ break;
+ }
+ return;
+ case 0x1020:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* Watchdog on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x watchdog exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1020 !\n");
+ break;
+ }
+ return;
+ case 0x1100:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* DTLBMISS on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x DTLBMISS exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* DLTLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ goto store_gprs;
+ default:
+ cpu_abort(env, "Invalid exception 0x1100 !\n");
+ break;
+ }
+ return;
+ case 0x1200:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* ITLBMISS on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x ITLBMISS exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* DSTLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ store_gprs:
+#if defined (DEBUG_SOFTWARE_TLB)
+ if (loglevel != 0) {
+ fprintf(logfile, "6xx %sTLB miss: IM %08x DM %08x IC %08x "
+ "DC %08x H1 %08x H2 %08x %08x\n",
+ excp == 0x1000 ? "I" : excp == 0x1100 ? "DL" : "DS",
+ env->spr[SPR_IMISS], env->spr[SPR_DMISS],
+ env->spr[SPR_ICMP], env->spr[SPR_DCMP],
+ env->spr[SPR_DHASH1], env->spr[SPR_DHASH2],
+ env->error_code);
+ }
+#endif
+ /* Swap temporary saved registers with GPRs */
+ tmp = env->gpr[0];
+ env->gpr[0] = env->tgpr[0];
+ env->tgpr[0] = tmp;
+ tmp = env->gpr[1];
+ env->gpr[1] = env->tgpr[1];
+ env->tgpr[1] = tmp;
+ tmp = env->gpr[2];
+ env->gpr[2] = env->tgpr[2];
+ env->tgpr[2] = tmp;
+ tmp = env->gpr[3];
+ env->gpr[3] = env->tgpr[3];
+ env->tgpr[3] = tmp;
+ msr |= env->crf[0] << 28;
+ msr |= env->error_code; /* key, D/I, S/L bits */
+ /* Set way using a LRU mechanism */
+ msr |= (env->last_way ^ 1) << 17;
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1200 !\n");
+ break;
+ }
+ return;
+ case 0x1300:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_601:
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ case PPC_FLAGS_EXCP_604:
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* IABR on 6xx/7xx */
+ /* XXX: TODO */
+ cpu_abort(env, "IABR exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1300 !\n");
+ break;
+ }
+ return;
+ case 0x1400:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_601:
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ case PPC_FLAGS_EXCP_604:
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* SMI on 6xx/7xx */
+ /* XXX: TODO */
+ cpu_abort(env, "SMI exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1400 !\n");
+ break;
+ }
+ return;
+ case 0x1500:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_602:
+ /* Watchdog on 602 */
+ cpu_abort(env,
+ "602 watchdog exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* Soft patch exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 soft-patch exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_74xx:
+ /* VPU assist on 74xx */
+ /* XXX: TODO */
+ cpu_abort(env, "VPU assist exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1500 !\n");
+ break;
+ }
+ return;
+ case 0x1600:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_602:
+ /* Emulation trap on 602 */
+ /* XXX: TODO */
+ cpu_abort(env, "602 emulation trap exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* Maintenance exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 maintenance exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1600 !\n");
+ break;
+ }
+ return;
+ case 0x1700:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* Thermal management interrupt on G3 */
+ /* XXX: TODO */
+ cpu_abort(env, "G3 thermal management exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* VPU assist on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 VPU assist exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1700 !\n");
+ break;
+ }
+ return;
+ case 0x1800:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_970:
+ /* Thermal exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env, "970 thermal management exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1800 !\n");
+ break;
+ }
+ return;
+ case 0x2000:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* DEBUG on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env, "40x debug exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_601:
+ /* Run mode exception on 601 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "601 run mode exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1800 !\n");
+ break;
+ }
+ return;
+ /* Other exceptions */
+ /* Qemu internal exceptions:
+ * we should never come here with those values: abort execution
+ */
+ default:
+ cpu_abort(env, "Invalid exception: code %d (%04x)\n", excp, excp);
+ return;
+ store_current:
+ /* save current instruction location */
+ *srr_0 = (env->nip - 4) & 0xFFFFFFFFULL;
+ break;
+ store_next:
+ /* save next instruction location */
+ *srr_0 = env->nip & 0xFFFFFFFFULL;
+ break;
+ }
+ /* Save msr */
+ *srr_1 = msr;
+ /* If we disactivated any translation, flush TLBs */
+ if (msr_ir || msr_dr) {
+ tlb_flush(env, 1);
+ }
+ /* reload MSR with correct bits */
+ msr_ee = 0;
+ msr_pr = 0;
+ msr_fp = 0;
+ msr_fe0 = 0;
+ msr_se = 0;
+ msr_be = 0;
+ msr_fe1 = 0;
+ msr_ir = 0;
+ msr_dr = 0;
+ msr_ri = 0;
+ msr_le = msr_ile;
+ msr_sf = msr_isf;
+ do_compute_hflags(env);
+ /* Jump to handler */
+ env->nip = excp;
+ env->exception_index = EXCP_NONE;
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/op.c b/target-ppc/op.c
new file mode 100644
index 0000000..ca1dbc5
--- /dev/null
+++ b/target-ppc/op.c
@@ -0,0 +1,1296 @@
+/*
+ * PowerPC emulation micro-operations for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//#define DEBUG_OP
+
+#include "config.h"
+#include "exec.h"
+
+#define regs (env)
+#define Ts0 (int32_t)T0
+#define Ts1 (int32_t)T1
+#define Ts2 (int32_t)T2
+
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define FT2 (env->ft2)
+
+#define PPC_OP(name) void glue(op_, name)(void)
+
+#define REG 0
+#include "op_template.h"
+
+#define REG 1
+#include "op_template.h"
+
+#define REG 2
+#include "op_template.h"
+
+#define REG 3
+#include "op_template.h"
+
+#define REG 4
+#include "op_template.h"
+
+#define REG 5
+#include "op_template.h"
+
+#define REG 6
+#include "op_template.h"
+
+#define REG 7
+#include "op_template.h"
+
+#define REG 8
+#include "op_template.h"
+
+#define REG 9
+#include "op_template.h"
+
+#define REG 10
+#include "op_template.h"
+
+#define REG 11
+#include "op_template.h"
+
+#define REG 12
+#include "op_template.h"
+
+#define REG 13
+#include "op_template.h"
+
+#define REG 14
+#include "op_template.h"
+
+#define REG 15
+#include "op_template.h"
+
+#define REG 16
+#include "op_template.h"
+
+#define REG 17
+#include "op_template.h"
+
+#define REG 18
+#include "op_template.h"
+
+#define REG 19
+#include "op_template.h"
+
+#define REG 20
+#include "op_template.h"
+
+#define REG 21
+#include "op_template.h"
+
+#define REG 22
+#include "op_template.h"
+
+#define REG 23
+#include "op_template.h"
+
+#define REG 24
+#include "op_template.h"
+
+#define REG 25
+#include "op_template.h"
+
+#define REG 26
+#include "op_template.h"
+
+#define REG 27
+#include "op_template.h"
+
+#define REG 28
+#include "op_template.h"
+
+#define REG 29
+#include "op_template.h"
+
+#define REG 30
+#include "op_template.h"
+
+#define REG 31
+#include "op_template.h"
+
+/* PowerPC state maintenance operations */
+/* set_Rc0 */
+PPC_OP(set_Rc0)
+{
+ uint32_t tmp;
+
+ if (Ts0 < 0) {
+ tmp = 0x08;
+ } else if (Ts0 > 0) {
+ tmp = 0x04;
+ } else {
+ tmp = 0x02;
+ }
+ tmp |= xer_ov;
+ env->crf[0] = tmp;
+ RETURN();
+}
+
+/* reset_Rc0 */
+PPC_OP(reset_Rc0)
+{
+ env->crf[0] = 0x02 | xer_ov;
+ RETURN();
+}
+
+/* set_Rc0_1 */
+PPC_OP(set_Rc0_1)
+{
+ env->crf[0] = 0x04 | xer_ov;
+ RETURN();
+}
+
+/* Set Rc1 (for floating point arithmetic) */
+PPC_OP(set_Rc1)
+{
+ env->crf[1] = regs->fpscr[7];
+ RETURN();
+}
+
+/* Constants load */
+PPC_OP(set_T0)
+{
+ T0 = PARAM(1);
+ RETURN();
+}
+
+PPC_OP(set_T1)
+{
+ T1 = PARAM(1);
+ RETURN();
+}
+
+PPC_OP(set_T2)
+{
+ T2 = PARAM(1);
+ RETURN();
+}
+
+/* Generate exceptions */
+PPC_OP(raise_exception_err)
+{
+ do_raise_exception_err(PARAM(1), PARAM(2));
+}
+
+PPC_OP(raise_exception)
+{
+ do_raise_exception(PARAM(1));
+}
+
+PPC_OP(update_nip)
+{
+ env->nip = PARAM(1);
+}
+
+PPC_OP(debug)
+{
+ do_raise_exception(EXCP_DEBUG);
+}
+
+/* Segment registers load and store with immediate index */
+PPC_OP(load_srin)
+{
+ T0 = regs->sr[T1 >> 28];
+ RETURN();
+}
+
+PPC_OP(store_srin)
+{
+ do_store_sr(env, ((uint32_t)T1 >> 28), T0);
+ RETURN();
+}
+
+PPC_OP(load_sdr1)
+{
+ T0 = regs->sdr1;
+ RETURN();
+}
+
+PPC_OP(store_sdr1)
+{
+ do_store_sdr1(env, T0);
+ RETURN();
+}
+
+PPC_OP(exit_tb)
+{
+ EXIT_TB();
+}
+
+/* Load/store special registers */
+PPC_OP(load_cr)
+{
+ T0 = do_load_cr(env);
+ RETURN();
+}
+
+PPC_OP(store_cr)
+{
+ do_store_cr(env, T0, PARAM(1));
+ RETURN();
+}
+
+PPC_OP(load_xer_cr)
+{
+ T0 = (xer_so << 3) | (xer_ov << 2) | (xer_ca << 1);
+ RETURN();
+}
+
+PPC_OP(clear_xer_cr)
+{
+ xer_so = 0;
+ xer_ov = 0;
+ xer_ca = 0;
+ RETURN();
+}
+
+PPC_OP(load_xer_bc)
+{
+ T1 = xer_bc;
+ RETURN();
+}
+
+PPC_OP(load_xer)
+{
+ T0 = do_load_xer(env);
+ RETURN();
+}
+
+PPC_OP(store_xer)
+{
+ do_store_xer(env, T0);
+ RETURN();
+}
+
+PPC_OP(load_msr)
+{
+ T0 = do_load_msr(env);
+ RETURN();
+}
+
+PPC_OP(store_msr)
+{
+ do_store_msr(env, T0);
+ RETURN();
+}
+
+/* SPR */
+PPC_OP(load_spr)
+{
+ T0 = regs->spr[PARAM(1)];
+ RETURN();
+}
+
+PPC_OP(store_spr)
+{
+ regs->spr[PARAM(1)] = T0;
+ RETURN();
+}
+
+PPC_OP(load_lr)
+{
+ T0 = regs->lr;
+ RETURN();
+}
+
+PPC_OP(store_lr)
+{
+ regs->lr = T0;
+ RETURN();
+}
+
+PPC_OP(load_ctr)
+{
+ T0 = regs->ctr;
+ RETURN();
+}
+
+PPC_OP(store_ctr)
+{
+ regs->ctr = T0;
+ RETURN();
+}
+
+PPC_OP(load_tbl)
+{
+ T0 = cpu_ppc_load_tbl(regs);
+ RETURN();
+}
+
+PPC_OP(load_tbu)
+{
+ T0 = cpu_ppc_load_tbu(regs);
+ RETURN();
+}
+
+PPC_OP(store_tbl)
+{
+ cpu_ppc_store_tbl(regs, T0);
+ RETURN();
+}
+
+PPC_OP(store_tbu)
+{
+ cpu_ppc_store_tbu(regs, T0);
+ RETURN();
+}
+
+PPC_OP(load_decr)
+{
+ T0 = cpu_ppc_load_decr(regs);
+ }
+
+PPC_OP(store_decr)
+{
+ cpu_ppc_store_decr(regs, T0);
+ RETURN();
+}
+
+PPC_OP(load_ibat)
+{
+ T0 = regs->IBAT[PARAM(1)][PARAM(2)];
+}
+
+void op_store_ibatu (void)
+{
+ do_store_ibatu(env, PARAM1, T0);
+ RETURN();
+}
+
+void op_store_ibatl (void)
+{
+#if 1
+ env->IBAT[1][PARAM1] = T0;
+#else
+ do_store_ibatl(env, PARAM1, T0);
+#endif
+ RETURN();
+}
+
+PPC_OP(load_dbat)
+{
+ T0 = regs->DBAT[PARAM(1)][PARAM(2)];
+}
+
+void op_store_dbatu (void)
+{
+ do_store_dbatu(env, PARAM1, T0);
+ RETURN();
+}
+
+void op_store_dbatl (void)
+{
+#if 1
+ env->DBAT[1][PARAM1] = T0;
+#else
+ do_store_dbatl(env, PARAM1, T0);
+#endif
+ RETURN();
+}
+
+/* FPSCR */
+PPC_OP(load_fpscr)
+{
+ FT0 = do_load_fpscr(env);
+ RETURN();
+}
+
+PPC_OP(store_fpscr)
+{
+ do_store_fpscr(env, FT0, PARAM1);
+ RETURN();
+}
+
+PPC_OP(reset_scrfx)
+{
+ regs->fpscr[7] &= ~0x8;
+ RETURN();
+}
+
+/* crf operations */
+PPC_OP(getbit_T0)
+{
+ T0 = (T0 >> PARAM(1)) & 1;
+ RETURN();
+}
+
+PPC_OP(getbit_T1)
+{
+ T1 = (T1 >> PARAM(1)) & 1;
+ RETURN();
+}
+
+PPC_OP(setcrfbit)
+{
+ T1 = (T1 & PARAM(1)) | (T0 << PARAM(2));
+ RETURN();
+}
+
+/* Branch */
+#define EIP regs->nip
+
+PPC_OP(setlr)
+{
+ regs->lr = PARAM1;
+}
+
+PPC_OP(goto_tb0)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+PPC_OP(goto_tb1)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+PPC_OP(b_T1)
+{
+ regs->nip = T1 & ~3;
+}
+
+PPC_OP(jz_T0)
+{
+ if (!T0)
+ GOTO_LABEL_PARAM(1);
+ RETURN();
+}
+
+PPC_OP(btest_T1)
+{
+ if (T0) {
+ regs->nip = T1 & ~3;
+ } else {
+ regs->nip = PARAM1;
+ }
+ RETURN();
+}
+
+PPC_OP(movl_T1_ctr)
+{
+ T1 = regs->ctr;
+}
+
+PPC_OP(movl_T1_lr)
+{
+ T1 = regs->lr;
+}
+
+/* tests with result in T0 */
+
+PPC_OP(test_ctr)
+{
+ T0 = regs->ctr;
+}
+
+PPC_OP(test_ctr_true)
+{
+ T0 = (regs->ctr != 0 && (T0 & PARAM(1)) != 0);
+}
+
+PPC_OP(test_ctr_false)
+{
+ T0 = (regs->ctr != 0 && (T0 & PARAM(1)) == 0);
+}
+
+PPC_OP(test_ctrz)
+{
+ T0 = (regs->ctr == 0);
+}
+
+PPC_OP(test_ctrz_true)
+{
+ T0 = (regs->ctr == 0 && (T0 & PARAM(1)) != 0);
+}
+
+PPC_OP(test_ctrz_false)
+{
+ T0 = (regs->ctr == 0 && (T0 & PARAM(1)) == 0);
+}
+
+PPC_OP(test_true)
+{
+ T0 = (T0 & PARAM(1));
+}
+
+PPC_OP(test_false)
+{
+ T0 = ((T0 & PARAM(1)) == 0);
+}
+
+/* CTR maintenance */
+PPC_OP(dec_ctr)
+{
+ regs->ctr--;
+ RETURN();
+}
+
+/*** Integer arithmetic ***/
+/* add */
+PPC_OP(add)
+{
+ T0 += T1;
+ RETURN();
+}
+
+void do_addo (void);
+void op_addo (void)
+{
+ do_addo();
+ RETURN();
+}
+
+/* add carrying */
+PPC_OP(addc)
+{
+ T2 = T0;
+ T0 += T1;
+ if (T0 < T2) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_addco (void);
+void op_addco (void)
+{
+ do_addco();
+ RETURN();
+}
+
+/* add extended */
+void do_adde (void);
+void op_adde (void)
+{
+ do_adde();
+}
+
+void do_addeo (void);
+PPC_OP(addeo)
+{
+ do_addeo();
+ RETURN();
+}
+
+/* add immediate */
+PPC_OP(addi)
+{
+ T0 += PARAM(1);
+ RETURN();
+}
+
+/* add immediate carrying */
+PPC_OP(addic)
+{
+ T1 = T0;
+ T0 += PARAM(1);
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* add to minus one extended */
+PPC_OP(addme)
+{
+ T1 = T0;
+ T0 += xer_ca + (-1);
+ if (T1 != 0)
+ xer_ca = 1;
+ RETURN();
+}
+
+void do_addmeo (void);
+void op_addmeo (void)
+{
+ do_addmeo();
+ RETURN();
+}
+
+/* add to zero extended */
+PPC_OP(addze)
+{
+ T1 = T0;
+ T0 += xer_ca;
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_addzeo (void);
+void op_addzeo (void)
+{
+ do_addzeo();
+ RETURN();
+}
+
+/* divide word */
+PPC_OP(divw)
+{
+ if ((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0) {
+ T0 = (int32_t)((-1) * (T0 >> 31));
+ } else {
+ T0 = (Ts0 / Ts1);
+ }
+ RETURN();
+}
+
+void do_divwo (void);
+void op_divwo (void)
+{
+ do_divwo();
+ RETURN();
+}
+
+/* divide word unsigned */
+PPC_OP(divwu)
+{
+ if (T1 == 0) {
+ T0 = 0;
+ } else {
+ T0 /= T1;
+ }
+ RETURN();
+}
+
+void do_divwuo (void);
+void op_divwuo (void)
+{
+ do_divwuo();
+ RETURN();
+}
+
+/* multiply high word */
+PPC_OP(mulhw)
+{
+ T0 = ((int64_t)Ts0 * (int64_t)Ts1) >> 32;
+ RETURN();
+}
+
+/* multiply high word unsigned */
+PPC_OP(mulhwu)
+{
+ T0 = ((uint64_t)T0 * (uint64_t)T1) >> 32;
+ RETURN();
+}
+
+/* multiply low immediate */
+PPC_OP(mulli)
+{
+ T0 = (Ts0 * SPARAM(1));
+ RETURN();
+}
+
+/* multiply low word */
+PPC_OP(mullw)
+{
+ T0 *= T1;
+ RETURN();
+}
+
+void do_mullwo (void);
+void op_mullwo (void)
+{
+ do_mullwo();
+ RETURN();
+}
+
+/* negate */
+PPC_OP(neg)
+{
+ if (T0 != 0x80000000) {
+ T0 = -Ts0;
+ }
+ RETURN();
+}
+
+void do_nego (void);
+void op_nego (void)
+{
+ do_nego();
+ RETURN();
+}
+
+/* substract from */
+PPC_OP(subf)
+{
+ T0 = T1 - T0;
+ RETURN();
+}
+
+void do_subfo (void);
+void op_subfo (void)
+{
+ do_subfo();
+ RETURN();
+}
+
+/* substract from carrying */
+PPC_OP(subfc)
+{
+ T0 = T1 - T0;
+ if (T0 <= T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_subfco (void);
+void op_subfco (void)
+{
+ do_subfco();
+ RETURN();
+}
+
+/* substract from extended */
+void do_subfe (void);
+void op_subfe (void)
+{
+ do_subfe();
+ RETURN();
+}
+
+void do_subfeo (void);
+PPC_OP(subfeo)
+{
+ do_subfeo();
+ RETURN();
+}
+
+/* substract from immediate carrying */
+PPC_OP(subfic)
+{
+ T0 = PARAM(1) + ~T0 + 1;
+ if (T0 <= PARAM(1)) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* substract from minus one extended */
+PPC_OP(subfme)
+{
+ T0 = ~T0 + xer_ca - 1;
+
+ if (T0 != -1)
+ xer_ca = 1;
+ RETURN();
+}
+
+void do_subfmeo (void);
+void op_subfmeo (void)
+{
+ do_subfmeo();
+ RETURN();
+}
+
+/* substract from zero extended */
+PPC_OP(subfze)
+{
+ T1 = ~T0;
+ T0 = T1 + xer_ca;
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_subfzeo (void);
+void op_subfzeo (void)
+{
+ do_subfzeo();
+ RETURN();
+}
+
+/*** Integer comparison ***/
+/* compare */
+PPC_OP(cmp)
+{
+ if (Ts0 < Ts1) {
+ T0 = 0x08;
+ } else if (Ts0 > Ts1) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare immediate */
+PPC_OP(cmpi)
+{
+ if (Ts0 < SPARAM(1)) {
+ T0 = 0x08;
+ } else if (Ts0 > SPARAM(1)) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare logical */
+PPC_OP(cmpl)
+{
+ if (T0 < T1) {
+ T0 = 0x08;
+ } else if (T0 > T1) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare logical immediate */
+PPC_OP(cmpli)
+{
+ if (T0 < PARAM(1)) {
+ T0 = 0x08;
+ } else if (T0 > PARAM(1)) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/*** Integer logical ***/
+/* and */
+PPC_OP(and)
+{
+ T0 &= T1;
+ RETURN();
+}
+
+/* andc */
+PPC_OP(andc)
+{
+ T0 &= ~T1;
+ RETURN();
+}
+
+/* andi. */
+PPC_OP(andi_)
+{
+ T0 &= PARAM(1);
+ RETURN();
+}
+
+/* count leading zero */
+PPC_OP(cntlzw)
+{
+ T1 = T0;
+ for (T0 = 32; T1 > 0; T0--)
+ T1 = T1 >> 1;
+ RETURN();
+}
+
+/* eqv */
+PPC_OP(eqv)
+{
+ T0 = ~(T0 ^ T1);
+ RETURN();
+}
+
+/* extend sign byte */
+PPC_OP(extsb)
+{
+ T0 = (int32_t)((int8_t)(Ts0));
+ RETURN();
+}
+
+/* extend sign half word */
+PPC_OP(extsh)
+{
+ T0 = (int32_t)((int16_t)(Ts0));
+ RETURN();
+}
+
+/* nand */
+PPC_OP(nand)
+{
+ T0 = ~(T0 & T1);
+ RETURN();
+}
+
+/* nor */
+PPC_OP(nor)
+{
+ T0 = ~(T0 | T1);
+ RETURN();
+}
+
+/* or */
+PPC_OP(or)
+{
+ T0 |= T1;
+ RETURN();
+}
+
+/* orc */
+PPC_OP(orc)
+{
+ T0 |= ~T1;
+ RETURN();
+}
+
+/* ori */
+PPC_OP(ori)
+{
+ T0 |= PARAM(1);
+ RETURN();
+}
+
+/* xor */
+PPC_OP(xor)
+{
+ T0 ^= T1;
+ RETURN();
+}
+
+/* xori */
+PPC_OP(xori)
+{
+ T0 ^= PARAM(1);
+ RETURN();
+}
+
+/*** Integer rotate ***/
+/* rotate left word immediate then mask insert */
+PPC_OP(rlwimi)
+{
+ T0 = (rotl(T0, PARAM(1)) & PARAM(2)) | (T1 & PARAM(3));
+ RETURN();
+}
+
+/* rotate left immediate then and with mask insert */
+PPC_OP(rotlwi)
+{
+ T0 = rotl(T0, PARAM(1));
+ RETURN();
+}
+
+PPC_OP(slwi)
+{
+ T0 = T0 << PARAM(1);
+ RETURN();
+}
+
+PPC_OP(srwi)
+{
+ T0 = T0 >> PARAM(1);
+ RETURN();
+}
+
+/* rotate left word then and with mask insert */
+PPC_OP(rlwinm)
+{
+ T0 = rotl(T0, PARAM(1)) & PARAM(2);
+ RETURN();
+}
+
+PPC_OP(rotl)
+{
+ T0 = rotl(T0, T1);
+ RETURN();
+}
+
+PPC_OP(rlwnm)
+{
+ T0 = rotl(T0, T1) & PARAM(1);
+ RETURN();
+}
+
+/*** Integer shift ***/
+/* shift left word */
+PPC_OP(slw)
+{
+ if (T1 & 0x20) {
+ T0 = 0;
+ } else {
+ T0 = T0 << T1;
+ }
+ RETURN();
+}
+
+/* shift right algebraic word */
+void op_sraw (void)
+{
+ do_sraw();
+ RETURN();
+}
+
+/* shift right algebraic word immediate */
+PPC_OP(srawi)
+{
+ T1 = T0;
+ T0 = (Ts0 >> PARAM(1));
+ if (Ts1 < 0 && (Ts1 & PARAM(2)) != 0) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* shift right word */
+PPC_OP(srw)
+{
+ if (T1 & 0x20) {
+ T0 = 0;
+ } else {
+ T0 = T0 >> T1;
+ }
+ RETURN();
+}
+
+/*** Floating-Point arithmetic ***/
+/* fadd - fadd. */
+PPC_OP(fadd)
+{
+ FT0 += FT1;
+ RETURN();
+}
+
+/* fsub - fsub. */
+PPC_OP(fsub)
+{
+ FT0 -= FT1;
+ RETURN();
+}
+
+/* fmul - fmul. */
+PPC_OP(fmul)
+{
+ FT0 *= FT1;
+ RETURN();
+}
+
+/* fdiv - fdiv. */
+PPC_OP(fdiv)
+{
+ FT0 = float64_div(FT0, FT1, &env->fp_status);
+ RETURN();
+}
+
+/* fsqrt - fsqrt. */
+PPC_OP(fsqrt)
+{
+ do_fsqrt();
+ RETURN();
+}
+
+/* fres - fres. */
+PPC_OP(fres)
+{
+ do_fres();
+ RETURN();
+}
+
+/* frsqrte - frsqrte. */
+PPC_OP(frsqrte)
+{
+ do_frsqrte();
+ RETURN();
+}
+
+/* fsel - fsel. */
+PPC_OP(fsel)
+{
+ do_fsel();
+ RETURN();
+}
+
+/*** Floating-Point multiply-and-add ***/
+/* fmadd - fmadd. */
+PPC_OP(fmadd)
+{
+ FT0 = (FT0 * FT1) + FT2;
+ RETURN();
+}
+
+/* fmsub - fmsub. */
+PPC_OP(fmsub)
+{
+ FT0 = (FT0 * FT1) - FT2;
+ RETURN();
+}
+
+/* fnmadd - fnmadd. - fnmadds - fnmadds. */
+PPC_OP(fnmadd)
+{
+ do_fnmadd();
+ RETURN();
+}
+
+/* fnmsub - fnmsub. */
+PPC_OP(fnmsub)
+{
+ do_fnmsub();
+ RETURN();
+}
+
+/*** Floating-Point round & convert ***/
+/* frsp - frsp. */
+PPC_OP(frsp)
+{
+ FT0 = (float)FT0;
+ RETURN();
+}
+
+/* fctiw - fctiw. */
+PPC_OP(fctiw)
+{
+ do_fctiw();
+ RETURN();
+}
+
+/* fctiwz - fctiwz. */
+PPC_OP(fctiwz)
+{
+ do_fctiwz();
+ RETURN();
+}
+
+
+/*** Floating-Point compare ***/
+/* fcmpu */
+PPC_OP(fcmpu)
+{
+ do_fcmpu();
+ RETURN();
+}
+
+/* fcmpo */
+PPC_OP(fcmpo)
+{
+ do_fcmpo();
+ RETURN();
+}
+
+/*** Floating-point move ***/
+/* fabs */
+PPC_OP(fabs)
+{
+ FT0 = float64_abs(FT0);
+ RETURN();
+}
+
+/* fnabs */
+PPC_OP(fnabs)
+{
+ FT0 = float64_abs(FT0);
+ FT0 = float64_chs(FT0);
+ RETURN();
+}
+
+/* fneg */
+PPC_OP(fneg)
+{
+ FT0 = float64_chs(FT0);
+ RETURN();
+}
+
+/* Load and store */
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
+
+/* Special op to check and maybe clear reservation */
+PPC_OP(check_reservation)
+{
+ if ((uint32_t)env->reserve == (uint32_t)(T0 & ~0x00000003))
+ env->reserve = -1;
+ RETURN();
+}
+
+/* Return from interrupt */
+void do_rfi (void);
+void op_rfi (void)
+{
+ do_rfi();
+ RETURN();
+}
+
+/* Trap word */
+void do_tw (uint32_t cmp, int flags);
+void op_tw (void)
+{
+ do_tw(T1, PARAM(1));
+ RETURN();
+}
+
+void op_twi (void)
+{
+ do_tw(PARAM(1), PARAM(2));
+ RETURN();
+}
+
+/* Instruction cache block invalidate */
+PPC_OP(icbi)
+{
+ do_icbi();
+ RETURN();
+}
+
+/* tlbia */
+PPC_OP(tlbia)
+{
+ do_tlbia();
+ RETURN();
+}
+
+/* tlbie */
+PPC_OP(tlbie)
+{
+ do_tlbie();
+ RETURN();
+}
+
+void op_store_pir (void)
+{
+ env->spr[SPR_PIR] = T0 & 0x0000000FUL;
+ RETURN();
+}
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
new file mode 100644
index 0000000..e949eb4
--- /dev/null
+++ b/target-ppc/op_helper.c
@@ -0,0 +1,589 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+#define MEMSUFFIX _raw
+#include "op_helper_mem.h"
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_helper_mem.h"
+#define MEMSUFFIX _kernel
+#include "op_helper_mem.h"
+#endif
+
+//#define DEBUG_OP
+//#define DEBUG_EXCEPTIONS
+//#define FLUSH_ALL_TLBS
+
+#define Ts0 (long)((target_long)T0)
+#define Ts1 (long)((target_long)T1)
+#define Ts2 (long)((target_long)T2)
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+void cpu_loop_exit(void)
+{
+ longjmp(env->jmp_env, 1);
+}
+
+void do_raise_exception_err (uint32_t exception, int error_code)
+{
+#if 0
+ printf("Raise exception %3x code : %d\n", exception, error_code);
+#endif
+ switch (exception) {
+ case EXCP_PROGRAM:
+ if (error_code == EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0)
+ return;
+ break;
+ default:
+ break;
+}
+ env->exception_index = exception;
+ env->error_code = error_code;
+ cpu_loop_exit();
+ }
+
+void do_raise_exception (uint32_t exception)
+{
+ do_raise_exception_err(exception, 0);
+}
+
+/*****************************************************************************/
+/* Fixed point operations helpers */
+void do_addo (void)
+{
+ T2 = T0;
+ T0 += T1;
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_addco (void)
+{
+ T2 = T0;
+ T0 += T1;
+ if (likely(T0 >= T2)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_adde (void)
+{
+ T2 = T0;
+ T0 += T1 + xer_ca;
+ if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_addeo (void)
+{
+ T2 = T0;
+ T0 += T1 + xer_ca;
+ if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_addmeo (void)
+{
+ T1 = T0;
+ T0 += xer_ca + (-1);
+ if (likely(!(T1 & (T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T1 != 0))
+ xer_ca = 1;
+}
+
+void do_addzeo (void)
+{
+ T1 = T0;
+ T0 += xer_ca;
+ if (likely(!((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T0 >= T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_divwo (void)
+{
+ if (likely(!((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0))) {
+ xer_ov = 0;
+ T0 = (Ts0 / Ts1);
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ T0 = (-1) * ((uint32_t)T0 >> 31);
+ }
+}
+
+void do_divwuo (void)
+{
+ if (likely((uint32_t)T1 != 0)) {
+ xer_ov = 0;
+ T0 = (uint32_t)T0 / (uint32_t)T1;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ T0 = 0;
+ }
+}
+
+void do_mullwo (void)
+{
+ int64_t res = (int64_t)Ts0 * (int64_t)Ts1;
+
+ if (likely((int32_t)res == res)) {
+ xer_ov = 0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+ T0 = (int32_t)res;
+}
+
+void do_nego (void)
+{
+ if (likely(T0 != INT32_MIN)) {
+ xer_ov = 0;
+ T0 = -Ts0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+}
+
+void do_subfo (void)
+{
+ T2 = T0;
+ T0 = T1 - T0;
+ if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ RETURN();
+}
+
+void do_subfco (void)
+{
+ T2 = T0;
+ T0 = T1 - T0;
+ if (likely(T0 > T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_subfe (void)
+{
+ T0 = T1 + ~T0 + xer_ca;
+ if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_subfeo (void)
+{
+ T2 = T0;
+ T0 = T1 + ~T0 + xer_ca;
+ if (likely(!((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_subfmeo (void)
+{
+ T1 = T0;
+ T0 = ~T0 + xer_ca - 1;
+ if (likely(!(~T1 & (~T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T1 != -1))
+ xer_ca = 1;
+}
+
+void do_subfzeo (void)
+{
+ T1 = T0;
+ T0 = ~T0 + xer_ca;
+ if (likely(!((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+ if (likely(T0 >= ~T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+/* shift right arithmetic helper */
+void do_sraw (void)
+{
+ int32_t ret;
+
+ if (likely(!(T1 & 0x20UL))) {
+ if (likely(T1 != 0)) {
+ ret = (int32_t)T0 >> (T1 & 0x1fUL);
+ if (likely(ret >= 0 || ((int32_t)T0 & ((1 << T1) - 1)) == 0)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ } else {
+ ret = T0;
+ xer_ca = 0;
+ }
+ } else {
+ ret = (-1) * ((uint32_t)T0 >> 31);
+ if (likely(ret >= 0 || ((uint32_t)T0 & ~0x80000000UL) == 0)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ }
+ T0 = ret;
+}
+
+/*****************************************************************************/
+/* Floating point operations helpers */
+void do_fctiw (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750 (aka G3)
+ */
+ p.i = float64_to_int32(FT0, &env->fp_status);
+ p.i |= 0xFFF80000ULL << 32;
+ FT0 = p.d;
+}
+
+void do_fctiwz (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750 (aka G3)
+ */
+ p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status);
+ p.i |= 0xFFF80000ULL << 32;
+ FT0 = p.d;
+}
+
+void do_fnmadd (void)
+{
+ FT0 = float64_mul(FT0, FT1, &env->fp_status);
+ FT0 = float64_add(FT0, FT2, &env->fp_status);
+ if (likely(!isnan(FT0)))
+ FT0 = float64_chs(FT0);
+}
+
+void do_fnmsub (void)
+{
+ FT0 = float64_mul(FT0, FT1, &env->fp_status);
+ FT0 = float64_sub(FT0, FT2, &env->fp_status);
+ if (likely(!isnan(FT0)))
+ FT0 = float64_chs(FT0);
+}
+
+void do_fsqrt (void)
+{
+ FT0 = float64_sqrt(FT0, &env->fp_status);
+}
+
+void do_fres (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ if (likely(isnormal(FT0))) {
+ FT0 = (float)(1.0 / FT0);
+ } else {
+ p.d = FT0;
+ if (p.i == 0x8000000000000000ULL) {
+ p.i = 0xFFF0000000000000ULL;
+ } else if (p.i == 0x0000000000000000ULL) {
+ p.i = 0x7FF0000000000000ULL;
+ } else if (isnan(FT0)) {
+ p.i = 0x7FF8000000000000ULL;
+ } else if (FT0 < 0.0) {
+ p.i = 0x8000000000000000ULL;
+ } else {
+ p.i = 0x0000000000000000ULL;
+ }
+ FT0 = p.d;
+ }
+}
+
+void do_frsqrte (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ if (likely(isnormal(FT0) && FT0 > 0.0)) {
+ FT0 = float64_sqrt(FT0, &env->fp_status);
+ FT0 = float32_div(1.0, FT0, &env->fp_status);
+ } else {
+ p.d = FT0;
+ if (p.i == 0x8000000000000000ULL) {
+ p.i = 0xFFF0000000000000ULL;
+ } else if (p.i == 0x0000000000000000ULL) {
+ p.i = 0x7FF0000000000000ULL;
+ } else if (isnan(FT0)) {
+ if (!(p.i & 0x0008000000000000ULL))
+ p.i |= 0x000FFFFFFFFFFFFFULL;
+ } else if (FT0 < 0) {
+ p.i = 0x7FF8000000000000ULL;
+ } else {
+ p.i = 0x0000000000000000ULL;
+ }
+ FT0 = p.d;
+ }
+}
+
+void do_fsel (void)
+{
+ if (FT0 >= 0)
+ FT0 = FT1;
+ else
+ FT0 = FT2;
+}
+
+void do_fcmpu (void)
+{
+ if (likely(!isnan(FT0) && !isnan(FT1))) {
+ if (float64_lt(FT0, FT1, &env->fp_status)) {
+ T0 = 0x08UL;
+ } else if (!float64_le(FT0, FT1, &env->fp_status)) {
+ T0 = 0x04UL;
+ } else {
+ T0 = 0x02UL;
+ }
+ } else {
+ T0 = 0x01UL;
+ env->fpscr[4] |= 0x1;
+ env->fpscr[6] |= 0x1;
+ }
+ env->fpscr[3] = T0;
+}
+
+void do_fcmpo (void)
+{
+ env->fpscr[4] &= ~0x1;
+ if (likely(!isnan(FT0) && !isnan(FT1))) {
+ if (float64_lt(FT0, FT1, &env->fp_status)) {
+ T0 = 0x08UL;
+ } else if (!float64_le(FT0, FT1, &env->fp_status)) {
+ T0 = 0x04UL;
+ } else {
+ T0 = 0x02UL;
+ }
+ } else {
+ T0 = 0x01UL;
+ env->fpscr[4] |= 0x1;
+ /* I don't know how to test "quiet" nan... */
+ if (0 /* || ! quiet_nan(...) */) {
+ env->fpscr[6] |= 0x1;
+ if (!(env->fpscr[1] & 0x8))
+ env->fpscr[4] |= 0x8;
+ } else {
+ env->fpscr[4] |= 0x8;
+ }
+ }
+ env->fpscr[3] = T0;
+}
+
+void do_rfi (void)
+{
+ env->nip = env->spr[SPR_SRR0] & ~0x00000003;
+ T0 = env->spr[SPR_SRR1] & ~0xFFFF0000UL;
+ do_store_msr(env, T0);
+#if defined (DEBUG_OP)
+ dump_rfi();
+#endif
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+void do_tw (uint32_t cmp, int flags)
+{
+ if (!likely(!((Ts0 < (int32_t)cmp && (flags & 0x10)) ||
+ (Ts0 > (int32_t)cmp && (flags & 0x08)) ||
+ (Ts0 == (int32_t)cmp && (flags & 0x04)) ||
+ (T0 < cmp && (flags & 0x02)) ||
+ (T0 > cmp && (flags & 0x01)))))
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP);
+}
+
+/* Instruction cache invalidation helper */
+void do_icbi (void)
+{
+ uint32_t tmp;
+ /* Invalidate one cache line :
+ * PowerPC specification says this is to be treated like a load
+ * (not a fetch) by the MMU. To be sure it will be so,
+ * do the load "by hand".
+ */
+#if defined(TARGET_PPC64)
+ if (!msr_sf)
+ T0 &= 0xFFFFFFFFULL;
+#endif
+ tmp = ldl_kernel(T0);
+ T0 &= ~(ICACHE_LINE_SIZE - 1);
+ tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE);
+}
+
+/*****************************************************************************/
+/* MMU related helpers */
+/* TLB invalidation helpers */
+void do_tlbia (void)
+{
+ tlb_flush(env, 1);
+}
+
+void do_tlbie (void)
+{
+#if !defined(FLUSH_ALL_TLBS)
+ tlb_flush_page(env, T0);
+#else
+ do_tlbia();
+#endif
+}
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ target_phys_addr_t pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (!likely(ret == 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (target_phys_addr_t)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+}
+ }
+ do_raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+#endif /* !CONFIG_USER_ONLY */
+
diff --git a/target-ppc/op_helper_mem.h b/target-ppc/op_helper_mem.h
new file mode 100644
index 0000000..fb90691
--- /dev/null
+++ b/target-ppc/op_helper_mem.h
@@ -0,0 +1,100 @@
+void glue(do_lsw, MEMSUFFIX) (int dst)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, dst);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ ugpr(dst++) = glue(ldl, MEMSUFFIX)(T0);
+ if (dst == 32)
+ dst = 0;
+ }
+ if (T1 > 0) {
+ tmp = 0;
+ for (sh = 24; T1 > 0; T1--, T0++, sh -= 8) {
+ tmp |= glue(ldub, MEMSUFFIX)(T0) << sh;
+ }
+ ugpr(dst) = tmp;
+ }
+}
+
+void glue(do_stsw, MEMSUFFIX) (int src)
+{
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, src);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ glue(stl, MEMSUFFIX)(T0, ugpr(src++));
+ if (src == 32)
+ src = 0;
+ }
+ if (T1 > 0) {
+ for (sh = 24; T1 > 0; T1--, T0++, sh -= 8)
+ glue(stb, MEMSUFFIX)(T0, (ugpr(src) >> sh) & 0xFF);
+ }
+}
+
+void glue(do_lsw_le, MEMSUFFIX) (int dst)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, dst);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ tmp = glue(ldl, MEMSUFFIX)(T0);
+ ugpr(dst++) = ((tmp & 0xFF000000) >> 24) | ((tmp & 0x00FF0000) >> 8) |
+ ((tmp & 0x0000FF00) << 8) | ((tmp & 0x000000FF) << 24);
+ if (dst == 32)
+ dst = 0;
+ }
+ if (T1 > 0) {
+ tmp = 0;
+ for (sh = 0; T1 > 0; T1--, T0++, sh += 8) {
+ tmp |= glue(ldub, MEMSUFFIX)(T0) << sh;
+ }
+ ugpr(dst) = tmp;
+ }
+}
+
+void glue(do_stsw_le, MEMSUFFIX) (int src)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, src);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ tmp = ((ugpr(src++) & 0xFF000000) >> 24);
+ tmp |= ((ugpr(src++) & 0x00FF0000) >> 8);
+ tmp |= ((ugpr(src++) & 0x0000FF00) << 8);
+ tmp |= ((ugpr(src++) & 0x000000FF) << 24);
+ glue(stl, MEMSUFFIX)(T0, tmp);
+ if (src == 32)
+ src = 0;
+ }
+ if (T1 > 0) {
+ for (sh = 0; T1 > 0; T1--, T0++, sh += 8)
+ glue(stb, MEMSUFFIX)(T0, (ugpr(src) >> sh) & 0xFF);
+ }
+}
+
+#undef MEMSUFFIX
diff --git a/target-ppc/op_mem.h b/target-ppc/op_mem.h
new file mode 100644
index 0000000..9b3f721
--- /dev/null
+++ b/target-ppc/op_mem.h
@@ -0,0 +1,371 @@
+/* External helpers */
+void glue(do_lsw, MEMSUFFIX) (int dst);
+void glue(do_stsw, MEMSUFFIX) (int src);
+
+static inline uint16_t glue(ld16r, MEMSUFFIX) (target_ulong EA)
+{
+ uint16_t tmp = glue(lduw, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF00) >> 8) | ((tmp & 0x00FF) << 8);
+}
+
+static inline int32_t glue(ld16rs, MEMSUFFIX) (target_ulong EA)
+{
+ int16_t tmp = glue(lduw, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF00) >> 8) | ((tmp & 0x00FF) << 8);
+}
+
+static inline uint32_t glue(ld32r, MEMSUFFIX) (target_ulong EA)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF000000) >> 24) | ((tmp & 0x00FF0000) >> 8) |
+ ((tmp & 0x0000FF00) << 8) | ((tmp & 0x000000FF) << 24);
+}
+
+static inline void glue(st16r, MEMSUFFIX) (target_ulong EA, uint16_t data)
+{
+ uint16_t tmp = ((data & 0xFF00) >> 8) | ((data & 0x00FF) << 8);
+ glue(stw, MEMSUFFIX)(EA, tmp);
+}
+
+static inline void glue(st32r, MEMSUFFIX) (target_ulong EA, uint32_t data)
+{
+ uint32_t tmp = ((data & 0xFF000000) >> 24) | ((data & 0x00FF0000) >> 8) |
+ ((data & 0x0000FF00) << 8) | ((data & 0x000000FF) << 24);
+ glue(stl, MEMSUFFIX)(EA, tmp);
+}
+
+/*** Integer load ***/
+#define PPC_LD_OP(name, op) \
+PPC_OP(glue(glue(l, name), MEMSUFFIX)) \
+{ \
+ T1 = glue(op, MEMSUFFIX)(T0); \
+ RETURN(); \
+}
+
+#define PPC_ST_OP(name, op) \
+PPC_OP(glue(glue(st, name), MEMSUFFIX)) \
+{ \
+ glue(op, MEMSUFFIX)(T0, T1); \
+ RETURN(); \
+}
+
+PPC_LD_OP(bz, ldub);
+PPC_LD_OP(ha, ldsw);
+PPC_LD_OP(hz, lduw);
+PPC_LD_OP(wz, ldl);
+
+PPC_LD_OP(ha_le, ld16rs);
+PPC_LD_OP(hz_le, ld16r);
+PPC_LD_OP(wz_le, ld32r);
+
+/*** Integer store ***/
+PPC_ST_OP(b, stb);
+PPC_ST_OP(h, stw);
+PPC_ST_OP(w, stl);
+
+PPC_ST_OP(h_le, st16r);
+PPC_ST_OP(w_le, st32r);
+
+/*** Integer load and store with byte reverse ***/
+PPC_LD_OP(hbr, ld16r);
+PPC_LD_OP(wbr, ld32r);
+PPC_ST_OP(hbr, st16r);
+PPC_ST_OP(wbr, st32r);
+
+PPC_LD_OP(hbr_le, lduw);
+PPC_LD_OP(wbr_le, ldl);
+PPC_ST_OP(hbr_le, stw);
+PPC_ST_OP(wbr_le, stl);
+
+/*** Integer load and store multiple ***/
+PPC_OP(glue(lmw, MEMSUFFIX))
+{
+ int dst = PARAM(1);
+
+ for (; dst < 32; dst++, T0 += 4) {
+ ugpr(dst) = glue(ldl, MEMSUFFIX)(T0);
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stmw, MEMSUFFIX))
+{
+ int src = PARAM(1);
+
+ for (; src < 32; src++, T0 += 4) {
+ glue(stl, MEMSUFFIX)(T0, ugpr(src));
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lmw_le, MEMSUFFIX))
+{
+ int dst = PARAM(1);
+
+ for (; dst < 32; dst++, T0 += 4) {
+ ugpr(dst) = glue(ld32r, MEMSUFFIX)(T0);
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stmw_le, MEMSUFFIX))
+{
+ int src = PARAM(1);
+
+ for (; src < 32; src++, T0 += 4) {
+ glue(st32r, MEMSUFFIX)(T0, ugpr(src));
+ }
+ RETURN();
+}
+
+/*** Integer load and store strings ***/
+PPC_OP(glue(lswi, MEMSUFFIX))
+{
+ glue(do_lsw, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+void glue(do_lsw_le, MEMSUFFIX) (int dst);
+PPC_OP(glue(lswi_le, MEMSUFFIX))
+{
+ glue(do_lsw_le, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+/* PPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+PPC_OP(glue(lswx, MEMSUFFIX))
+{
+ if (T1 > 0) {
+ if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) ||
+ (PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) {
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ } else {
+ glue(do_lsw, MEMSUFFIX)(PARAM(1));
+ }
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lswx_le, MEMSUFFIX))
+{
+ if (T1 > 0) {
+ if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) ||
+ (PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) {
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ } else {
+ glue(do_lsw_le, MEMSUFFIX)(PARAM(1));
+ }
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stsw, MEMSUFFIX))
+{
+ glue(do_stsw, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+void glue(do_stsw_le, MEMSUFFIX) (int src);
+PPC_OP(glue(stsw_le, MEMSUFFIX))
+{
+ glue(do_stsw_le, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+/*** Floating-point store ***/
+#define PPC_STF_OP(name, op) \
+PPC_OP(glue(glue(st, name), MEMSUFFIX)) \
+{ \
+ glue(op, MEMSUFFIX)(T0, FT1); \
+ RETURN(); \
+}
+
+PPC_STF_OP(fd, stfq);
+PPC_STF_OP(fs, stfl);
+
+static inline void glue(stfqr, MEMSUFFIX) (target_ulong EA, double d)
+{
+ union {
+ double d;
+ uint64_t u;
+ } u;
+
+ u.d = d;
+ u.u = ((u.u & 0xFF00000000000000ULL) >> 56) |
+ ((u.u & 0x00FF000000000000ULL) >> 40) |
+ ((u.u & 0x0000FF0000000000ULL) >> 24) |
+ ((u.u & 0x000000FF00000000ULL) >> 8) |
+ ((u.u & 0x00000000FF000000ULL) << 8) |
+ ((u.u & 0x0000000000FF0000ULL) << 24) |
+ ((u.u & 0x000000000000FF00ULL) << 40) |
+ ((u.u & 0x00000000000000FFULL) << 56);
+ glue(stfq, MEMSUFFIX)(EA, u.d);
+}
+
+static inline void glue(stflr, MEMSUFFIX) (target_ulong EA, float f)
+{
+ union {
+ float f;
+ uint32_t u;
+ } u;
+
+ u.f = f;
+ u.u = ((u.u & 0xFF000000UL) >> 24) |
+ ((u.u & 0x00FF0000ULL) >> 8) |
+ ((u.u & 0x0000FF00UL) << 8) |
+ ((u.u & 0x000000FFULL) << 24);
+ glue(stfl, MEMSUFFIX)(EA, u.f);
+}
+
+PPC_STF_OP(fd_le, stfqr);
+PPC_STF_OP(fs_le, stflr);
+
+/*** Floating-point load ***/
+#define PPC_LDF_OP(name, op) \
+PPC_OP(glue(glue(l, name), MEMSUFFIX)) \
+{ \
+ FT1 = glue(op, MEMSUFFIX)(T0); \
+ RETURN(); \
+}
+
+PPC_LDF_OP(fd, ldfq);
+PPC_LDF_OP(fs, ldfl);
+
+static inline double glue(ldfqr, MEMSUFFIX) (target_ulong EA)
+{
+ union {
+ double d;
+ uint64_t u;
+ } u;
+
+ u.d = glue(ldfq, MEMSUFFIX)(EA);
+ u.u = ((u.u & 0xFF00000000000000ULL) >> 56) |
+ ((u.u & 0x00FF000000000000ULL) >> 40) |
+ ((u.u & 0x0000FF0000000000ULL) >> 24) |
+ ((u.u & 0x000000FF00000000ULL) >> 8) |
+ ((u.u & 0x00000000FF000000ULL) << 8) |
+ ((u.u & 0x0000000000FF0000ULL) << 24) |
+ ((u.u & 0x000000000000FF00ULL) << 40) |
+ ((u.u & 0x00000000000000FFULL) << 56);
+
+ return u.d;
+}
+
+static inline float glue(ldflr, MEMSUFFIX) (target_ulong EA)
+{
+ union {
+ float f;
+ uint32_t u;
+ } u;
+
+ u.f = glue(ldfl, MEMSUFFIX)(EA);
+ u.u = ((u.u & 0xFF000000UL) >> 24) |
+ ((u.u & 0x00FF0000ULL) >> 8) |
+ ((u.u & 0x0000FF00UL) << 8) |
+ ((u.u & 0x000000FFULL) << 24);
+
+ return u.f;
+}
+
+PPC_LDF_OP(fd_le, ldfqr);
+PPC_LDF_OP(fs_le, ldflr);
+
+/* Load and set reservation */
+PPC_OP(glue(lwarx, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ T1 = glue(ldl, MEMSUFFIX)(T0);
+ regs->reserve = T0;
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lwarx_le, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ T1 = glue(ld32r, MEMSUFFIX)(T0);
+ regs->reserve = T0;
+ }
+ RETURN();
+}
+
+/* Store with reservation */
+PPC_OP(glue(stwcx, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ if (regs->reserve != T0) {
+ env->crf[0] = xer_ov;
+ } else {
+ glue(stl, MEMSUFFIX)(T0, T1);
+ env->crf[0] = xer_ov | 0x02;
+ }
+ }
+ regs->reserve = 0;
+ RETURN();
+}
+
+PPC_OP(glue(stwcx_le, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ if (regs->reserve != T0) {
+ env->crf[0] = xer_ov;
+ } else {
+ glue(st32r, MEMSUFFIX)(T0, T1);
+ env->crf[0] = xer_ov | 0x02;
+ }
+ }
+ regs->reserve = 0;
+ RETURN();
+}
+
+PPC_OP(glue(dcbz, MEMSUFFIX))
+{
+ glue(stl, MEMSUFFIX)(T0 + 0x00, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x04, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x08, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x0C, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x10, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x14, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x18, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x1C, 0);
+ RETURN();
+}
+
+/* External access */
+PPC_OP(glue(eciwx, MEMSUFFIX))
+{
+ T1 = glue(ldl, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+PPC_OP(glue(ecowx, MEMSUFFIX))
+{
+ glue(stl, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+PPC_OP(glue(eciwx_le, MEMSUFFIX))
+{
+ T1 = glue(ld32r, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+PPC_OP(glue(ecowx_le, MEMSUFFIX))
+{
+ glue(st32r, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+#undef MEMSUFFIX
diff --git a/target-ppc/op_template.h b/target-ppc/op_template.h
new file mode 100644
index 0000000..1be640d
--- /dev/null
+++ b/target-ppc/op_template.h
@@ -0,0 +1,183 @@
+/*
+ * PowerPC emulation micro-operations for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* General purpose registers moves */
+void OPPROTO glue(op_load_gpr_T0_gpr, REG)(void)
+{
+ T0 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_gpr_T1_gpr, REG)(void)
+{
+ T1 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_gpr_T2_gpr, REG)(void)
+{
+ T2 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T1_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T1;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T2_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T2;
+ RETURN();
+}
+
+#if REG <= 7
+/* Condition register moves */
+void OPPROTO glue(op_load_crf_T0_crf, REG)(void)
+{
+ T0 = regs->crf[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_crf_T1_crf, REG)(void)
+{
+ T1 = regs->crf[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_crf_crf, REG)(void)
+{
+ regs->crf[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T1_crf_crf, REG)(void)
+{
+ regs->crf[REG] = T1;
+ RETURN();
+}
+
+/* Floating point condition and status register moves */
+void OPPROTO glue(op_load_fpscr_T0_fpscr, REG)(void)
+{
+ T0 = regs->fpscr[REG];
+ RETURN();
+}
+
+#if REG == 0
+void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & 0x9) | (T0 & ~0x9);
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & ~0x9) | (PARAM(1) & 0x9);
+ RETURN();
+}
+
+void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & 0x9);
+ RETURN();
+}
+#else
+void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = PARAM(1);
+ RETURN();
+}
+
+void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = 0x0;
+ RETURN();
+}
+#endif
+
+#endif /* REG <= 7 */
+
+/* floating point registers moves */
+void OPPROTO glue(op_load_fpr_FT0_fpr, REG)(void)
+{
+ FT0 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT0_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT0;
+ RETURN();
+}
+
+void OPPROTO glue(op_load_fpr_FT1_fpr, REG)(void)
+{
+ FT1 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT1_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT1;
+ RETURN();
+}
+
+void OPPROTO glue(op_load_fpr_FT2_fpr, REG)(void)
+{
+ FT2 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT2_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT2;
+ RETURN();
+}
+
+#if REG <= 15
+/* Segment register moves */
+void OPPROTO glue(op_load_sr, REG)(void)
+{
+ T0 = env->sr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_sr, REG)(void)
+{
+ do_store_sr(env, REG, T0);
+ RETURN();
+}
+#endif
+
+#undef REG
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
new file mode 100644
index 0000000..046f168
--- /dev/null
+++ b/target-ppc/translate.c
@@ -0,0 +1,2701 @@
+/*
+ * PowerPC emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+//#define DO_SINGLE_STEP
+//#define PPC_DEBUG_DISAS
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+
+#include "gen-op.h"
+
+#define GEN8(func, NAME) \
+static GenOpFunc *NAME ## _table [8] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+#define GEN16(func, NAME) \
+static GenOpFunc *NAME ## _table [16] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+#define GEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+/* Condition register moves */
+GEN8(gen_op_load_crf_T0, gen_op_load_crf_T0_crf);
+GEN8(gen_op_load_crf_T1, gen_op_load_crf_T1_crf);
+GEN8(gen_op_store_T0_crf, gen_op_store_T0_crf_crf);
+GEN8(gen_op_store_T1_crf, gen_op_store_T1_crf_crf);
+
+/* Floating point condition and status register moves */
+GEN8(gen_op_load_fpscr_T0, gen_op_load_fpscr_T0_fpscr);
+GEN8(gen_op_store_T0_fpscr, gen_op_store_T0_fpscr_fpscr);
+GEN8(gen_op_clear_fpscr, gen_op_clear_fpscr_fpscr);
+static GenOpFunc1 *gen_op_store_T0_fpscri_fpscr_table[8] = {
+ &gen_op_store_T0_fpscri_fpscr0,
+ &gen_op_store_T0_fpscri_fpscr1,
+ &gen_op_store_T0_fpscri_fpscr2,
+ &gen_op_store_T0_fpscri_fpscr3,
+ &gen_op_store_T0_fpscri_fpscr4,
+ &gen_op_store_T0_fpscri_fpscr5,
+ &gen_op_store_T0_fpscri_fpscr6,
+ &gen_op_store_T0_fpscri_fpscr7,
+};
+static inline void gen_op_store_T0_fpscri(int n, uint8_t param)
+{
+ (*gen_op_store_T0_fpscri_fpscr_table[n])(param);
+}
+
+/* Segment register moves */
+GEN16(gen_op_load_sr, gen_op_load_sr);
+GEN16(gen_op_store_sr, gen_op_store_sr);
+
+/* General purpose registers moves */
+GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr);
+GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr);
+GEN32(gen_op_load_gpr_T2, gen_op_load_gpr_T2_gpr);
+
+GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr);
+GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr);
+GEN32(gen_op_store_T2_gpr, gen_op_store_T2_gpr_gpr);
+
+/* floating point registers moves */
+GEN32(gen_op_load_fpr_FT0, gen_op_load_fpr_FT0_fpr);
+GEN32(gen_op_load_fpr_FT1, gen_op_load_fpr_FT1_fpr);
+GEN32(gen_op_load_fpr_FT2, gen_op_load_fpr_FT2_fpr);
+GEN32(gen_op_store_FT0_fpr, gen_op_store_FT0_fpr_fpr);
+GEN32(gen_op_store_FT1_fpr, gen_op_store_FT1_fpr_fpr);
+GEN32(gen_op_store_FT2_fpr, gen_op_store_FT2_fpr_fpr);
+
+static uint8_t spr_access[1024 / 2];
+
+/* internal defines */
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong nip;
+ uint32_t opcode;
+ uint32_t exception;
+ /* Routine used to access memory */
+ int mem_idx;
+ /* Translation flags */
+#if !defined(CONFIG_USER_ONLY)
+ int supervisor;
+#endif
+ int fpu_enabled;
+ ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
+ int singlestep_enabled;
+} DisasContext;
+
+struct opc_handler_t {
+ /* invalid bits */
+ uint32_t inval;
+ /* instruction type */
+ uint32_t type;
+ /* handler */
+ void (*handler)(DisasContext *ctx);
+};
+
+#define RET_EXCP(ctx, excp, error) \
+do { \
+ if ((ctx)->exception == EXCP_NONE) { \
+ gen_op_update_nip((ctx)->nip); \
+ } \
+ gen_op_raise_exception_err((excp), (error)); \
+ ctx->exception = (excp); \
+} while (0)
+
+#define RET_INVAL(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_INVAL)
+
+#define RET_PRIVOPC(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_OPC)
+
+#define RET_PRIVREG(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_REG)
+
+/* Stop translation */
+static inline void RET_STOP (DisasContext *ctx)
+{
+ gen_op_update_nip((ctx)->nip);
+ ctx->exception = EXCP_MTMSR;
+}
+
+/* No need to update nip here, as execution flow will change */
+static inline void RET_CHG_FLOW (DisasContext *ctx)
+{
+ ctx->exception = EXCP_MTMSR;
+}
+
+#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \
+static void gen_##name (DisasContext *ctx); \
+GEN_OPCODE(name, opc1, opc2, opc3, inval, type); \
+static void gen_##name (DisasContext *ctx)
+
+typedef struct opcode_t {
+ unsigned char opc1, opc2, opc3;
+#if HOST_LONG_BITS == 64 /* Explicitely align to 64 bits */
+ unsigned char pad[5];
+#else
+ unsigned char pad[1];
+#endif
+ opc_handler_t handler;
+ const unsigned char *oname;
+} opcode_t;
+
+/*** Instruction decoding ***/
+#define EXTRACT_HELPER(name, shift, nb) \
+static inline uint32_t name (uint32_t opcode) \
+{ \
+ return (opcode >> (shift)) & ((1 << (nb)) - 1); \
+}
+
+#define EXTRACT_SHELPER(name, shift, nb) \
+static inline int32_t name (uint32_t opcode) \
+{ \
+ return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \
+}
+
+/* Opcode part 1 */
+EXTRACT_HELPER(opc1, 26, 6);
+/* Opcode part 2 */
+EXTRACT_HELPER(opc2, 1, 5);
+/* Opcode part 3 */
+EXTRACT_HELPER(opc3, 6, 5);
+/* Update Cr0 flags */
+EXTRACT_HELPER(Rc, 0, 1);
+/* Destination */
+EXTRACT_HELPER(rD, 21, 5);
+/* Source */
+EXTRACT_HELPER(rS, 21, 5);
+/* First operand */
+EXTRACT_HELPER(rA, 16, 5);
+/* Second operand */
+EXTRACT_HELPER(rB, 11, 5);
+/* Third operand */
+EXTRACT_HELPER(rC, 6, 5);
+/*** Get CRn ***/
+EXTRACT_HELPER(crfD, 23, 3);
+EXTRACT_HELPER(crfS, 18, 3);
+EXTRACT_HELPER(crbD, 21, 5);
+EXTRACT_HELPER(crbA, 16, 5);
+EXTRACT_HELPER(crbB, 11, 5);
+/* SPR / TBL */
+EXTRACT_HELPER(_SPR, 11, 10);
+static inline uint32_t SPR (uint32_t opcode)
+{
+ uint32_t sprn = _SPR(opcode);
+
+ return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+}
+/*** Get constants ***/
+EXTRACT_HELPER(IMM, 12, 8);
+/* 16 bits signed immediate value */
+EXTRACT_SHELPER(SIMM, 0, 16);
+/* 16 bits unsigned immediate value */
+EXTRACT_HELPER(UIMM, 0, 16);
+/* Bit count */
+EXTRACT_HELPER(NB, 11, 5);
+/* Shift count */
+EXTRACT_HELPER(SH, 11, 5);
+/* Mask start */
+EXTRACT_HELPER(MB, 6, 5);
+/* Mask end */
+EXTRACT_HELPER(ME, 1, 5);
+/* Trap operand */
+EXTRACT_HELPER(TO, 21, 5);
+
+EXTRACT_HELPER(CRM, 12, 8);
+EXTRACT_HELPER(FM, 17, 8);
+EXTRACT_HELPER(SR, 16, 4);
+EXTRACT_HELPER(FPIMM, 20, 4);
+
+/*** Jump target decoding ***/
+/* Displacement */
+EXTRACT_SHELPER(d, 0, 16);
+/* Immediate address */
+static inline uint32_t LI (uint32_t opcode)
+{
+ return (opcode >> 0) & 0x03FFFFFC;
+}
+
+static inline uint32_t BD (uint32_t opcode)
+{
+ return (opcode >> 0) & 0xFFFC;
+}
+
+EXTRACT_HELPER(BO, 21, 5);
+EXTRACT_HELPER(BI, 16, 5);
+/* Absolute/relative address */
+EXTRACT_HELPER(AA, 1, 1);
+/* Link */
+EXTRACT_HELPER(LK, 0, 1);
+
+/* Create a mask between <start> and <end> bits */
+static inline uint32_t MASK (uint32_t start, uint32_t end)
+{
+ uint32_t ret;
+
+ ret = (((uint32_t)(-1)) >> (start)) ^ (((uint32_t)(-1) >> (end)) >> 1);
+ if (start > end)
+ return ~ret;
+
+ return ret;
+}
+
+#if HOST_LONG_BITS == 64
+#define OPC_ALIGN 8
+#else
+#define OPC_ALIGN 4
+#endif
+#if defined(__APPLE__)
+#define OPCODES_SECTION \
+ __attribute__ ((section("__TEXT,__opcodes"), unused, aligned (OPC_ALIGN) ))
+#else
+#define OPCODES_SECTION \
+ __attribute__ ((section(".opcodes"), unused, aligned (OPC_ALIGN) ))
+#endif
+
+#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \
+OPCODES_SECTION opcode_t opc_##name = { \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ }, \
+ .oname = stringify(name), \
+}
+
+#define GEN_OPCODE_MARK(name) \
+OPCODES_SECTION opcode_t opc_##name = { \
+ .opc1 = 0xFF, \
+ .opc2 = 0xFF, \
+ .opc3 = 0xFF, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = 0x00000000, \
+ .type = 0x00, \
+ .handler = NULL, \
+ }, \
+ .oname = stringify(name), \
+}
+
+/* Start opcode list */
+GEN_OPCODE_MARK(start);
+
+/* Invalid instruction */
+GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE)
+{
+ RET_INVAL(ctx);
+}
+
+static opc_handler_t invalid_handler = {
+ .inval = 0xFFFFFFFF,
+ .type = PPC_NONE,
+ .handler = gen_invalid,
+};
+
+/*** Integer arithmetic ***/
+#define __GEN_INT_ARITH2(name, opc1, opc2, opc3, inval) \
+GEN_HANDLER(name, opc1, opc2, opc3, inval, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+#define __GEN_INT_ARITH2_O(name, opc1, opc2, opc3, inval) \
+GEN_HANDLER(name, opc1, opc2, opc3, inval, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+#define __GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+GEN_HANDLER(name, opc1, opc2, opc3, 0x0000F800, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+#define __GEN_INT_ARITH1_O(name, opc1, opc2, opc3) \
+GEN_HANDLER(name, opc1, opc2, opc3, 0x0000F800, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+/* Two operands arithmetic functions */
+#define GEN_INT_ARITH2(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH2(name, opc1, opc2, opc3, 0x00000000) \
+__GEN_INT_ARITH2_O(name##o, opc1, opc2, opc3 | 0x10, 0x00000000)
+
+/* Two operands arithmetic functions with no overflow allowed */
+#define GEN_INT_ARITHN(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH2(name, opc1, opc2, opc3, 0x00000400)
+
+/* One operand arithmetic functions */
+#define GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH1_O(name##o, opc1, opc2, opc3 | 0x10)
+
+/* add add. addo addo. */
+GEN_INT_ARITH2 (add, 0x1F, 0x0A, 0x08);
+/* addc addc. addco addco. */
+GEN_INT_ARITH2 (addc, 0x1F, 0x0A, 0x00);
+/* adde adde. addeo addeo. */
+GEN_INT_ARITH2 (adde, 0x1F, 0x0A, 0x04);
+/* addme addme. addmeo addmeo. */
+GEN_INT_ARITH1 (addme, 0x1F, 0x0A, 0x07);
+/* addze addze. addzeo addzeo. */
+GEN_INT_ARITH1 (addze, 0x1F, 0x0A, 0x06);
+/* divw divw. divwo divwo. */
+GEN_INT_ARITH2 (divw, 0x1F, 0x0B, 0x0F);
+/* divwu divwu. divwuo divwuo. */
+GEN_INT_ARITH2 (divwu, 0x1F, 0x0B, 0x0E);
+/* mulhw mulhw. */
+GEN_INT_ARITHN (mulhw, 0x1F, 0x0B, 0x02);
+/* mulhwu mulhwu. */
+GEN_INT_ARITHN (mulhwu, 0x1F, 0x0B, 0x00);
+/* mullw mullw. mullwo mullwo. */
+GEN_INT_ARITH2 (mullw, 0x1F, 0x0B, 0x07);
+/* neg neg. nego nego. */
+GEN_INT_ARITH1 (neg, 0x1F, 0x08, 0x03);
+/* subf subf. subfo subfo. */
+GEN_INT_ARITH2 (subf, 0x1F, 0x08, 0x01);
+/* subfc subfc. subfco subfco. */
+GEN_INT_ARITH2 (subfc, 0x1F, 0x08, 0x00);
+/* subfe subfe. subfeo subfeo. */
+GEN_INT_ARITH2 (subfe, 0x1F, 0x08, 0x04);
+/* subfme subfme. subfmeo subfmeo. */
+GEN_INT_ARITH1 (subfme, 0x1F, 0x08, 0x07);
+/* subfze subfze. subfzeo subfzeo. */
+GEN_INT_ARITH1 (subfze, 0x1F, 0x08, 0x06);
+/* addi */
+GEN_HANDLER(addi, 0x0E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int32_t simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addi(simm);
+ }
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addic */
+GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addic(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addic. */
+GEN_HANDLER(addic_, 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addic(SIMM(ctx->opcode));
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addis */
+GEN_HANDLER(addis, 0x0F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int32_t simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm << 16);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addi(simm << 16);
+ }
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* mulli */
+GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_mulli(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* subfic */
+GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_subfic(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/*** Integer comparison ***/
+#define GEN_CMP(name, opc) \
+GEN_HANDLER(name, 0x1F, 0x00, opc, 0x00400000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ gen_op_store_T0_crf(crfD(ctx->opcode)); \
+}
+
+/* cmp */
+GEN_CMP(cmp, 0x00);
+/* cmpi */
+GEN_HANDLER(cmpi, 0x0B, 0xFF, 0xFF, 0x00400000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_cmpi(SIMM(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+/* cmpl */
+GEN_CMP(cmpl, 0x01);
+/* cmpli */
+GEN_HANDLER(cmpli, 0x0A, 0xFF, 0xFF, 0x00400000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_cmpli(UIMM(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** Integer logical ***/
+#define __GEN_LOGICAL2(name, opc2, opc3) \
+GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rS(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+#define GEN_LOGICAL2(name, opc) \
+__GEN_LOGICAL2(name, 0x1C, opc)
+
+#define GEN_LOGICAL1(name, opc) \
+GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rS(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+/* and & and. */
+GEN_LOGICAL2(and, 0x00);
+/* andc & andc. */
+GEN_LOGICAL2(andc, 0x01);
+/* andi. */
+GEN_HANDLER(andi_, 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_andi_(UIMM(ctx->opcode));
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* andis. */
+GEN_HANDLER(andis_, 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_andi_(UIMM(ctx->opcode) << 16);
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* cntlzw */
+GEN_LOGICAL1(cntlzw, 0x00);
+/* eqv & eqv. */
+GEN_LOGICAL2(eqv, 0x08);
+/* extsb & extsb. */
+GEN_LOGICAL1(extsb, 0x1D);
+/* extsh & extsh. */
+GEN_LOGICAL1(extsh, 0x1C);
+/* nand & nand. */
+GEN_LOGICAL2(nand, 0x0E);
+/* nor & nor. */
+GEN_LOGICAL2(nor, 0x03);
+
+/* or & or. */
+GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ /* Optimisation for mr case */
+ if (rS(ctx->opcode) != rB(ctx->opcode)) {
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_or();
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* orc & orc. */
+GEN_LOGICAL2(orc, 0x0C);
+/* xor & xor. */
+GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ /* Optimisation for "set to zero" case */
+ if (rS(ctx->opcode) != rB(ctx->opcode)) {
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_xor();
+ } else {
+ gen_op_set_T0(0);
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* ori */
+GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_ori(uimm);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* oris */
+GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_ori(uimm << 16);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* xori */
+GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_xori(uimm);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* xoris */
+GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_xori(uimm << 16);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/*** Integer rotate ***/
+/* rlwimi & rlwimi. */
+GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rA(ctx->opcode));
+ gen_op_rlwimi(SH(ctx->opcode), MASK(mb, me), ~MASK(mb, me));
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* rlwinm & rlwinm. */
+GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me, sh;
+
+ sh = SH(ctx->opcode);
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+#if 1 // TRY
+ if (sh == 0) {
+ gen_op_andi_(MASK(mb, me));
+ goto store;
+ }
+#endif
+ if (mb == 0) {
+ if (me == 31) {
+ gen_op_rotlwi(sh);
+ goto store;
+#if 0
+ } else if (me == (31 - sh)) {
+ gen_op_slwi(sh);
+ goto store;
+#endif
+ }
+ } else if (me == 31) {
+#if 0
+ if (sh == (32 - mb)) {
+ gen_op_srwi(mb);
+ goto store;
+ }
+#endif
+ }
+ gen_op_rlwinm(sh, MASK(mb, me));
+store:
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* rlwnm & rlwnm. */
+GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ if (mb == 0 && me == 31) {
+ gen_op_rotl();
+ } else
+ {
+ gen_op_rlwnm(MASK(mb, me));
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/*** Integer shift ***/
+/* slw & slw. */
+__GEN_LOGICAL2(slw, 0x18, 0x00);
+/* sraw & sraw. */
+__GEN_LOGICAL2(sraw, 0x18, 0x18);
+/* srawi & srawi. */
+GEN_HANDLER(srawi, 0x1F, 0x18, 0x19, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (SH(ctx->opcode) != 0)
+ gen_op_srawi(SH(ctx->opcode), MASK(32 - SH(ctx->opcode), 31));
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* srw & srw. */
+__GEN_LOGICAL2(srw, 0x18, 0x10);
+
+/*** Floating-Point arithmetic ***/
+#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rC(ctx->opcode)); \
+ gen_op_load_fpr_FT2(rB(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+#define GEN_FLOAT_ACB(name, op2) \
+_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0); \
+_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1);
+
+#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rB(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+#define GEN_FLOAT_AB(name, op2, inval) \
+_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0); \
+_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1);
+
+#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rC(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+#define GEN_FLOAT_AC(name, op2, inval) \
+_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0); \
+_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1);
+
+#define GEN_FLOAT_B(name, op2, op3) \
+GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rB(ctx->opcode)); \
+ gen_op_f##name(); \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+#define GEN_FLOAT_BS(name, op1, op2) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rB(ctx->opcode)); \
+ gen_op_f##name(); \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+/* fadd - fadds */
+GEN_FLOAT_AB(add, 0x15, 0x000007C0);
+/* fdiv - fdivs */
+GEN_FLOAT_AB(div, 0x12, 0x000007C0);
+/* fmul - fmuls */
+GEN_FLOAT_AC(mul, 0x19, 0x0000F800);
+
+/* fres */
+GEN_FLOAT_BS(res, 0x3B, 0x18);
+
+/* frsqrte */
+GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A);
+
+/* fsel */
+_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0);
+/* fsub - fsubs */
+GEN_FLOAT_AB(sub, 0x14, 0x000007C0);
+/* Optional: */
+/* fsqrt */
+GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_OPT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_fsqrt();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_OPT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_fsqrt();
+ gen_op_frsp();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/*** Floating-Point multiply-and-add ***/
+/* fmadd - fmadds */
+GEN_FLOAT_ACB(madd, 0x1D);
+/* fmsub - fmsubs */
+GEN_FLOAT_ACB(msub, 0x1C);
+/* fnmadd - fnmadds */
+GEN_FLOAT_ACB(nmadd, 0x1F);
+/* fnmsub - fnmsubs */
+GEN_FLOAT_ACB(nmsub, 0x1E);
+
+/*** Floating-Point round & convert ***/
+/* fctiw */
+GEN_FLOAT_B(ctiw, 0x0E, 0x00);
+/* fctiwz */
+GEN_FLOAT_B(ctiwz, 0x0F, 0x00);
+/* frsp */
+GEN_FLOAT_B(rsp, 0x0C, 0x00);
+
+/*** Floating-Point compare ***/
+/* fcmpo */
+GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rA(ctx->opcode));
+ gen_op_load_fpr_FT1(rB(ctx->opcode));
+ gen_op_fcmpo();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/* fcmpu */
+GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rA(ctx->opcode));
+ gen_op_load_fpr_FT1(rB(ctx->opcode));
+ gen_op_fcmpu();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** Floating-point move ***/
+/* fabs */
+GEN_FLOAT_B(abs, 0x08, 0x08);
+
+/* fmr - fmr. */
+GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* fnabs */
+GEN_FLOAT_B(nabs, 0x08, 0x04);
+/* fneg */
+GEN_FLOAT_B(neg, 0x08, 0x01);
+
+/*** Floating-Point status & ctrl register ***/
+/* mcrfs */
+GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpscr_T0(crfS(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+ gen_op_clear_fpscr(crfS(ctx->opcode));
+}
+
+/* mffs */
+GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpscr();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsb0 */
+GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT)
+{
+ uint8_t crb;
+
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ crb = crbD(ctx->opcode) >> 2;
+ gen_op_load_fpscr_T0(crb);
+ gen_op_andi_(~(1 << (crbD(ctx->opcode) & 0x03)));
+ gen_op_store_T0_fpscr(crb);
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsb1 */
+GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT)
+{
+ uint8_t crb;
+
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ crb = crbD(ctx->opcode) >> 2;
+ gen_op_load_fpscr_T0(crb);
+ gen_op_ori(1 << (crbD(ctx->opcode) & 0x03));
+ gen_op_store_T0_fpscr(crb);
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsf */
+GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x02010000, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_store_fpscr(FM(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsfi */
+GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006f0800, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_store_T0_fpscri(crbD(ctx->opcode) >> 2, FPIMM(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/*** Integer load ***/
+#define op_ldst(name) (*gen_op_##name[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_l##width[] = { \
+ &gen_op_l##width##_raw, \
+ &gen_op_l##width##_le_raw, \
+};
+#define OP_ST_TABLE(width) \
+static GenOpFunc *gen_op_st##width[] = { \
+ &gen_op_st##width##_raw, \
+ &gen_op_st##width##_le_raw, \
+};
+/* Byte access routine are endian safe */
+#define gen_op_stb_le_raw gen_op_stb_raw
+#define gen_op_lbz_le_raw gen_op_lbz_raw
+#else
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_l##width[] = { \
+ &gen_op_l##width##_user, \
+ &gen_op_l##width##_le_user, \
+ &gen_op_l##width##_kernel, \
+ &gen_op_l##width##_le_kernel, \
+};
+#define OP_ST_TABLE(width) \
+static GenOpFunc *gen_op_st##width[] = { \
+ &gen_op_st##width##_user, \
+ &gen_op_st##width##_le_user, \
+ &gen_op_st##width##_kernel, \
+ &gen_op_st##width##_le_kernel, \
+};
+/* Byte access routine are endian safe */
+#define gen_op_stb_le_user gen_op_stb_user
+#define gen_op_lbz_le_user gen_op_lbz_user
+#define gen_op_stb_le_kernel gen_op_stb_kernel
+#define gen_op_lbz_le_kernel gen_op_lbz_kernel
+#endif
+
+#define GEN_LD(width, opc) \
+GEN_HANDLER(l##width, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDU(width, opc) \
+GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDUX(width, opc) \
+GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDX(width, opc2, opc3) \
+GEN_HANDLER(l##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDS(width, op) \
+OP_LD_TABLE(width); \
+GEN_LD(width, op | 0x20); \
+GEN_LDU(width, op | 0x21); \
+GEN_LDUX(width, op | 0x01); \
+GEN_LDX(width, 0x17, op | 0x00)
+
+/* lbz lbzu lbzux lbzx */
+GEN_LDS(bz, 0x02);
+/* lha lhau lhaux lhax */
+GEN_LDS(ha, 0x0A);
+/* lhz lhzu lhzux lhzx */
+GEN_LDS(hz, 0x08);
+/* lwz lwzu lwzux lwzx */
+GEN_LDS(wz, 0x00);
+
+/*** Integer store ***/
+#define GEN_ST(width, opc) \
+GEN_HANDLER(st##width, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STU(width, opc) \
+GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STUX(width, opc) \
+GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STX(width, opc2, opc3) \
+GEN_HANDLER(st##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STS(width, op) \
+OP_ST_TABLE(width); \
+GEN_ST(width, op | 0x20); \
+GEN_STU(width, op | 0x21); \
+GEN_STUX(width, op | 0x01); \
+GEN_STX(width, 0x17, op | 0x00)
+
+/* stb stbu stbux stbx */
+GEN_STS(b, 0x06);
+/* sth sthu sthux sthx */
+GEN_STS(h, 0x0C);
+/* stw stwu stwux stwx */
+GEN_STS(w, 0x04);
+
+/*** Integer load and store with byte reverse ***/
+/* lhbrx */
+OP_LD_TABLE(hbr);
+GEN_LDX(hbr, 0x16, 0x18);
+/* lwbrx */
+OP_LD_TABLE(wbr);
+GEN_LDX(wbr, 0x16, 0x10);
+/* sthbrx */
+OP_ST_TABLE(hbr);
+GEN_STX(hbr, 0x16, 0x1C);
+/* stwbrx */
+OP_ST_TABLE(wbr);
+GEN_STX(wbr, 0x16, 0x14);
+
+/*** Integer load and store multiple ***/
+#define op_ldstm(name, reg) (*gen_op_##name[ctx->mem_idx])(reg)
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc1 *gen_op_lmw[] = {
+ &gen_op_lmw_raw,
+ &gen_op_lmw_le_raw,
+};
+static GenOpFunc1 *gen_op_stmw[] = {
+ &gen_op_stmw_raw,
+ &gen_op_stmw_le_raw,
+};
+#else
+static GenOpFunc1 *gen_op_lmw[] = {
+ &gen_op_lmw_user,
+ &gen_op_lmw_le_user,
+ &gen_op_lmw_kernel,
+ &gen_op_lmw_le_kernel,
+};
+static GenOpFunc1 *gen_op_stmw[] = {
+ &gen_op_stmw_user,
+ &gen_op_stmw_le_user,
+ &gen_op_stmw_kernel,
+ &gen_op_stmw_le_kernel,
+};
+#endif
+
+/* lmw */
+GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ if (simm != 0)
+ gen_op_addi(simm);
+ }
+ op_ldstm(lmw, rD(ctx->opcode));
+}
+
+/* stmw */
+GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ if (simm != 0)
+ gen_op_addi(simm);
+ }
+ op_ldstm(stmw, rS(ctx->opcode));
+}
+
+/*** Integer load and store strings ***/
+#define op_ldsts(name, start) (*gen_op_##name[ctx->mem_idx])(start)
+#define op_ldstsx(name, rd, ra, rb) (*gen_op_##name[ctx->mem_idx])(rd, ra, rb)
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc1 *gen_op_lswi[] = {
+ &gen_op_lswi_raw,
+ &gen_op_lswi_le_raw,
+};
+static GenOpFunc3 *gen_op_lswx[] = {
+ &gen_op_lswx_raw,
+ &gen_op_lswx_le_raw,
+};
+static GenOpFunc1 *gen_op_stsw[] = {
+ &gen_op_stsw_raw,
+ &gen_op_stsw_le_raw,
+};
+#else
+static GenOpFunc1 *gen_op_lswi[] = {
+ &gen_op_lswi_user,
+ &gen_op_lswi_le_user,
+ &gen_op_lswi_kernel,
+ &gen_op_lswi_le_kernel,
+};
+static GenOpFunc3 *gen_op_lswx[] = {
+ &gen_op_lswx_user,
+ &gen_op_lswx_le_user,
+ &gen_op_lswx_kernel,
+ &gen_op_lswx_le_kernel,
+};
+static GenOpFunc1 *gen_op_stsw[] = {
+ &gen_op_stsw_user,
+ &gen_op_stsw_le_user,
+ &gen_op_stsw_kernel,
+ &gen_op_stsw_le_kernel,
+};
+#endif
+
+/* lswi */
+/* PowerPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_INTEGER)
+{
+ int nb = NB(ctx->opcode);
+ int start = rD(ctx->opcode);
+ int ra = rA(ctx->opcode);
+ int nr;
+
+ if (nb == 0)
+ nb = 32;
+ nr = nb / 4;
+ if (((start + nr) > 32 && start <= ra && (start + nr - 32) > ra) ||
+ ((start + nr) <= 32 && start <= ra && (start + nr) > ra)) {
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ return;
+ }
+ if (ra == 0) {
+ gen_op_set_T0(0);
+ } else {
+ gen_op_load_gpr_T0(ra);
+ }
+ gen_op_set_T1(nb);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(lswi, start);
+}
+
+/* lswx */
+GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_INTEGER)
+{
+ int ra = rA(ctx->opcode);
+ int rb = rB(ctx->opcode);
+
+ if (ra == 0) {
+ gen_op_load_gpr_T0(rb);
+ ra = rb;
+ } else {
+ gen_op_load_gpr_T0(ra);
+ gen_op_load_gpr_T1(rb);
+ gen_op_add();
+ }
+ gen_op_load_xer_bc();
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldstsx(lswx, rD(ctx->opcode), ra, rb);
+}
+
+/* stswi */
+GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_INTEGER)
+{
+ int nb = NB(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(0);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ }
+ if (nb == 0)
+ nb = 32;
+ gen_op_set_T1(nb);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(stsw, rS(ctx->opcode));
+}
+
+/* stswx */
+GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_INTEGER)
+{
+ int ra = rA(ctx->opcode);
+
+ if (ra == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ ra = rB(ctx->opcode);
+ } else {
+ gen_op_load_gpr_T0(ra);
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_xer_bc();
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(stsw, rS(ctx->opcode));
+}
+
+/*** Memory synchronisation ***/
+/* eieio */
+GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FF0801, PPC_MEM)
+{
+}
+
+/* isync */
+GEN_HANDLER(isync, 0x13, 0x16, 0xFF, 0x03FF0801, PPC_MEM)
+{
+}
+
+#define op_lwarx() (*gen_op_lwarx[ctx->mem_idx])()
+#define op_stwcx() (*gen_op_stwcx[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc *gen_op_lwarx[] = {
+ &gen_op_lwarx_raw,
+ &gen_op_lwarx_le_raw,
+};
+static GenOpFunc *gen_op_stwcx[] = {
+ &gen_op_stwcx_raw,
+ &gen_op_stwcx_le_raw,
+};
+#else
+static GenOpFunc *gen_op_lwarx[] = {
+ &gen_op_lwarx_user,
+ &gen_op_lwarx_le_user,
+ &gen_op_lwarx_kernel,
+ &gen_op_lwarx_le_kernel,
+};
+static GenOpFunc *gen_op_stwcx[] = {
+ &gen_op_stwcx_user,
+ &gen_op_stwcx_le_user,
+ &gen_op_stwcx_kernel,
+ &gen_op_stwcx_le_kernel,
+};
+#endif
+
+/* lwarx */
+GEN_HANDLER(lwarx, 0x1F, 0x14, 0xFF, 0x00000001, PPC_RES)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_lwarx();
+ gen_op_store_T1_gpr(rD(ctx->opcode));
+}
+
+/* stwcx. */
+GEN_HANDLER(stwcx_, 0x1F, 0x16, 0x04, 0x00000000, PPC_RES)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_gpr_T1(rS(ctx->opcode));
+ op_stwcx();
+}
+
+/* sync */
+GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x03FF0801, PPC_MEM)
+{
+}
+
+/*** Floating-point load ***/
+#define GEN_LDF(width, opc) \
+GEN_HANDLER(l##width, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDUF(width, opc) \
+GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDUXF(width, opc) \
+GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDXF(width, opc2, opc3) \
+GEN_HANDLER(l##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDFS(width, op) \
+OP_LD_TABLE(width); \
+GEN_LDF(width, op | 0x20); \
+GEN_LDUF(width, op | 0x21); \
+GEN_LDUXF(width, op | 0x01); \
+GEN_LDXF(width, 0x17, op | 0x00)
+
+/* lfd lfdu lfdux lfdx */
+GEN_LDFS(fd, 0x12);
+/* lfs lfsu lfsux lfsx */
+GEN_LDFS(fs, 0x10);
+
+/*** Floating-point store ***/
+#define GEN_STF(width, opc) \
+GEN_HANDLER(st##width, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STUF(width, opc) \
+GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STUXF(width, opc) \
+GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STXF(width, opc2, opc3) \
+GEN_HANDLER(st##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STFS(width, op) \
+OP_ST_TABLE(width); \
+GEN_STF(width, op | 0x20); \
+GEN_STUF(width, op | 0x21); \
+GEN_STUXF(width, op | 0x01); \
+GEN_STXF(width, 0x17, op | 0x00)
+
+/* stfd stfdu stfdux stfdx */
+GEN_STFS(fd, 0x16);
+/* stfs stfsu stfsux stfsx */
+GEN_STFS(fs, 0x14);
+
+/* Optional: */
+/* stfiwx */
+GEN_HANDLER(stfiwx, 0x1F, 0x17, 0x1E, 0x00000001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ RET_INVAL(ctx);
+}
+
+/*** Branch ***/
+
+static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ if (n == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_op_set_T1(dest);
+ gen_op_b_T1();
+ gen_op_set_T0((long)tb + n);
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_exit_tb();
+ } else {
+ gen_op_set_T1(dest);
+ gen_op_b_T1();
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_set_T0(0);
+ gen_op_exit_tb();
+ }
+}
+
+/* b ba bl bla */
+GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ uint32_t li, target;
+
+ /* sign extend LI */
+ li = ((int32_t)LI(ctx->opcode) << 6) >> 6;
+
+ if (AA(ctx->opcode) == 0)
+ target = ctx->nip + li - 4;
+ else
+ target = li;
+ if (LK(ctx->opcode)) {
+ gen_op_setlr(ctx->nip);
+ }
+ gen_goto_tb(ctx, 0, target);
+ ctx->exception = EXCP_BRANCH;
+}
+
+#define BCOND_IM 0
+#define BCOND_LR 1
+#define BCOND_CTR 2
+
+static inline void gen_bcond(DisasContext *ctx, int type)
+{
+ uint32_t target = 0;
+ uint32_t bo = BO(ctx->opcode);
+ uint32_t bi = BI(ctx->opcode);
+ uint32_t mask;
+ uint32_t li;
+
+ if ((bo & 0x4) == 0)
+ gen_op_dec_ctr();
+ switch(type) {
+ case BCOND_IM:
+ li = (int32_t)((int16_t)(BD(ctx->opcode)));
+ if (AA(ctx->opcode) == 0) {
+ target = ctx->nip + li - 4;
+ } else {
+ target = li;
+ }
+ break;
+ case BCOND_CTR:
+ gen_op_movl_T1_ctr();
+ break;
+ default:
+ case BCOND_LR:
+ gen_op_movl_T1_lr();
+ break;
+ }
+ if (LK(ctx->opcode)) {
+ gen_op_setlr(ctx->nip);
+ }
+ if (bo & 0x10) {
+ /* No CR condition */
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr();
+ break;
+ case 2:
+ gen_op_test_ctrz();
+ break;
+ default:
+ case 4:
+ case 6:
+ if (type == BCOND_IM) {
+ gen_goto_tb(ctx, 0, target);
+ } else {
+ gen_op_b_T1();
+ }
+ goto no_test;
+ }
+ } else {
+ mask = 1 << (3 - (bi & 0x03));
+ gen_op_load_crf_T0(bi >> 2);
+ if (bo & 0x8) {
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr_true(mask);
+ break;
+ case 2:
+ gen_op_test_ctrz_true(mask);
+ break;
+ default:
+ case 4:
+ case 6:
+ gen_op_test_true(mask);
+ break;
+ }
+ } else {
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr_false(mask);
+ break;
+ case 2:
+ gen_op_test_ctrz_false(mask);
+ break;
+ default:
+ case 4:
+ case 6:
+ gen_op_test_false(mask);
+ break;
+ }
+ }
+ }
+ if (type == BCOND_IM) {
+ int l1 = gen_new_label();
+ gen_op_jz_T0(l1);
+ gen_goto_tb(ctx, 0, target);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 1, ctx->nip);
+ } else {
+ gen_op_btest_T1(ctx->nip);
+ }
+ no_test:
+ ctx->exception = EXCP_BRANCH;
+}
+
+GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_IM);
+}
+
+GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_CTR);
+}
+
+GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_LR);
+}
+
+/*** Condition register logical ***/
+#define GEN_CRLOGIC(op, opc) \
+GEN_HANDLER(cr##op, 0x13, 0x01, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ gen_op_load_crf_T0(crbA(ctx->opcode) >> 2); \
+ gen_op_getbit_T0(3 - (crbA(ctx->opcode) & 0x03)); \
+ gen_op_load_crf_T1(crbB(ctx->opcode) >> 2); \
+ gen_op_getbit_T1(3 - (crbB(ctx->opcode) & 0x03)); \
+ gen_op_##op(); \
+ gen_op_load_crf_T1(crbD(ctx->opcode) >> 2); \
+ gen_op_setcrfbit(~(1 << (3 - (crbD(ctx->opcode) & 0x03))), \
+ 3 - (crbD(ctx->opcode) & 0x03)); \
+ gen_op_store_T1_crf(crbD(ctx->opcode) >> 2); \
+}
+
+/* crand */
+GEN_CRLOGIC(and, 0x08)
+/* crandc */
+GEN_CRLOGIC(andc, 0x04)
+/* creqv */
+GEN_CRLOGIC(eqv, 0x09)
+/* crnand */
+GEN_CRLOGIC(nand, 0x07)
+/* crnor */
+GEN_CRLOGIC(nor, 0x01)
+/* cror */
+GEN_CRLOGIC(or, 0x0E)
+/* crorc */
+GEN_CRLOGIC(orc, 0x0D)
+/* crxor */
+GEN_CRLOGIC(xor, 0x06)
+/* mcrf */
+GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER)
+{
+ gen_op_load_crf_T0(crfS(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** System linkage ***/
+/* rfi (supervisor only) */
+GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ /* Restore CPU state */
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_rfi();
+ RET_CHG_FLOW(ctx);
+#endif
+}
+
+/* sc */
+GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFFFFD, PPC_FLOW)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_EXCP(ctx, EXCP_SYSCALL_USER, 0);
+#else
+ RET_EXCP(ctx, EXCP_SYSCALL, 0);
+#endif
+}
+
+/*** Trap ***/
+/* tw */
+GEN_HANDLER(tw, 0x1F, 0x04, 0xFF, 0x00000001, PPC_FLOW)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_tw(TO(ctx->opcode));
+}
+
+/* twi */
+GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+#if 0
+ printf("%s: param=0x%04x T0=0x%04x\n", __func__,
+ SIMM(ctx->opcode), TO(ctx->opcode));
+#endif
+ gen_op_twi(SIMM(ctx->opcode), TO(ctx->opcode));
+}
+
+/*** Processor control ***/
+static inline int check_spr_access (int spr, int rw, int supervisor)
+{
+ uint32_t rights = spr_access[spr >> 1] >> (4 * (spr & 1));
+
+#if 0
+ if (spr != LR && spr != CTR) {
+ if (loglevel > 0) {
+ fprintf(logfile, "%s reg=%d s=%d rw=%d r=0x%02x 0x%02x\n", __func__,
+ SPR_ENCODE(spr), supervisor, rw, rights,
+ (rights >> ((2 * supervisor) + rw)) & 1);
+ } else {
+ printf("%s reg=%d s=%d rw=%d r=0x%02x 0x%02x\n", __func__,
+ SPR_ENCODE(spr), supervisor, rw, rights,
+ (rights >> ((2 * supervisor) + rw)) & 1);
+ }
+ }
+#endif
+ if (rights == 0)
+ return -1;
+ rights = rights >> (2 * supervisor);
+ rights = rights >> rw;
+
+ return rights & 1;
+}
+
+/* mcrxr */
+GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC)
+{
+ gen_op_load_xer_cr();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+ gen_op_clear_xer_cr();
+}
+
+/* mfcr */
+GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x001FF801, PPC_MISC)
+{
+ gen_op_load_cr();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/* mfmsr */
+GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_msr();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+#if 0
+#define SPR_NOACCESS ((void *)(-1))
+#else
+static void spr_noaccess (void *opaque, int sprn)
+{
+ sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+ printf("ERROR: try to access SPR %d !\n", sprn);
+}
+#define SPR_NOACCESS (&spr_noaccess)
+#endif
+
+/* mfspr */
+static inline void gen_op_mfspr (DisasContext *ctx)
+{
+ void (*read_cb)(void *opaque, int sprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->supervisor)
+ read_cb = ctx->spr_cb[sprn].oea_read;
+ else
+#endif
+ read_cb = ctx->spr_cb[sprn].uea_read;
+ if (read_cb != NULL) {
+ if (read_cb != SPR_NOACCESS) {
+ (*read_cb)(ctx, sprn);
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+ } else {
+ /* Privilege exception */
+ if (loglevel) {
+ fprintf(logfile, "Trying to read priviledged spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to read priviledged spr %d %03x\n", sprn, sprn);
+ RET_PRIVREG(ctx);
+ }
+ } else {
+ /* Not defined */
+ if (loglevel) {
+ fprintf(logfile, "Trying to read invalid spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to read invalid spr %d %03x\n", sprn, sprn);
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
+ }
+}
+
+GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC)
+{
+ gen_op_mfspr(ctx);
+ }
+
+/* mftb */
+GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_TB)
+{
+ gen_op_mfspr(ctx);
+}
+
+/* mtcrf */
+/* The mask should be 0x00100801, but Mac OS X 10.4 use an alternate form */
+GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_cr(CRM(ctx->opcode));
+}
+
+/* mtmsr */
+GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_update_nip((ctx)->nip);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_msr();
+ /* Must stop the translation as machine state (may have) changed */
+ RET_CHG_FLOW(ctx);
+#endif
+}
+
+/* mtspr */
+GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC)
+{
+ void (*write_cb)(void *opaque, int sprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->supervisor)
+ write_cb = ctx->spr_cb[sprn].oea_write;
+ else
+#endif
+ write_cb = ctx->spr_cb[sprn].uea_write;
+ if (write_cb != NULL) {
+ if (write_cb != SPR_NOACCESS) {
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ (*write_cb)(ctx, sprn);
+ } else {
+ /* Privilege exception */
+ if (loglevel) {
+ fprintf(logfile, "Trying to write priviledged spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to write priviledged spr %d %03x\n", sprn, sprn);
+ RET_PRIVREG(ctx);
+ }
+ } else {
+ /* Not defined */
+ if (loglevel) {
+ fprintf(logfile, "Trying to write invalid spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to write invalid spr %d %03x\n", sprn, sprn);
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
+ }
+}
+
+/*** Cache management ***/
+/* For now, all those will be implemented as nop:
+ * this is valid, regarding the PowerPC specs...
+ * We just have to flush tb while invalidating instruction cache lines...
+ */
+/* dcbf */
+GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+}
+
+/* dcbi (Supervisor only) */
+GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+ op_ldst(stb);
+#endif
+}
+
+/* dcdst */
+GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+}
+
+/* dcbt */
+GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x03E00001, PPC_CACHE)
+{
+}
+
+/* dcbtst */
+GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x03E00001, PPC_CACHE)
+{
+}
+
+/* dcbz */
+#if defined(CONFIG_USER_ONLY)
+#define op_dcbz() gen_op_dcbz_raw()
+#else
+#define op_dcbz() (*gen_op_dcbz[ctx->mem_idx])()
+static GenOpFunc *gen_op_dcbz[] = {
+ &gen_op_dcbz_user,
+ &gen_op_dcbz_user,
+ &gen_op_dcbz_kernel,
+ &gen_op_dcbz_kernel,
+};
+#endif
+
+GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_dcbz();
+ gen_op_check_reservation();
+}
+
+/* icbi */
+GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_icbi();
+}
+
+/* Optional: */
+/* dcba */
+GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_OPT)
+{
+}
+
+/*** Segment register manipulation ***/
+/* Supervisor only: */
+/* mfsr */
+GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_sr(SR(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+/* mfsrin */
+GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_load_srin();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+/* mtsr */
+GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_sr(SR(ctx->opcode));
+ RET_STOP(ctx);
+#endif
+}
+
+/* mtsrin */
+GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_store_srin();
+ RET_STOP(ctx);
+#endif
+}
+
+/*** Lookaside buffer management ***/
+/* Optional & supervisor only: */
+/* tlbia */
+GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ if (loglevel)
+ fprintf(logfile, "%s: ! supervisor\n", __func__);
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_tlbia();
+ RET_STOP(ctx);
+#endif
+}
+
+/* tlbie */
+GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ gen_op_tlbie();
+ RET_STOP(ctx);
+#endif
+}
+
+/* tlbsync */
+GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ /* This has no effect: it should ensure that all previous
+ * tlbie have completed
+ */
+ RET_STOP(ctx);
+#endif
+}
+
+/*** External control ***/
+/* Optional: */
+#define op_eciwx() (*gen_op_eciwx[ctx->mem_idx])()
+#define op_ecowx() (*gen_op_ecowx[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc *gen_op_eciwx[] = {
+ &gen_op_eciwx_raw,
+ &gen_op_eciwx_le_raw,
+};
+static GenOpFunc *gen_op_ecowx[] = {
+ &gen_op_ecowx_raw,
+ &gen_op_ecowx_le_raw,
+};
+#else
+static GenOpFunc *gen_op_eciwx[] = {
+ &gen_op_eciwx_user,
+ &gen_op_eciwx_le_user,
+ &gen_op_eciwx_kernel,
+ &gen_op_eciwx_le_kernel,
+};
+static GenOpFunc *gen_op_ecowx[] = {
+ &gen_op_ecowx_user,
+ &gen_op_ecowx_le_user,
+ &gen_op_ecowx_kernel,
+ &gen_op_ecowx_le_kernel,
+};
+#endif
+
+/* eciwx */
+GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN)
+{
+ /* Should check EAR[E] & alignment ! */
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_eciwx();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/* ecowx */
+GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN)
+{
+ /* Should check EAR[E] & alignment ! */
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_gpr_T2(rS(ctx->opcode));
+ op_ecowx();
+}
+
+/* End opcode list */
+GEN_OPCODE_MARK(end);
+
+#include "translate_init.c"
+
+/*****************************************************************************/
+/* Misc PowerPC helpers */
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+#if defined(TARGET_PPC64) || 1
+#define FILL ""
+#define REGX "%016" PRIx64
+#define RGPL 4
+#define RFPL 4
+#else
+#define FILL " "
+#define REGX "%08" PRIx64
+#define RGPL 8
+#define RFPL 4
+#endif
+
+ int i;
+
+ cpu_fprintf(f, "NIP " REGX " LR " REGX " CTR " REGX "\n",
+ env->nip, env->lr, env->ctr);
+ cpu_fprintf(f, "MSR " REGX FILL " XER %08x TB %08x %08x DECR %08x\n",
+ do_load_msr(env), do_load_xer(env), cpu_ppc_load_tbu(env),
+ cpu_ppc_load_tbl(env), cpu_ppc_load_decr(env));
+ for (i = 0; i < 32; i++) {
+ if ((i & (RGPL - 1)) == 0)
+ cpu_fprintf(f, "GPR%02d", i);
+ cpu_fprintf(f, " " REGX, env->gpr[i]);
+ if ((i & (RGPL - 1)) == (RGPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "CR ");
+ for (i = 0; i < 8; i++)
+ cpu_fprintf(f, "%01x", env->crf[i]);
+ cpu_fprintf(f, " [");
+ for (i = 0; i < 8; i++) {
+ char a = '-';
+ if (env->crf[i] & 0x08)
+ a = 'L';
+ else if (env->crf[i] & 0x04)
+ a = 'G';
+ else if (env->crf[i] & 0x02)
+ a = 'E';
+ cpu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' ');
+ }
+ cpu_fprintf(f, " ] " FILL "RES " REGX "\n", env->reserve);
+ for (i = 0; i < 32; i++) {
+ if ((i & (RFPL - 1)) == 0)
+ cpu_fprintf(f, "FPR%02d", i);
+ cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i]));
+ if ((i & (RFPL - 1)) == (RFPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "SRR0 " REGX " SRR1 " REGX " " FILL FILL FILL
+ "SDR1 " REGX "\n",
+ env->spr[SPR_SRR0], env->spr[SPR_SRR1], env->sdr1);
+
+#undef REGX
+#undef RGPL
+#undef RFPL
+#undef FILL
+}
+
+/*****************************************************************************/
+int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx, *ctxp = &ctx;
+ opc_handler_t **table, *handler;
+ target_ulong pc_start;
+ uint16_t *gen_opc_end;
+ int j, lj = -1;
+
+ pc_start = tb->pc;
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ nb_gen_labels = 0;
+ ctx.nip = pc_start;
+ ctx.tb = tb;
+ ctx.exception = EXCP_NONE;
+ ctx.spr_cb = env->spr_cb;
+#if defined(CONFIG_USER_ONLY)
+ ctx.mem_idx = msr_le;
+#else
+ ctx.supervisor = 1 - msr_pr;
+ ctx.mem_idx = ((1 - msr_pr) << 1) | msr_le;
+#endif
+ ctx.fpu_enabled = msr_fp;
+ ctx.singlestep_enabled = env->singlestep_enabled;
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr_se = 1;
+#endif
+ /* Set env in case of segfault during code fetch */
+ while (ctx.exception == EXCP_NONE && gen_opc_ptr < gen_opc_end) {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == ctx.nip) {
+ gen_op_update_nip(ctx.nip);
+ gen_op_debug();
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ gen_opc_pc[lj] = ctx.nip;
+ gen_opc_instr_start[lj] = 1;
+ }
+ }
+#if defined PPC_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "----------------\n");
+ fprintf(logfile, "nip=%08x super=%d ir=%d\n",
+ ctx.nip, 1 - msr_pr, msr_ir);
+ }
+#endif
+ ctx.opcode = ldl_code(ctx.nip);
+ if (msr_le) {
+ ctx.opcode = ((ctx.opcode & 0xFF000000) >> 24) |
+ ((ctx.opcode & 0x00FF0000) >> 8) |
+ ((ctx.opcode & 0x0000FF00) << 8) |
+ ((ctx.opcode & 0x000000FF) << 24);
+ }
+#if defined PPC_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "translate opcode %08x (%02x %02x %02x) (%s)\n",
+ ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), msr_le ? "little" : "big");
+ }
+#endif
+ ctx.nip += 4;
+ table = env->opcodes;
+ handler = table[opc1(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc2(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc3(ctx.opcode)];
+ }
+ }
+ /* Is opcode *REALLY* valid ? */
+ if (handler->handler == &gen_invalid) {
+ if (loglevel > 0) {
+ fprintf(logfile, "invalid/unsupported opcode: "
+ "%02x - %02x - %02x (%08x) 0x%08x %d\n",
+ opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, msr_ir);
+ } else {
+ printf("invalid/unsupported opcode: "
+ "%02x - %02x - %02x (%08x) 0x%08x %d\n",
+ opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, msr_ir);
+ }
+ } else {
+ if ((ctx.opcode & handler->inval) != 0) {
+ if (loglevel > 0) {
+ fprintf(logfile, "invalid bits: %08x for opcode: "
+ "%02x -%02x - %02x (0x%08x) (0x%08x)\n",
+ ctx.opcode & handler->inval, opc1(ctx.opcode),
+ opc2(ctx.opcode), opc3(ctx.opcode),
+ ctx.opcode, ctx.nip - 4);
+ } else {
+ printf("invalid bits: %08x for opcode: "
+ "%02x -%02x - %02x (0x%08x) (0x%08x)\n",
+ ctx.opcode & handler->inval, opc1(ctx.opcode),
+ opc2(ctx.opcode), opc3(ctx.opcode),
+ ctx.opcode, ctx.nip - 4);
+ }
+ RET_INVAL(ctxp);
+ break;
+ }
+ }
+ (*(handler->handler))(&ctx);
+ /* Check trace mode exceptions */
+ if ((msr_be && ctx.exception == EXCP_BRANCH) ||
+ /* Check in single step trace mode
+ * we need to stop except if:
+ * - rfi, trap or syscall
+ * - first instruction of an exception handler
+ */
+ (msr_se && (ctx.nip < 0x100 ||
+ ctx.nip > 0xF00 ||
+ (ctx.nip & 0xFC) != 0x04) &&
+ ctx.exception != EXCP_SYSCALL &&
+ ctx.exception != EXCP_SYSCALL_USER &&
+ ctx.exception != EXCP_TRAP)) {
+ RET_EXCP(ctxp, EXCP_TRACE, 0);
+ }
+
+ /* if we reach a page boundary or are single stepping, stop
+ * generation
+ */
+ if (((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) ||
+ (env->singlestep_enabled)) {
+ break;
+ }
+#if defined (DO_SINGLE_STEP)
+ break;
+#endif
+ }
+ if (ctx.exception == EXCP_NONE) {
+ gen_goto_tb(&ctx, 0, ctx.nip);
+ } else if (ctx.exception != EXCP_BRANCH) {
+ gen_op_set_T0(0);
+ }
+#if 1
+ /* TO BE FIXED: T0 hasn't got a proper value, which makes tb_add_jump
+ * do bad business and then qemu crashes !
+ */
+ gen_op_set_T0(0);
+#endif
+ /* Generate the return instruction */
+ gen_op_exit_tb();
+ *gen_opc_ptr = INDEX_op_end;
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ tb->size = 0;
+#if 0
+ if (loglevel > 0) {
+ page_dump(logfile);
+ }
+#endif
+ } else {
+ tb->size = ctx.nip - pc_start;
+ }
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_CPU) {
+ fprintf(logfile, "---------------- excp: %04x\n", ctx.exception);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+ target_disas(logfile, pc_start, ctx.nip - pc_start, msr_le);
+ fprintf(logfile, "\n");
+ }
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+#endif
+ return 0;
+}
+
+int gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
new file mode 100644
index 0000000..ddf0c91
--- /dev/null
+++ b/target-ppc/translate_init.c
@@ -0,0 +1,2067 @@
+/*
+ * PowerPC CPU initialization for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* A lot of PowerPC definition have been included here.
+ * Most of them are not usable for now but have been kept
+ * inside "#if defined(TODO) ... #endif" statements to make tests easier.
+ */
+
+//#define PPC_DUMP_CPU
+//#define PPC_DEBUG_SPR
+
+struct ppc_def_t {
+ const unsigned char *name;
+ uint32_t pvr;
+ uint32_t pvr_mask;
+ uint32_t insns_flags;
+ uint32_t flags;
+ uint64_t msr_mask;
+};
+
+/* Generic callbacks:
+ * do nothing but store/retrieve spr value
+ */
+static void spr_read_generic (void *opaque, int sprn)
+{
+ gen_op_load_spr(sprn);
+}
+
+static void spr_write_generic (void *opaque, int sprn)
+{
+ gen_op_store_spr(sprn);
+}
+
+/* SPR common to all PPC */
+/* XER */
+static void spr_read_xer (void *opaque, int sprn)
+{
+ gen_op_load_xer();
+}
+
+static void spr_write_xer (void *opaque, int sprn)
+{
+ gen_op_store_xer();
+}
+
+/* LR */
+static void spr_read_lr (void *opaque, int sprn)
+{
+ gen_op_load_lr();
+}
+
+static void spr_write_lr (void *opaque, int sprn)
+{
+ gen_op_store_lr();
+}
+
+/* CTR */
+static void spr_read_ctr (void *opaque, int sprn)
+{
+ gen_op_load_ctr();
+}
+
+static void spr_write_ctr (void *opaque, int sprn)
+{
+ gen_op_store_ctr();
+}
+
+/* User read access to SPR */
+/* USPRx */
+/* UMMCRx */
+/* UPMCx */
+/* USIA */
+/* UDECR */
+static void spr_read_ureg (void *opaque, int sprn)
+{
+ gen_op_load_spr(sprn + 0x10);
+}
+
+/* SPR common to all non-embedded PPC (ie not 4xx) */
+/* DECR */
+static void spr_read_decr (void *opaque, int sprn)
+{
+ gen_op_load_decr();
+}
+
+static void spr_write_decr (void *opaque, int sprn)
+{
+ gen_op_store_decr();
+}
+
+/* SPR common to all non-embedded PPC, except 601 */
+/* Time base */
+static void spr_read_tbl (void *opaque, int sprn)
+{
+ gen_op_load_tbl();
+}
+
+static void spr_write_tbl (void *opaque, int sprn)
+{
+ gen_op_store_tbl();
+}
+
+static void spr_read_tbu (void *opaque, int sprn)
+{
+ gen_op_load_tbu();
+}
+
+static void spr_write_tbu (void *opaque, int sprn)
+{
+ gen_op_store_tbu();
+}
+
+/* IBAT0U...IBAT0U */
+/* IBAT0L...IBAT7L */
+static void spr_read_ibat (void *opaque, int sprn)
+{
+ gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT0U) / 2);
+}
+
+static void spr_read_ibat_h (void *opaque, int sprn)
+{
+ gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT4U) / 2);
+}
+
+static void spr_write_ibatu (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatu((sprn - SPR_IBAT0U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatu_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatu((sprn - SPR_IBAT4U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatl (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatl((sprn - SPR_IBAT0L) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatl_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatl((sprn - SPR_IBAT4L) / 2);
+ RET_STOP(ctx);
+}
+
+/* DBAT0U...DBAT7U */
+/* DBAT0L...DBAT7L */
+static void spr_read_dbat (void *opaque, int sprn)
+{
+ gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT0U) / 2);
+}
+
+static void spr_read_dbat_h (void *opaque, int sprn)
+{
+ gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT4U) / 2);
+}
+
+static void spr_write_dbatu (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatu((sprn - SPR_DBAT0U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatu_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatu((sprn - SPR_DBAT4U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatl (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatl((sprn - SPR_DBAT0L) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatl_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatl((sprn - SPR_DBAT4L) / 2);
+ RET_STOP(ctx);
+}
+
+/* SDR1 */
+static void spr_read_sdr1 (void *opaque, int sprn)
+{
+ gen_op_load_sdr1();
+}
+
+static void spr_write_sdr1 (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_sdr1();
+ RET_STOP(ctx);
+}
+
+static void spr_write_pir (void *opaque, int sprn)
+{
+ gen_op_store_pir();
+}
+
+static inline void spr_register (CPUPPCState *env, int num,
+ const unsigned char *name,
+ void (*uea_read)(void *opaque, int sprn),
+ void (*uea_write)(void *opaque, int sprn),
+ void (*oea_read)(void *opaque, int sprn),
+ void (*oea_write)(void *opaque, int sprn),
+ target_ulong initial_value)
+{
+ ppc_spr_t *spr;
+
+ spr = &env->spr_cb[num];
+ if (spr->name != NULL ||env-> spr[num] != 0x00000000 ||
+ spr->uea_read != NULL || spr->uea_write != NULL ||
+ spr->oea_read != NULL || spr->oea_write != NULL) {
+ printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
+ exit(1);
+ }
+#if defined(PPC_DEBUG_SPR)
+ printf("*** register spr %d (%03x) %s val %08" PRIx64 "\n", num, num, name,
+ (unsigned long long)initial_value);
+#endif
+ spr->name = name;
+ spr->uea_read = uea_read;
+ spr->uea_write = uea_write;
+ spr->oea_read = oea_read;
+ spr->oea_write = oea_write;
+ env->spr[num] = initial_value;
+}
+
+/* Generic PowerPC SPRs */
+static void gen_spr_generic (CPUPPCState *env)
+{
+ /* Integer processing */
+ spr_register(env, SPR_XER, "XER",
+ &spr_read_xer, &spr_write_xer,
+ &spr_read_xer, &spr_write_xer,
+ 0x00000000);
+ /* Branch contol */
+ spr_register(env, SPR_LR, "LR",
+ &spr_read_lr, &spr_write_lr,
+ &spr_read_lr, &spr_write_lr,
+ 0x00000000);
+ spr_register(env, SPR_CTR, "CTR",
+ &spr_read_ctr, &spr_write_ctr,
+ &spr_read_ctr, &spr_write_ctr,
+ 0x00000000);
+ /* Interrupt processing */
+ spr_register(env, SPR_SRR0, "SRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SRR1, "SRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Processor control */
+ spr_register(env, SPR_SPRG0, "SPRG0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG1, "SPRG1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG2, "SPRG2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG3, "SPRG3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR common to all non-embedded PowerPC, including 601 */
+static void gen_spr_ne_601 (CPUPPCState *env)
+{
+ /* Exception processing */
+ spr_register(env, SPR_DSISR, "DSISR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_DAR, "DAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Timer */
+ spr_register(env, SPR_DECR, "DECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_decr, &spr_write_decr,
+ 0x00000000);
+ /* Memory management */
+ spr_register(env, SPR_SDR1, "SDR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_sdr1, &spr_write_sdr1,
+ 0x00000000);
+}
+
+/* BATs 0-3 */
+static void gen_low_BATs (CPUPPCState *env)
+{
+ spr_register(env, SPR_IBAT0U, "IBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT0L, "IBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1U, "IBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1L, "IBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2U, "IBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2L, "IBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3U, "IBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3L, "IBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0U, "DBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0L, "DBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1U, "DBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1L, "DBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2U, "DBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2L, "DBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3U, "DBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3L, "DBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ env->nb_BATs = 4;
+}
+
+/* BATs 4-7 */
+static void gen_high_BATs (CPUPPCState *env)
+{
+ spr_register(env, SPR_IBAT4U, "IBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT4L, "IBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5U, "IBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5L, "IBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6U, "IBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6L, "IBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7U, "IBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7L, "IBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4U, "DBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4L, "DBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5U, "DBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5L, "DBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6U, "DBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6L, "DBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7U, "DBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7L, "DBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ env->nb_BATs = 8;
+}
+
+/* Generic PowerPC time base */
+static void gen_tbl (CPUPPCState *env)
+{
+ spr_register(env, SPR_VTBL, "TBL",
+ &spr_read_tbl, SPR_NOACCESS,
+ &spr_read_tbl, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBL, "TBL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbl,
+ 0x00000000);
+ spr_register(env, SPR_VTBU, "TBU",
+ &spr_read_tbu, SPR_NOACCESS,
+ &spr_read_tbu, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBU, "TBU",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbu,
+ 0x00000000);
+}
+
+/* SPR common to all 7xx PowerPC implementations */
+static void gen_spr_7xx (CPUPPCState *env)
+{
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Cache management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTC, "ICTC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance monitors */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIA, "SIA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UMMCR0, "UMMCR0",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UMMCR1, "UMMCR1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC1, "UPMC1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC2, "UPMC2",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC3, "UPMC3",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC4, "UPMC4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_USIA, "USIA",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Thermal management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM1, "THRM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM2, "THRM2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM3, "THRM3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 604 implementation */
+static void gen_spr_604 (CPUPPCState *env)
+{
+ /* Processor identification */
+ spr_register(env, SPR_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance counters */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIA, "SIA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SDA, "SDA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+// XXX: TODO (64 bits PPC sprs)
+/*
+ * ASR => SPR 280 (64 bits)
+ * FPECR => SPR 1022 (?)
+ * VRSAVE => SPR 256 (Altivec)
+ * SCOMC => SPR 276 (64 bits ?)
+ * SCOMD => SPR 277 (64 bits ?)
+ * HSPRG0 => SPR 304 (hypervisor)
+ * HSPRG1 => SPR 305 (hypervisor)
+ * HDEC => SPR 310 (hypervisor)
+ * HIOR => SPR 311 (hypervisor)
+ * RMOR => SPR 312 (970)
+ * HRMOR => SPR 313 (hypervisor)
+ * HSRR0 => SPR 314 (hypervisor)
+ * HSRR1 => SPR 315 (hypervisor)
+ * LPCR => SPR 316 (970)
+ * LPIDR => SPR 317 (970)
+ * ... and more (thermal management, performance counters, ...)
+ */
+
+static void init_ppc_proc (CPUPPCState *env, ppc_def_t *def)
+{
+ /* Default MMU definitions */
+ env->nb_BATs = -1;
+ env->nb_tlb = 0;
+ env->nb_ways = 0;
+ /* XXX: missing:
+ * 32 bits PPC:
+ * - MPC5xx(x)
+ * - MPC8xx(x)
+ * - RCPU (MPC5xx)
+ */
+ spr_register(env, SPR_PVR, "PVR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ def->pvr);
+ switch (def->pvr & def->pvr_mask) {
+ case CPU_PPC_604: /* PPC 604 */
+ case CPU_PPC_604E: /* PPC 604e */
+ case CPU_PPC_604R: /* PPC 604r */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_604(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ case CPU_PPC_74x: /* PPC 740 / 750 */
+ case CPU_PPC_74xP: /* PPC 740P / 750P */
+ case CPU_PPC_750CXE: /* IBM PPC 750cxe */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ case CPU_PPC_750FX: /* IBM PPC 750 FX */
+ case CPU_PPC_750GX: /* IBM PPC 750 GX */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ default:
+ gen_spr_generic(env);
+ break;
+ }
+ if (env->nb_BATs == -1)
+ env->nb_BATs = 4;
+}
+
+#if defined(PPC_DUMP_CPU)
+static void dump_sprs (CPUPPCState *env)
+{
+ ppc_spr_t *spr;
+ uint32_t pvr = env->spr[SPR_PVR];
+ uint32_t sr, sw, ur, uw;
+ int i, j, n;
+
+ printf("* SPRs for PVR=%08x\n", pvr);
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < 32; j++) {
+ n = (i << 5) | j;
+ spr = &env->spr_cb[n];
+ sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS;
+ sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS;
+ uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS;
+ ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS;
+ if (sw || sr || uw || ur) {
+ printf("%4d (%03x) %8s s%c%c u%c%c\n",
+ (i << 5) | j, (i << 5) | j, spr->name,
+ sw ? 'w' : '-', sr ? 'r' : '-',
+ uw ? 'w' : '-', ur ? 'r' : '-');
+ }
+ }
+ }
+ fflush(stdout);
+ fflush(stderr);
+}
+#endif
+
+/*****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+
+int fflush (FILE *stream);
+
+/* Opcode types */
+enum {
+ PPC_DIRECT = 0, /* Opcode routine */
+ PPC_INDIRECT = 1, /* Indirect opcode table */
+};
+
+static inline int is_indirect_opcode (void *handler)
+{
+ return ((unsigned long)handler & 0x03) == PPC_INDIRECT;
+}
+
+static inline opc_handler_t **ind_table(void *handler)
+{
+ return (opc_handler_t **)((unsigned long)handler & ~3);
+}
+
+/* Instruction table creation */
+/* Opcodes tables creation */
+static void fill_new_table (opc_handler_t **table, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ table[i] = &invalid_handler;
+}
+
+static int create_new_table (opc_handler_t **table, unsigned char idx)
+{
+ opc_handler_t **tmp;
+
+ tmp = malloc(0x20 * sizeof(opc_handler_t));
+ if (tmp == NULL)
+ return -1;
+ fill_new_table(tmp, 0x20);
+ table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT);
+
+ return 0;
+}
+
+static int insert_in_table (opc_handler_t **table, unsigned char idx,
+ opc_handler_t *handler)
+{
+ if (table[idx] != &invalid_handler)
+ return -1;
+ table[idx] = handler;
+
+ return 0;
+}
+
+static int register_direct_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx, opc_handler_t *handler)
+{
+ if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in main "
+ "opcode table\n", idx);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_in_table (opc_handler_t **table,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ if (table[idx1] == &invalid_handler) {
+ if (create_new_table(table, idx1) < 0) {
+ printf("*** ERROR: unable to create indirect table "
+ "idx=%02x\n", idx1);
+ return -1;
+ }
+ } else {
+ if (!is_indirect_opcode(table[idx1])) {
+ printf("*** ERROR: idx %02x already assigned to a direct "
+ "opcode\n", idx1);
+ return -1;
+ }
+ }
+ if (handler != NULL &&
+ insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in "
+ "opcode table %02x\n", idx2, idx1);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ int ret;
+
+ ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
+
+ return ret;
+}
+
+static int register_dblind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ unsigned char idx3, opc_handler_t *handler)
+{
+ if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
+ printf("*** ERROR: unable to join indirect table idx "
+ "[%02x-%02x]\n", idx1, idx2);
+ return -1;
+ }
+ if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
+ handler) < 0) {
+ printf("*** ERROR: unable to insert opcode "
+ "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn)
+{
+ if (insn->opc2 != 0xFF) {
+ if (insn->opc3 != 0xFF) {
+ if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
+ insn->opc3, &insn->handler) < 0)
+ return -1;
+ } else {
+ if (register_ind_insn(ppc_opcodes, insn->opc1,
+ insn->opc2, &insn->handler) < 0)
+ return -1;
+ }
+ } else {
+ if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_opcode_table (opc_handler_t **table, int len)
+{
+ int i, count, tmp;
+
+ for (i = 0, count = 0; i < len; i++) {
+ /* Consistency fixup */
+ if (table[i] == NULL)
+ table[i] = &invalid_handler;
+ if (table[i] != &invalid_handler) {
+ if (is_indirect_opcode(table[i])) {
+ tmp = test_opcode_table(ind_table(table[i]), 0x20);
+ if (tmp == 0) {
+ free(table[i]);
+ table[i] = &invalid_handler;
+ } else {
+ count++;
+ }
+ } else {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+static void fix_opcode_tables (opc_handler_t **ppc_opcodes)
+{
+ if (test_opcode_table(ppc_opcodes, 0x40) == 0)
+ printf("*** WARNING: no opcode defined !\n");
+}
+
+/*****************************************************************************/
+static int create_ppc_opcodes (CPUPPCState *env, ppc_def_t *def)
+{
+ opcode_t *opc, *start, *end;
+
+ fill_new_table(env->opcodes, 0x40);
+#if defined(PPC_DUMP_CPU)
+ printf("* PPC instructions for PVR %08x: %s\n", def->pvr, def->name);
+#endif
+ if (&opc_start < &opc_end) {
+ start = &opc_start;
+ end = &opc_end;
+ } else {
+ start = &opc_end;
+ end = &opc_start;
+ }
+ for (opc = start + 1; opc != end; opc++) {
+ if ((opc->handler.type & def->insns_flags) != 0) {
+ if (register_insn(env->opcodes, opc) < 0) {
+ printf("*** ERROR initializing PPC instruction "
+ "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
+ opc->opc3);
+ return -1;
+ }
+#if defined(PPC_DUMP_CPU)
+ if (opc1 != 0x00) {
+ if (opc->opc3 == 0xFF) {
+ if (opc->opc2 == 0xFF) {
+ printf(" %02x -- -- (%2d ----) : %s\n",
+ opc->opc1, opc->opc1, opc->oname);
+ } else {
+ printf(" %02x %02x -- (%2d %4d) : %s\n",
+ opc->opc1, opc->opc2, opc->opc1, opc->opc2,
+ opc->oname);
+ }
+ } else {
+ printf(" %02x %02x %02x (%2d %4d) : %s\n",
+ opc->opc1, opc->opc2, opc->opc3,
+ opc->opc1, (opc->opc3 << 5) | opc->opc2,
+ opc->oname);
+ }
+ }
+#endif
+ }
+ }
+ fix_opcode_tables(env->opcodes);
+ fflush(stdout);
+ fflush(stderr);
+
+ return 0;
+}
+
+int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def)
+{
+ env->msr_mask = def->msr_mask;
+ env->flags = def->flags;
+ if (create_ppc_opcodes(env, def) < 0) {
+ printf("Error creating opcodes table\n");
+ fflush(stdout);
+ fflush(stderr);
+ return -1;
+ }
+ init_ppc_proc(env, def);
+#if defined(PPC_DUMP_CPU)
+ dump_sprs(env);
+#endif
+ fflush(stdout);
+ fflush(stderr);
+
+ return 0;
+}
+
+CPUPPCState *cpu_ppc_init(void)
+{
+ CPUPPCState *env;
+
+ env = qemu_mallocz(sizeof(CPUPPCState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ tlb_flush(env, 1);
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr_se = 1;
+ msr_be = 1;
+#endif
+ msr_fp = 1; /* Allow floating point exceptions */
+ msr_me = 1; /* Allow machine check exceptions */
+#if defined(CONFIG_USER_ONLY)
+ msr_pr = 1;
+#else
+ env->nip = 0xFFFFFFFC;
+#endif
+ do_compute_hflags(env);
+ env->reserve = -1;
+ return env;
+}
+
+void cpu_ppc_close(CPUPPCState *env)
+{
+ /* Should also remove all opcode tables... */
+ free(env);
+}
+
+/*****************************************************************************/
+/* PowerPC CPU definitions */
+static ppc_def_t ppc_defs[] =
+{
+ /* Embedded PPC */
+#if defined (TODO)
+ /* PPC 401 */
+ {
+ .name = "401",
+ .pvr = CPU_PPC_401,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_401,
+ .flags = PPC_FLAGS_401,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* IOP480 (401 microcontroler) */
+ {
+ .name = "iop480",
+ .pvr = CPU_PPC_IOP480,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_401,
+ .flags = PPC_FLAGS_401,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GA */
+ {
+ .name = "403ga",
+ .pvr = CPU_PPC_403GA,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GB */
+ {
+ .name = "403gb",
+ .pvr = CPU_PPC_403GB,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GC */
+ {
+ .name = "403gc",
+ .pvr = CPU_PPC_403GC,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GCX */
+ {
+ .name = "403gcx",
+ .pvr = CPU_PPC_403GCX,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 CR */
+ {
+ .name = "405cr",
+ .pvr = CPU_PPC_405,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 GP */
+ {
+ .name = "405gp",
+ .pvr = CPU_PPC_405,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 EP */
+ {
+ .name = "405ep",
+ .pvr = CPU_PPC_405EP,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 GPR */
+ {
+ .name = "405gpr",
+ .pvr = CPU_PPC_405GPR,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 D2 */
+ {
+ .name = "405d2",
+ .pvr = CPU_PPC_405D2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 D4 */
+ {
+ .name = "405d4",
+ .pvr = CPU_PPC_405D4,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* Npe405 H */
+ {
+ .name = "Npe405H",
+ .pvr = CPU_PPC_NPE405H,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* Npe405 L */
+ {
+ .name = "Npe405L",
+ .pvr = CPU_PPC_NPE405L,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB03xx */
+ {
+ .name = "STB03",
+ .pvr = CPU_PPC_STB03,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB04xx */
+ {
+ .name = "STB04",
+ .pvr = CPU_PPC_STB04,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB25xx */
+ {
+ .name = "STB25",
+ .pvr = CPU_PPC_STB25,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 EP */
+ {
+ .name = "440ep",
+ .pvr = CPU_PPC_440EP,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_440,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 GP */
+ {
+ .name = "440gp",
+ .pvr = CPU_PPC_440GP,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_440,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 GX */
+ {
+ .name = "440gx",
+ .pvr = CPU_PPC_440GX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+
+ /* 32 bits "classic" powerpc */
+#if defined (TODO)
+ /* PPC 601 */
+ {
+ .name = "601",
+ .pvr = CPU_PPC_601,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_601,
+ .flags = PPC_FLAGS_601,
+ .msr_mask = 0x000000000000FD70,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 602 */
+ {
+ .name = "602",
+ .pvr = CPU_PPC_602,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_602,
+ .flags = PPC_FLAGS_602,
+ .msr_mask = 0x0000000000C7FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603 */
+ {
+ .name = "603",
+ .pvr = CPU_PPC_603,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603e */
+ {
+ .name = "603e",
+ .pvr = CPU_PPC_603E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+ {
+ .name = "Stretch",
+ .pvr = CPU_PPC_603E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603ev */
+ {
+ .name = "603ev",
+ .pvr = CPU_PPC_603EV,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603r */
+ {
+ .name = "603r",
+ .pvr = CPU_PPC_603R,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+ {
+ .name = "Goldeneye",
+ .pvr = CPU_PPC_603R,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* XXX: TODO: according to Motorola UM, this is a derivative to 603e */
+ {
+ .name = "G2",
+ .pvr = CPU_PPC_G2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_G2,
+ .flags = PPC_FLAGS_G2,
+ .msr_mask = 0x000000000006FFF2,
+ },
+ { /* Same as G2, with LE mode support */
+ .name = "G2le",
+ .pvr = CPU_PPC_G2LE,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_G2,
+ .flags = PPC_FLAGS_G2,
+ .msr_mask = 0x000000000007FFF3,
+ },
+#endif
+ /* PPC 604 */
+ {
+ .name = "604",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* PPC 604e */
+ {
+ .name = "604e",
+ .pvr = CPU_PPC_604E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* PPC 604r */
+ {
+ .name = "604r",
+ .pvr = CPU_PPC_604R,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* generic G3 */
+ {
+ .name = "G3",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC740 (G3) */
+ {
+ .name = "740",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Arthur",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC745 (G3) */
+ {
+ .name = "745",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Goldfinger",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* MPC750 (G3) */
+ {
+ .name = "750",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC755 (G3) */
+ {
+ .name = "755",
+ .pvr = CPU_PPC_755,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC740P (G3) */
+ {
+ .name = "740p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Conan/Doyle",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC745P (G3) */
+ {
+ .name = "745p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* MPC750P (G3) */
+ {
+ .name = "750p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC755P (G3) */
+ {
+ .name = "755p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* IBM 750CXe (G3 embedded) */
+ {
+ .name = "750cxe",
+ .pvr = CPU_PPC_750CXE,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ /* IBM 750FX (G3 embedded) */
+ {
+ .name = "750fx",
+ .pvr = CPU_PPC_750FX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ /* IBM 750GX (G3 embedded) */
+ {
+ .name = "750gx",
+ .pvr = CPU_PPC_750GX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* generic G4 */
+ {
+ .name = "G4",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7400 (G4) */
+ {
+ .name = "7400",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Max",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7410 (G4) */
+ {
+ .name = "7410",
+ .pvr = CPU_PPC_7410,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Nitro",
+ .pvr = CPU_PPC_7410,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* XXX: 7441 */
+ /* XXX: 7445 */
+ /* XXX: 7447 */
+ /* XXX: 7447A */
+#if defined (TODO)
+ /* PPC 7450 (G4) */
+ {
+ .name = "7450",
+ .pvr = CPU_PPC_7450,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Vger",
+ .pvr = CPU_PPC_7450,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* XXX: 7451 */
+#if defined (TODO)
+ /* PPC 7455 (G4) */
+ {
+ .name = "7455",
+ .pvr = CPU_PPC_7455,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 6",
+ .pvr = CPU_PPC_7455,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7457 (G4) */
+ {
+ .name = "7457",
+ .pvr = CPU_PPC_7457,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 7",
+ .pvr = CPU_PPC_7457,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7457A (G4) */
+ {
+ .name = "7457A",
+ .pvr = CPU_PPC_7457A,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 7 PM",
+ .pvr = CPU_PPC_7457A,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* 64 bits PPC */
+#if defined (TODO)
+ /* PPC 620 */
+ {
+ .name = "620",
+ .pvr = CPU_PPC_620,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_620,
+ .flags = PPC_FLAGS_620,
+ .msr_mask = 0x800000000005FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 630 (POWER3) */
+ {
+ .name = "630",
+ .pvr = CPU_PPC_630,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_630,
+ .flags = PPC_FLAGS_630,
+ .msr_mask = xxx,
+ }
+ {
+ .name = "POWER3",
+ .pvr = CPU_PPC_630,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_630,
+ .flags = PPC_FLAGS_630,
+ .msr_mask = xxx,
+ }
+#endif
+#if defined (TODO)
+ /* PPC 631 (Power 3+)*/
+ {
+ .name = "631",
+ .pvr = CPU_PPC_631,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_631,
+ .flags = PPC_FLAGS_631,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "POWER3+",
+ .pvr = CPU_PPC_631,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_631,
+ .flags = PPC_FLAGS_631,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER4 */
+ {
+ .name = "POWER4",
+ .pvr = CPU_PPC_POWER4,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER4,
+ .flags = PPC_FLAGS_POWER4,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER4p */
+ {
+ .name = "POWER4+",
+ .pvr = CPU_PPC_POWER4P,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER4,
+ .flags = PPC_FLAGS_POWER4,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER5 */
+ {
+ .name = "POWER5",
+ .pvr = CPU_PPC_POWER5,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER5,
+ .flags = PPC_FLAGS_POWER5,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER5+ */
+ {
+ .name = "POWER5+",
+ .pvr = CPU_PPC_POWER5P,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER5,
+ .flags = PPC_FLAGS_POWER5,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 970 */
+ {
+ .name = "970",
+ .pvr = CPU_PPC_970,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_970,
+ .flags = PPC_FLAGS_970,
+ .msr_mask = 0x900000000204FF36,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 970FX (G5) */
+ {
+ .name = "970fx",
+ .pvr = CPU_PPC_970FX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_970FX,
+ .flags = PPC_FLAGS_970FX,
+ .msr_mask = 0x800000000204FF36,
+ },
+#endif
+#if defined (TODO)
+ /* RS64 (Apache/A35) */
+ /* This one seems to support the whole POWER2 instruction set
+ * and the PowerPC 64 one.
+ */
+ {
+ .name = "RS64",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "Apache",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "A35",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-II (NorthStar/A50) */
+ {
+ .name = "RS64-II",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "NortStar",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "A50",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-III (Pulsar) */
+ {
+ .name = "RS64-III",
+ .pvr = CPU_PPC_RS64III,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "Pulsar",
+ .pvr = CPU_PPC_RS64III,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-IV (IceStar/IStar/SStar) */
+ {
+ .name = "RS64-IV",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "IceStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "IStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "SStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+ /* POWER */
+#if defined (TODO)
+ /* Original POWER */
+ {
+ .name = "POWER",
+ .pvr = CPU_POWER,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER,
+ .flags = PPC_FLAGS_POWER,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER2 */
+ {
+ .name = "POWER2",
+ .pvr = CPU_POWER2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER,
+ .flags = PPC_FLAGS_POWER,
+ .msr_mask = xxx,
+ },
+#endif
+ /* Generic PowerPCs */
+#if defined (TODO)
+ {
+ .name = "ppc64",
+ .pvr = CPU_PPC_970,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC64,
+ .flags = PPC_FLAGS_PPC64,
+ .msr_mask = 0xA00000000204FF36,
+ },
+#endif
+ {
+ .name = "ppc32",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC32,
+ .flags = PPC_FLAGS_PPC32,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* Fallback */
+ {
+ .name = "ppc",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC32,
+ .flags = PPC_FLAGS_PPC32,
+ .msr_mask = 0x000000000005FF77,
+ },
+};
+
+int ppc_find_by_name (const unsigned char *name, ppc_def_t **def)
+{
+ int i, ret;
+
+ ret = -1;
+ *def = NULL;
+ for (i = 0; strcmp(ppc_defs[i].name, "ppc") != 0; i++) {
+ if (strcasecmp(name, ppc_defs[i].name) == 0) {
+ *def = &ppc_defs[i];
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int ppc_find_by_pvr (uint32_t pvr, ppc_def_t **def)
+{
+ int i, ret;
+
+ ret = -1;
+ *def = NULL;
+ for (i = 0; ppc_defs[i].name != NULL; i++) {
+ if ((pvr & ppc_defs[i].pvr_mask) ==
+ (ppc_defs[i].pvr & ppc_defs[i].pvr_mask)) {
+ *def = &ppc_defs[i];
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+ int i;
+
+ for (i = 0; ; i++) {
+ (*cpu_fprintf)(f, "PowerPC '%s' PVR %08x mask %08x\n",
+ ppc_defs[i].name,
+ ppc_defs[i].pvr, ppc_defs[i].pvr_mask);
+ if (strcmp(ppc_defs[i].name, "ppc") == 0)
+ break;
+ }
+}
diff --git a/target-sh4/README.sh4 b/target-sh4/README.sh4
new file mode 100644
index 0000000..b887175
--- /dev/null
+++ b/target-sh4/README.sh4
@@ -0,0 +1,150 @@
+qemu target: sh4
+author: Samuel Tardieu <sam@rfc1149.net>
+last modified: Tue Dec 6 07:22:44 CET 2005
+
+The sh4 target is not ready at all yet for integration in qemu. This
+file describes the current state of implementation.
+
+Most places requiring attention and/or modification can be detected by
+looking for "XXXXX" or "assert (0)".
+
+The sh4 core is located in target-sh4/*, while the 7750 peripheral
+features (IO ports for example) are located in hw/sh7750.[ch]. The
+main board description is in hw/shix.c, and the NAND flash in
+hw/tc58128.[ch].
+
+All the shortcomings indicated here will eventually be resolved. This
+is a work in progress. Features are added in a semi-random order: if a
+point is blocking to progress on booting the Linux kernel for the shix
+board, it is addressed first; if feedback is necessary and no progress
+can be made on blocking points until it is received, a random feature
+is worked on.
+
+Goals
+-----
+
+The primary model being worked on is the soft MMU target to be able to
+emulate the Shix 2.0 board by Alexis Polti, described at
+http://perso.enst.fr/~polti/realisations/shix20/
+
+Ultimately, qemu will be coupled with a system C or a verilog
+simulator to simulate the whole board functionalities.
+
+A sh4 user-mode has also somewhat started but will be worked on
+afterwards. The goal is to automate tests for GNAT (GNU Ada) compiler
+that I ported recently to the sh4-linux target.
+
+Registers
+---------
+
+16 general purpose registers are available at any time. The first 8
+registers are banked and the non-directly visible ones can be accessed
+by privileged instructions. In qemu, we define 24 general purpose
+registers and the code generation use either [0-7]+[8-15] or
+[16-23]+[8-15] depending on the MD and RB flags in the sr
+configuration register.
+
+Instructions
+------------
+
+Most sh4 instructions have been implemented. The missing ones at this
+time are:
+ - FPU related instructions
+ - LDTLB to load a new MMU entry
+ - SLEEP to put the processor in sleep mode
+
+Most instructions could be optimized a lot. This will be worked on
+after the current model is fully functional unless debugging
+convenience requires that it is done early.
+
+Many instructions did not have a chance to be tested yet. The plan is
+to implement unit and regression testing of those in the future.
+
+MMU
+---
+
+The MMU is implemented in the sh4 core. MMU management has not been
+tested at all yet. In the sh7750, it can be manipulated through memory
+mapped registers and this part has not yet been implemented.
+
+Exceptions
+----------
+
+Exceptions are implemented as described in the sh4 reference manual
+but have not been tested yet. They do not use qemu EXCP_ features
+yet.
+
+IRQ
+---
+
+IRQ are not implemented yet.
+
+Peripheral features
+-------------------
+
+ + Serial ports
+
+Configuration and use of the first serial port (SCI) without
+interrupts is supported. Input has not yet been tested.
+
+Configuration of the second serial port (SCIF) is supported. FIFO
+handling infrastructure has been started but is not completed yet.
+
+ + GPIO ports
+
+GPIO ports have been implemented. A registration function allows
+external modules to register interest in some port changes (see
+hw/tc58128.[ch] for an example) and will be called back. Interrupt
+generation is not yet supported but some infrastructure is in place
+for this purpose. Note that in the current model a peripheral module
+cannot directly simulate a H->L->H input port transition and have an
+interrupt generated on the low level.
+
+ + TC58128 NAND flash
+
+TC58128 NAND flash is partially implemented through GPIO ports. It
+supports reading from flash.
+
+GDB
+---
+
+GDB remote target support has been implemented and lightly tested.
+
+Files
+-----
+
+File names are harcoded at this time. The bootloader must be stored in
+shix_bios.bin in the current directory. The initial Linux image must
+be stored in shix_linux_nand.bin in the current directory in NAND
+format. Test files can be obtained from
+http://perso.enst.fr/~polti/robot/ as well as the various datasheets I
+use.
+
+qemu disk parameter on the command line is unused. You can supply any
+existing image and it will be ignored. As the goal is to simulate an
+embedded target, it is not clear how this parameter will be handled in
+the future.
+
+To build an ELF kernel image from the NAND image, 16 bytes have to be
+stripped off the end of every 528 bytes, keeping only 512 of them. The
+following Python code snippet does it:
+
+#! /usr/bin/python
+
+def denand (infd, outfd):
+ while True:
+ d = infd.read (528)
+ if not d: return
+ outfd.write (d[:512])
+
+if __name__ == '__main__':
+ import sys
+ denand (open (sys.argv[1], 'rb'),
+ open (sys.argv[2], 'wb'))
+
+Style isssues
+-------------
+
+There is currently a mix between my style (space before opening
+parenthesis) and qemu style. This will be resolved before final
+integration is proposed.
diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h
new file mode 100644
index 0000000..ef818fd
--- /dev/null
+++ b/target-sh4/cpu.h
@@ -0,0 +1,146 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _CPU_SH4_H
+#define _CPU_SH4_H
+
+#include "config.h"
+
+#define TARGET_LONG_BITS 32
+#define TARGET_HAS_ICE 1
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_PAGE_BITS 12 /* 4k XXXXX */
+
+#define SR_MD (1 << 30)
+#define SR_RB (1 << 29)
+#define SR_BL (1 << 28)
+#define SR_FD (1 << 15)
+#define SR_M (1 << 9)
+#define SR_Q (1 << 8)
+#define SR_S (1 << 1)
+#define SR_T (1 << 0)
+
+#define FPSCR_FR (1 << 21)
+#define FPSCR_SZ (1 << 20)
+#define FPSCR_PR (1 << 19)
+#define FPSCR_DN (1 << 18)
+
+#define DELAY_SLOT (1 << 0) /* Must be the same as SR_T. */
+/* This flag is set if the next insn is a delay slot for a conditional jump.
+ The dynamic value of the DELAY_SLOT determines whether the jup is taken. */
+#define DELAY_SLOT_CONDITIONAL (1 << 1)
+/* Those are used in contexts only */
+#define BRANCH (1 << 2)
+#define BRANCH_CONDITIONAL (1 << 3)
+#define MODE_CHANGE (1 << 4) /* Potential MD|RB change */
+#define BRANCH_EXCEPTION (1 << 5) /* Branch after exception */
+
+/* XXXXX The structure could be made more compact */
+typedef struct tlb_t {
+ uint8_t asid; /* address space identifier */
+ uint32_t vpn; /* virtual page number */
+ uint8_t v; /* validity */
+ uint32_t ppn; /* physical page number */
+ uint8_t sz; /* page size */
+ uint32_t size; /* cached page size in bytes */
+ uint8_t sh; /* share status */
+ uint8_t c; /* cacheability */
+ uint8_t pr; /* protection key */
+ uint8_t d; /* dirty */
+ uint8_t wt; /* write through */
+ uint8_t sa; /* space attribute (PCMCIA) */
+ uint8_t tc; /* timing control */
+} tlb_t;
+
+#define UTLB_SIZE 64
+#define ITLB_SIZE 4
+
+typedef struct CPUSH4State {
+ uint32_t flags; /* general execution flags */
+ uint32_t gregs[24]; /* general registers */
+ uint32_t fregs[32]; /* floating point registers */
+ uint32_t sr; /* status register */
+ uint32_t ssr; /* saved status register */
+ uint32_t spc; /* saved program counter */
+ uint32_t gbr; /* global base register */
+ uint32_t vbr; /* vector base register */
+ uint32_t sgr; /* saved global register 15 */
+ uint32_t dbr; /* debug base register */
+ uint32_t pc; /* program counter */
+ uint32_t delayed_pc; /* target of delayed jump */
+ uint32_t mach; /* multiply and accumulate high */
+ uint32_t macl; /* multiply and accumulate low */
+ uint32_t pr; /* procedure register */
+ uint32_t fpscr; /* floating point status/control register */
+ uint32_t fpul; /* floating point communication register */
+
+ /* temporary float registers */
+ float32 ft0, ft1;
+ float64 dt0, dt1;
+
+ /* Those belong to the specific unit (SH7750) but are handled here */
+ uint32_t mmucr; /* MMU control register */
+ uint32_t pteh; /* page table entry high register */
+ uint32_t ptel; /* page table entry low register */
+ uint32_t ptea; /* page table entry assistance register */
+ uint32_t ttb; /* tranlation table base register */
+ uint32_t tea; /* TLB exception address register */
+ uint32_t tra; /* TRAPA exception register */
+ uint32_t expevt; /* exception event register */
+ uint32_t intevt; /* interrupt event register */
+
+ jmp_buf jmp_env;
+ int user_mode_only;
+ int interrupt_request;
+ int exception_index;
+ CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */
+ tlb_t itlb[ITLB_SIZE]; /* instruction translation table */
+} CPUSH4State;
+
+CPUSH4State *cpu_sh4_init(void);
+int cpu_sh4_exec(CPUSH4State * s);
+struct siginfo;
+int cpu_sh4_signal_handler(int hostsignum, struct siginfo *info,
+ void *puc);
+
+#include "softfloat.h"
+
+#include "cpu-all.h"
+
+/* Memory access type */
+enum {
+ /* Privilege */
+ ACCESS_PRIV = 0x01,
+ /* Direction */
+ ACCESS_WRITE = 0x02,
+ /* Type of instruction */
+ ACCESS_CODE = 0x10,
+ ACCESS_INT = 0x20
+};
+
+/* MMU control register */
+#define MMUCR 0x1F000010
+#define MMUCR_AT (1<<0)
+#define MMUCR_SV (1<<8)
+
+#endif /* _CPU_SH4_H */
diff --git a/target-sh4/exec.h b/target-sh4/exec.h
new file mode 100644
index 0000000..3563300
--- /dev/null
+++ b/target-sh4/exec.h
@@ -0,0 +1,80 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _EXEC_SH4_H
+#define _EXEC_SH4_H
+
+#include "config.h"
+#include "dyngen-exec.h"
+
+register struct CPUSH4State *env asm(AREG0);
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+//register uint32_t T2 asm(AREG3);
+
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define DT0 (env->dt0)
+#define DT1 (env->dt1)
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#ifndef CONFIG_USER_ONLY
+#include "softmmu_exec.h"
+#endif
+
+#define RETURN() __asm__ __volatile__("")
+
+static inline void regs_to_env(void)
+{
+ /* XXXXX */
+}
+
+static inline void env_to_regs(void)
+{
+ /* XXXXX */
+}
+
+int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int is_user, int is_softmmu);
+
+int find_itlb_entry(CPUState * env, target_ulong address,
+ int use_asid, int update);
+int find_utlb_entry(CPUState * env, target_ulong address, int use_asid);
+
+void helper_addc_T0_T1(void);
+void helper_addv_T0_T1(void);
+void helper_div1_T0_T1(void);
+void helper_dmulsl_T0_T1(void);
+void helper_dmulul_T0_T1(void);
+void helper_macl_T0_T1(void);
+void helper_macw_T0_T1(void);
+void helper_negc_T0(void);
+void helper_subc_T0_T1(void);
+void helper_subv_T0_T1(void);
+void helper_rotcl(uint32_t * addr);
+void helper_rotcr(uint32_t * addr);
+
+void do_interrupt(CPUState * env);
+
+void cpu_loop_exit(void);
+void do_raise_exception(void);
+
+#endif /* _EXEC_SH4_H */
diff --git a/target-sh4/helper.c b/target-sh4/helper.c
new file mode 100644
index 0000000..1839c96
--- /dev/null
+++ b/target-sh4/helper.c
@@ -0,0 +1,441 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ env->tea = address;
+ switch (rw) {
+ case 0:
+ env->exception_index = 0x0a0;
+ break;
+ case 1:
+ env->exception_index = 0x0c0;
+ break;
+ case 2:
+ env->exception_index = 0x0a0;
+ break;
+ }
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
+{
+ return addr;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+#define MMU_OK 0
+#define MMU_ITLB_MISS (-1)
+#define MMU_ITLB_MULTIPLE (-2)
+#define MMU_ITLB_VIOLATION (-3)
+#define MMU_DTLB_MISS_READ (-4)
+#define MMU_DTLB_MISS_WRITE (-5)
+#define MMU_DTLB_INITIAL_WRITE (-6)
+#define MMU_DTLB_VIOLATION_READ (-7)
+#define MMU_DTLB_VIOLATION_WRITE (-8)
+#define MMU_DTLB_MULTIPLE (-9)
+#define MMU_DTLB_MISS (-10)
+
+void do_interrupt(CPUState * env)
+{
+ if (loglevel & CPU_LOG_INT) {
+ const char *expname;
+ switch (env->exception_index) {
+ case 0x0e0:
+ expname = "addr_error";
+ break;
+ case 0x040:
+ expname = "tlb_miss";
+ break;
+ case 0x0a0:
+ expname = "tlb_violation";
+ break;
+ case 0x180:
+ expname = "illegal_instruction";
+ break;
+ case 0x1a0:
+ expname = "slot_illegal_instruction";
+ break;
+ case 0x800:
+ expname = "fpu_disable";
+ break;
+ case 0x820:
+ expname = "slot_fpu";
+ break;
+ case 0x100:
+ expname = "data_write";
+ break;
+ case 0x060:
+ expname = "dtlb_miss_write";
+ break;
+ case 0x0c0:
+ expname = "dtlb_violation_write";
+ break;
+ case 0x120:
+ expname = "fpu_exception";
+ break;
+ case 0x080:
+ expname = "initial_page_write";
+ break;
+ case 0x160:
+ expname = "trapa";
+ break;
+ default:
+ expname = "???";
+ break;
+ }
+ fprintf(logfile, "exception 0x%03x [%s] raised\n",
+ env->exception_index, expname);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+
+ env->ssr = env->sr;
+ env->spc = env->spc;
+ env->sgr = env->gregs[15];
+ env->sr |= SR_BL | SR_MD | SR_RB;
+
+ env->expevt = env->exception_index & 0x7ff;
+ switch (env->exception_index) {
+ case 0x040:
+ case 0x060:
+ case 0x080:
+ env->pc = env->vbr + 0x400;
+ break;
+ case 0x140:
+ env->pc = 0xa0000000;
+ break;
+ default:
+ env->pc = env->vbr + 0x100;
+ break;
+ }
+}
+
+static void update_itlb_use(CPUState * env, int itlbnb)
+{
+ uint8_t or_mask = 0, and_mask = (uint8_t) - 1;
+
+ switch (itlbnb) {
+ case 0:
+ and_mask = 0x7f;
+ break;
+ case 1:
+ and_mask = 0xe7;
+ or_mask = 0x80;
+ break;
+ case 2:
+ and_mask = 0xfb;
+ or_mask = 0x50;
+ break;
+ case 3:
+ or_mask = 0x2c;
+ break;
+ }
+
+ env->mmucr &= (and_mask << 24);
+ env->mmucr |= (or_mask << 24);
+}
+
+static int itlb_replacement(CPUState * env)
+{
+ if ((env->mmucr & 0xe0000000) == 0xe0000000)
+ return 0;
+ if ((env->mmucr & 0x98000000) == 0x08000000)
+ return 1;
+ if ((env->mmucr & 0x54000000) == 0x04000000)
+ return 2;
+ if ((env->mmucr & 0x2c000000) == 0x00000000)
+ return 3;
+ assert(0);
+}
+
+/* Find the corresponding entry in the right TLB
+ Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE
+*/
+static int find_tlb_entry(CPUState * env, target_ulong address,
+ tlb_t * entries, uint8_t nbtlb, int use_asid)
+{
+ int match = MMU_DTLB_MISS;
+ uint32_t start, end;
+ uint8_t asid;
+ int i;
+
+ asid = env->pteh & 0xff;
+
+ for (i = 0; i < nbtlb; i++) {
+ if (!entries[i].v)
+ continue; /* Invalid entry */
+ if (use_asid && entries[i].asid != asid && !entries[i].sh)
+ continue; /* Bad ASID */
+#if 0
+ switch (entries[i].sz) {
+ case 0:
+ size = 1024; /* 1kB */
+ break;
+ case 1:
+ size = 4 * 1024; /* 4kB */
+ break;
+ case 2:
+ size = 64 * 1024; /* 64kB */
+ break;
+ case 3:
+ size = 1024 * 1024; /* 1MB */
+ break;
+ default:
+ assert(0);
+ }
+#endif
+ start = (entries[i].vpn << 10) & ~(entries[i].size - 1);
+ end = start + entries[i].size - 1;
+ if (address >= start && address <= end) { /* Match */
+ if (match != -1)
+ return MMU_DTLB_MULTIPLE; /* Multiple match */
+ match = i;
+ }
+ }
+ return match;
+}
+
+/* Find itlb entry - update itlb from utlb if necessary and asked for
+ Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE
+ Update the itlb from utlb if update is not 0
+*/
+int find_itlb_entry(CPUState * env, target_ulong address,
+ int use_asid, int update)
+{
+ int e, n;
+
+ e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid);
+ if (e == MMU_DTLB_MULTIPLE)
+ e = MMU_ITLB_MULTIPLE;
+ else if (e == MMU_DTLB_MISS && update) {
+ e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
+ if (e >= 0) {
+ n = itlb_replacement(env);
+ env->itlb[n] = env->utlb[e];
+ e = n;
+ }
+ }
+ if (e >= 0)
+ update_itlb_use(env, e);
+ return e;
+}
+
+/* Find utlb entry
+ Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */
+int find_utlb_entry(CPUState * env, target_ulong address, int use_asid)
+{
+ uint8_t urb, urc;
+
+ /* Increment URC */
+ urb = ((env->mmucr) >> 18) & 0x3f;
+ urc = ((env->mmucr) >> 10) & 0x3f;
+ urc++;
+ if (urc == urb || urc == UTLB_SIZE - 1)
+ urc = 0;
+ env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
+
+ /* Return entry */
+ return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
+}
+
+/* Match address against MMU
+ Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE,
+ MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ,
+ MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS,
+ MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION
+*/
+static int get_mmu_address(CPUState * env, target_ulong * physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ int use_asid, is_code, n;
+ tlb_t *matching = NULL;
+
+ use_asid = (env->mmucr & MMUCR_SV) == 0 && (env->sr & SR_MD) == 0;
+ is_code = env->pc == address; /* Hack */
+
+ /* Use a hack to find if this is an instruction or data access */
+ if (env->pc == address && !(rw & PAGE_WRITE)) {
+ n = find_itlb_entry(env, address, use_asid, 1);
+ if (n >= 0) {
+ matching = &env->itlb[n];
+ if ((env->sr & SR_MD) & !(matching->pr & 2))
+ n = MMU_ITLB_VIOLATION;
+ else
+ *prot = PAGE_READ;
+ }
+ } else {
+ n = find_utlb_entry(env, address, use_asid);
+ if (n >= 0) {
+ matching = &env->utlb[n];
+ switch ((matching->pr << 1) | ((env->sr & SR_MD) ? 1 : 0)) {
+ case 0: /* 000 */
+ case 2: /* 010 */
+ n = (rw & PAGE_WRITE) ? MMU_DTLB_VIOLATION_WRITE :
+ MMU_DTLB_VIOLATION_READ;
+ break;
+ case 1: /* 001 */
+ case 4: /* 100 */
+ case 5: /* 101 */
+ if (rw & PAGE_WRITE)
+ n = MMU_DTLB_VIOLATION_WRITE;
+ else
+ *prot = PAGE_READ;
+ break;
+ case 3: /* 011 */
+ case 6: /* 110 */
+ case 7: /* 111 */
+ *prot = rw & (PAGE_READ | PAGE_WRITE);
+ break;
+ }
+ } else if (n == MMU_DTLB_MISS) {
+ n = (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE :
+ MMU_DTLB_MISS_READ;
+ }
+ }
+ if (n >= 0) {
+ *physical = ((matching->ppn << 10) & ~(matching->size - 1)) |
+ (address & (matching->size - 1));
+ if ((rw & PAGE_WRITE) & !matching->d)
+ n = MMU_DTLB_INITIAL_WRITE;
+ else
+ n = MMU_OK;
+ }
+ return n;
+}
+
+int get_physical_address(CPUState * env, target_ulong * physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ /* P1, P2 and P4 areas do not use translation */
+ if ((address >= 0x80000000 && address < 0xc0000000) ||
+ address >= 0xe0000000) {
+ if (!(env->sr & SR_MD)
+ && (address < 0xe0000000 || address > 0xe4000000)) {
+ /* Unauthorized access in user mode (only store queues are available) */
+ fprintf(stderr, "Unauthorized access\n");
+ return (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE :
+ MMU_DTLB_MISS_READ;
+ }
+ /* Mask upper 3 bits */
+ *physical = address & 0x1FFFFFFF;
+ *prot = PAGE_READ | PAGE_WRITE;
+ return MMU_OK;
+ }
+
+ /* If MMU is disabled, return the corresponding physical page */
+ if (!env->mmucr & MMUCR_AT) {
+ *physical = address & 0x1FFFFFFF;
+ *prot = PAGE_READ | PAGE_WRITE;
+ return MMU_OK;
+ }
+
+ /* We need to resort to the MMU */
+ return get_mmu_address(env, physical, prot, address, rw, access_type);
+}
+
+int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ target_ulong physical, page_offset, page_size;
+ int prot, ret, access_type;
+
+ /* XXXXX */
+#if 0
+ fprintf(stderr, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
+ __func__, env->pc, address, rw, is_user, is_softmmu);
+#endif
+
+ access_type = ACCESS_INT;
+ ret =
+ get_physical_address(env, &physical, &prot, address, rw,
+ access_type);
+
+ if (ret != MMU_OK) {
+ env->tea = address;
+ switch (ret) {
+ case MMU_ITLB_MISS:
+ case MMU_DTLB_MISS_READ:
+ env->exception_index = 0x040;
+ break;
+ case MMU_DTLB_MULTIPLE:
+ case MMU_ITLB_MULTIPLE:
+ env->exception_index = 0x140;
+ break;
+ case MMU_ITLB_VIOLATION:
+ env->exception_index = 0x0a0;
+ break;
+ case MMU_DTLB_MISS_WRITE:
+ env->exception_index = 0x060;
+ break;
+ case MMU_DTLB_INITIAL_WRITE:
+ env->exception_index = 0x080;
+ break;
+ case MMU_DTLB_VIOLATION_READ:
+ env->exception_index = 0x0a0;
+ break;
+ case MMU_DTLB_VIOLATION_WRITE:
+ env->exception_index = 0x0c0;
+ break;
+ default:
+ assert(0);
+ }
+ return 1;
+ }
+
+ page_size = TARGET_PAGE_SIZE;
+ page_offset =
+ (address - (address & TARGET_PAGE_MASK)) & ~(page_size - 1);
+ address = (address & TARGET_PAGE_MASK) + page_offset;
+ physical = (physical & TARGET_PAGE_MASK) + page_offset;
+
+ return tlb_set_page(env, address, physical, prot, is_user, is_softmmu);
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
+{
+ target_ulong physical;
+ int prot;
+
+ get_physical_address(env, &physical, &prot, addr, PAGE_READ, 0);
+ return physical;
+}
+
+#endif
diff --git a/target-sh4/op.c b/target-sh4/op.c
new file mode 100644
index 0000000..d3b68bc
--- /dev/null
+++ b/target-sh4/op.c
@@ -0,0 +1,967 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+static inline void set_flag(uint32_t flag)
+{
+ env->flags |= flag;
+}
+
+static inline void clr_flag(uint32_t flag)
+{
+ env->flags &= ~flag;
+}
+
+static inline void set_t(void)
+{
+ env->sr |= SR_T;
+}
+
+static inline void clr_t(void)
+{
+ env->sr &= ~SR_T;
+}
+
+static inline void cond_t(int cond)
+{
+ if (cond)
+ set_t();
+ else
+ clr_t();
+}
+
+void OPPROTO op_movl_imm_T0(void)
+{
+ T0 = (uint32_t) PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_movl_imm_T1(void)
+{
+ T0 = (uint32_t) PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_movl_imm_T2(void)
+{
+ T0 = (uint32_t) PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_cmp_eq_imm_T0(void)
+{
+ cond_t((int32_t) T0 == (int32_t) PARAM1);
+ RETURN();
+}
+
+void OPPROTO op_cmd_eq_T0_T1(void)
+{
+ cond_t(T0 == T1);
+ RETURN();
+}
+
+void OPPROTO op_cmd_hs_T0_T1(void)
+{
+ cond_t((uint32_t) T0 <= (uint32_t) T1);
+ RETURN();
+}
+
+void OPPROTO op_cmd_ge_T0_T1(void)
+{
+ cond_t((int32_t) T0 <= (int32_t) T1);
+ RETURN();
+}
+
+void OPPROTO op_cmd_hi_T0_T1(void)
+{
+ cond_t((uint32_t) T0 < (uint32_t) T1);
+ RETURN();
+}
+
+void OPPROTO op_cmd_gt_T0_T1(void)
+{
+ cond_t((int32_t) T0 < (int32_t) T1);
+ RETURN();
+}
+
+void OPPROTO op_not_T0(void)
+{
+ T0 = ~T0;
+ RETURN();
+}
+
+void OPPROTO op_bf_s(void)
+{
+ env->delayed_pc = PARAM1;
+ set_flag(DELAY_SLOT_CONDITIONAL | ((~env->sr) & SR_T));
+ RETURN();
+}
+
+void OPPROTO op_bt_s(void)
+{
+ env->delayed_pc = PARAM1;
+ set_flag(DELAY_SLOT_CONDITIONAL | (env->sr & SR_T));
+ RETURN();
+}
+
+void OPPROTO op_bra(void)
+{
+ env->delayed_pc = PARAM1;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_braf_T0(void)
+{
+ env->delayed_pc = PARAM1 + T0;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_bsr(void)
+{
+ env->pr = PARAM1;
+ env->delayed_pc = PARAM2;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_bsrf_T0(void)
+{
+ env->pr = PARAM1;
+ env->delayed_pc = PARAM1 + T0;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_jsr_T0(void)
+{
+ env->pr = PARAM1;
+ env->delayed_pc = T0;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_rts(void)
+{
+ env->delayed_pc = env->pr;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_clr_delay_slot(void)
+{
+ clr_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_clr_delay_slot_conditional(void)
+{
+ clr_flag(DELAY_SLOT_CONDITIONAL);
+ RETURN();
+}
+
+void OPPROTO op_exit_tb(void)
+{
+ EXIT_TB();
+ RETURN();
+}
+
+void OPPROTO op_addl_imm_T0(void)
+{
+ T0 += PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_addl_imm_T1(void)
+{
+ T1 += PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_clrmac(void)
+{
+ env->mach = env->macl = 0;
+ RETURN();
+}
+
+void OPPROTO op_clrs(void)
+{
+ env->sr &= ~SR_S;
+ RETURN();
+}
+
+void OPPROTO op_clrt(void)
+{
+ env->sr &= ~SR_T;
+ RETURN();
+}
+
+void OPPROTO op_sets(void)
+{
+ env->sr |= SR_S;
+ RETURN();
+}
+
+void OPPROTO op_sett(void)
+{
+ env->sr |= SR_T;
+ RETURN();
+}
+
+void OPPROTO op_frchg(void)
+{
+ env->fpscr ^= FPSCR_FR;
+ RETURN();
+}
+
+void OPPROTO op_fschg(void)
+{
+ env->fpscr ^= FPSCR_SZ;
+ RETURN();
+}
+
+void OPPROTO op_rte(void)
+{
+ env->sr = env->ssr;
+ env->delayed_pc = env->spc;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_swapb_T0(void)
+{
+ T0 = (T0 & 0xffff0000) | ((T0 & 0xff) << 8) | ((T0 >> 8) & 0xff);
+ RETURN();
+}
+
+void OPPROTO op_swapw_T0(void)
+{
+ T0 = ((T0 & 0xffff) << 16) | ((T0 >> 16) & 0xffff);
+ RETURN();
+}
+
+void OPPROTO op_xtrct_T0_T1(void)
+{
+ T1 = ((T0 & 0xffff) << 16) | ((T1 >> 16) & 0xffff);
+ RETURN();
+}
+
+void OPPROTO op_addc_T0_T1(void)
+{
+ helper_addc_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_addv_T0_T1(void)
+{
+ helper_addv_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_cmp_eq_T0_T1(void)
+{
+ cond_t(T1 == T0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_ge_T0_T1(void)
+{
+ cond_t((int32_t) T1 >= (int32_t) T0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_gt_T0_T1(void)
+{
+ cond_t((int32_t) T1 > (int32_t) T0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_hi_T0_T1(void)
+{
+ cond_t((uint32_t) T1 > (uint32_t) T0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_hs_T0_T1(void)
+{
+ cond_t((uint32_t) T1 >= (uint32_t) T0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_str_T0_T1(void)
+{
+ cond_t((T0 & 0x000000ff) == (T1 & 0x000000ff) ||
+ (T0 & 0x0000ff00) == (T1 & 0x0000ff00) ||
+ (T0 & 0x00ff0000) == (T1 & 0x00ff0000) ||
+ (T0 & 0xff000000) == (T1 & 0xff000000));
+ RETURN();
+}
+
+void OPPROTO op_tst_T0_T1(void)
+{
+ cond_t((T1 & T0) == 0);
+ RETURN();
+}
+
+void OPPROTO op_div0s_T0_T1(void)
+{
+ if (T1 & 0x80000000)
+ env->sr |= SR_Q;
+ else
+ env->sr &= ~SR_Q;
+ if (T0 & 0x80000000)
+ env->sr |= SR_M;
+ else
+ env->sr &= ~SR_M;
+ cond_t((T1 ^ T0) & 0x80000000);
+ RETURN();
+}
+
+void OPPROTO op_div0u(void)
+{
+ env->sr &= ~(SR_M | SR_Q | SR_T);
+ RETURN();
+}
+
+void OPPROTO op_div1_T0_T1(void)
+{
+ helper_div1_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_dmulsl_T0_T1(void)
+{
+ helper_dmulsl_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_dmulul_T0_T1(void)
+{
+ helper_dmulul_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_macl_T0_T1(void)
+{
+ helper_macl_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_macw_T0_T1(void)
+{
+ helper_macw_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_mull_T0_T1(void)
+{
+ env->macl = (T0 * T1) & 0xffffffff;
+ RETURN();
+}
+
+void OPPROTO op_mulsw_T0_T1(void)
+{
+ env->macl = (int32_t) T0 *(int32_t) T1;
+ RETURN();
+}
+
+void OPPROTO op_muluw_T0_T1(void)
+{
+ env->macl = (uint32_t) T0 *(uint32_t) T1;
+ RETURN();
+}
+
+void OPPROTO op_neg_T0(void)
+{
+ T0 = -T0;
+ RETURN();
+}
+
+void OPPROTO op_negc_T0(void)
+{
+ helper_negc_T0();
+ RETURN();
+}
+
+void OPPROTO op_shad_T0_T1(void)
+{
+ if ((T0 & 0x80000000) == 0)
+ T1 <<= (T0 & 0x1f);
+ else if ((T0 & 0x1f) == 0)
+ T1 = 0;
+ else
+ T1 = ((int32_t) T1) >> ((~T0 & 0x1f) + 1);
+ RETURN();
+}
+
+void OPPROTO op_shld_T0_T1(void)
+{
+ if ((T0 & 0x80000000) == 0)
+ T1 <<= (T0 & 0x1f);
+ else if ((T0 & 0x1f) == 0)
+ T1 = 0;
+ else
+ T1 = ((uint32_t) T1) >> ((~T0 & 0x1f) + 1);
+ RETURN();
+}
+
+void OPPROTO op_subc_T0_T1(void)
+{
+ helper_subc_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_subv_T0_T1(void)
+{
+ helper_subv_T0_T1();
+ RETURN();
+}
+
+void OPPROTO op_trapa(void)
+{
+ env->tra = PARAM1 * 2;
+ env->exception_index = 0x160;
+ do_raise_exception();
+ RETURN();
+}
+
+void OPPROTO op_cmp_pl_T0(void)
+{
+ cond_t((int32_t) T0 > 0);
+ RETURN();
+}
+
+void OPPROTO op_cmp_pz_T0(void)
+{
+ cond_t((int32_t) T0 >= 0);
+ RETURN();
+}
+
+void OPPROTO op_jmp_T0(void)
+{
+ env->delayed_pc = T0;
+ set_flag(DELAY_SLOT);
+ RETURN();
+}
+
+void OPPROTO op_movl_rN_rN(void)
+{
+ env->gregs[PARAM2] = env->gregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_ldcl_rMplus_rN_bank(void)
+{
+ env->gregs[PARAM2] = env->gregs[PARAM1];
+ env->gregs[PARAM1] += 4;
+ RETURN();
+}
+
+void OPPROTO op_ldc_T0_sr(void)
+{
+ env->sr = T0 & 0x700083f3;
+ RETURN();
+}
+
+void OPPROTO op_stc_sr_T0(void)
+{
+ T0 = env->sr;
+ RETURN();
+}
+
+#define LDSTOPS(target,load,store) \
+void OPPROTO op_##load##_T0_##target (void) \
+{ env ->target = T0; RETURN(); \
+} \
+void OPPROTO op_##store##_##target##_T0 (void) \
+{ T0 = env->target; RETURN(); \
+} \
+
+ LDSTOPS(gbr, ldc, stc)
+ LDSTOPS(vbr, ldc, stc)
+ LDSTOPS(ssr, ldc, stc)
+ LDSTOPS(spc, ldc, stc)
+ LDSTOPS(sgr, ldc, stc)
+ LDSTOPS(dbr, ldc, stc)
+ LDSTOPS(mach, lds, sts)
+ LDSTOPS(macl, lds, sts)
+ LDSTOPS(pr, lds, sts)
+ LDSTOPS(fpul, lds, sts)
+
+void OPPROTO op_lds_T0_fpscr(void)
+{
+ env->fpscr = T0 & 0x003fffff;
+ RETURN();
+}
+
+void OPPROTO op_sts_fpscr_T0(void)
+{
+ T0 = env->fpscr & 0x003fffff;
+ RETURN();
+}
+
+void OPPROTO op_movt_rN(void)
+{
+ env->gregs[PARAM1] = env->sr & SR_T;
+ RETURN();
+}
+
+void OPPROTO op_rotcl_Rn(void)
+{
+ helper_rotcl(&env->gregs[PARAM1]);
+ RETURN();
+}
+
+void OPPROTO op_rotcr_Rn(void)
+{
+ helper_rotcr(&env->gregs[PARAM1]);
+ RETURN();
+}
+
+void OPPROTO op_rotl_Rn(void)
+{
+ cond_t(env->gregs[PARAM1] & 0x80000000);
+ env->gregs[PARAM1] = (env->gregs[PARAM1] << 1) | (env->sr & SR_T);
+ RETURN();
+}
+
+void OPPROTO op_rotr_Rn(void)
+{
+ cond_t(env->gregs[PARAM1] & 1);
+ env->gregs[PARAM1] = (env->gregs[PARAM1] >> 1) |
+ ((env->sr & SR_T) ? 0x80000000 : 0);
+ RETURN();
+}
+
+void OPPROTO op_shal_Rn(void)
+{
+ cond_t(env->gregs[PARAM1] & 0x80000000);
+ env->gregs[PARAM1] <<= 1;
+ RETURN();
+}
+
+void OPPROTO op_shar_Rn(void)
+{
+ cond_t(env->gregs[PARAM1] & 1);
+ *(int32_t *) & env->gregs[PARAM1] >>= 1;
+ RETURN();
+}
+
+void OPPROTO op_shlr_Rn(void)
+{
+ cond_t(env->gregs[PARAM1] & 1);
+ *(uint32_t *) & env->gregs[PARAM1] >>= 1;
+ RETURN();
+}
+
+void OPPROTO op_shll2_Rn(void)
+{
+ env->gregs[PARAM1] <<= 2;
+ RETURN();
+}
+
+void OPPROTO op_shll8_Rn(void)
+{
+ env->gregs[PARAM1] <<= 8;
+ RETURN();
+}
+
+void OPPROTO op_shll16_Rn(void)
+{
+ env->gregs[PARAM1] <<= 16;
+ RETURN();
+}
+
+void OPPROTO op_shlr2_Rn(void)
+{
+ *(uint32_t *) & env->gregs[PARAM1] >>= 2;
+ RETURN();
+}
+
+void OPPROTO op_shlr8_Rn(void)
+{
+ *(uint32_t *) & env->gregs[PARAM1] >>= 8;
+ RETURN();
+}
+
+void OPPROTO op_shlr16_Rn(void)
+{
+ *(uint32_t *) & env->gregs[PARAM1] >>= 16;
+ RETURN();
+}
+
+void OPPROTO op_tasb_rN(void)
+{
+ cond_t(*(int8_t *) env->gregs[PARAM1] == 0);
+ *(int8_t *) env->gregs[PARAM1] |= 0x80;
+ RETURN();
+}
+
+void OPPROTO op_movl_T0_rN(void)
+{
+ env->gregs[PARAM1] = T0;
+ RETURN();
+}
+
+void OPPROTO op_movl_T1_rN(void)
+{
+ env->gregs[PARAM1] = T1;
+ RETURN();
+}
+
+void OPPROTO op_movb_rN_T0(void)
+{
+ T0 = (int32_t) (int8_t) (env->gregs[PARAM1] & 0xff);
+ RETURN();
+}
+
+void OPPROTO op_movub_rN_T0(void)
+{
+ T0 = env->gregs[PARAM1] & 0xff;
+ RETURN();
+}
+
+void OPPROTO op_movw_rN_T0(void)
+{
+ T0 = (int32_t) (int16_t) (env->gregs[PARAM1] & 0xffff);
+ RETURN();
+}
+
+void OPPROTO op_movuw_rN_T0(void)
+{
+ T0 = env->gregs[PARAM1] & 0xffff;
+ RETURN();
+}
+
+void OPPROTO op_movl_rN_T0(void)
+{
+ T0 = env->gregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_movb_rN_T1(void)
+{
+ T1 = (int32_t) (int8_t) (env->gregs[PARAM1] & 0xff);
+ RETURN();
+}
+
+void OPPROTO op_movub_rN_T1(void)
+{
+ T1 = env->gregs[PARAM1] & 0xff;
+ RETURN();
+}
+
+void OPPROTO op_movw_rN_T1(void)
+{
+ T1 = (int32_t) (int16_t) (env->gregs[PARAM1] & 0xffff);
+ RETURN();
+}
+
+void OPPROTO op_movuw_rN_T1(void)
+{
+ T1 = env->gregs[PARAM1] & 0xffff;
+ RETURN();
+}
+
+void OPPROTO op_movl_rN_T1(void)
+{
+ T1 = env->gregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_movl_imm_rN(void)
+{
+ env->gregs[PARAM2] = PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_fmov_frN_FT0(void)
+{
+ FT0 = *(float32 *)&env->fregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_fmov_drN_DT0(void)
+{
+ DT0 = *(float64 *)&env->fregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_fmov_FT0_frN(void)
+{
+ *(float32 *)&env->fregs[PARAM1] = FT0;
+ RETURN();
+}
+
+void OPPROTO op_fmov_DT0_drN(void)
+{
+ *(float64 *)&env->fregs[PARAM1] = DT0;
+ RETURN();
+}
+
+void OPPROTO op_dec1_rN(void)
+{
+ env->gregs[PARAM1] -= 1;
+ RETURN();
+}
+
+void OPPROTO op_dec2_rN(void)
+{
+ env->gregs[PARAM1] -= 2;
+ RETURN();
+}
+
+void OPPROTO op_dec4_rN(void)
+{
+ env->gregs[PARAM1] -= 4;
+ RETURN();
+}
+
+void OPPROTO op_dec8_rN(void)
+{
+ env->gregs[PARAM1] -= 4;
+ RETURN();
+}
+
+void OPPROTO op_inc1_rN(void)
+{
+ env->gregs[PARAM1] += 1;
+ RETURN();
+}
+
+void OPPROTO op_inc2_rN(void)
+{
+ env->gregs[PARAM1] += 2;
+ RETURN();
+}
+
+void OPPROTO op_inc4_rN(void)
+{
+ env->gregs[PARAM1] += 4;
+ RETURN();
+}
+
+void OPPROTO op_inc8_rN(void)
+{
+ env->gregs[PARAM1] += 4;
+ RETURN();
+}
+
+void OPPROTO op_add_T0_rN(void)
+{
+ env->gregs[PARAM1] += T0;
+ RETURN();
+}
+
+void OPPROTO op_sub_T0_rN(void)
+{
+ env->gregs[PARAM1] -= T0;
+ RETURN();
+}
+
+void OPPROTO op_and_T0_rN(void)
+{
+ env->gregs[PARAM1] &= T0;
+ RETURN();
+}
+
+void OPPROTO op_or_T0_rN(void)
+{
+ env->gregs[PARAM1] |= T0;
+ RETURN();
+}
+
+void OPPROTO op_xor_T0_rN(void)
+{
+ env->gregs[PARAM1] ^= T0;
+ RETURN();
+}
+
+void OPPROTO op_add_rN_T0(void)
+{
+ T0 += env->gregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_add_rN_T1(void)
+{
+ T1 += env->gregs[PARAM1];
+ RETURN();
+}
+
+void OPPROTO op_add_imm_rN(void)
+{
+ env->gregs[PARAM2] += PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_and_imm_rN(void)
+{
+ env->gregs[PARAM2] &= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_or_imm_rN(void)
+{
+ env->gregs[PARAM2] |= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_xor_imm_rN(void)
+{
+ env->gregs[PARAM2] ^= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_dt_rN(void)
+{
+ cond_t((--env->gregs[PARAM1]) == 0);
+ RETURN();
+}
+
+void OPPROTO op_tst_imm_rN(void)
+{
+ cond_t((env->gregs[PARAM2] & PARAM1) == 0);
+ RETURN();
+}
+
+void OPPROTO op_movl_T0_T1(void)
+{
+ T1 = T0;
+ RETURN();
+}
+
+void OPPROTO op_movl_fpul_FT0(void)
+{
+ FT0 = *(float32 *)&env->fpul;
+ RETURN();
+}
+
+void OPPROTO op_movl_FT0_fpul(void)
+{
+ *(float32 *)&env->fpul = FT0;
+ RETURN();
+}
+
+void OPPROTO op_goto_tb0(void)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+ RETURN();
+}
+
+void OPPROTO op_goto_tb1(void)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+ RETURN();
+}
+
+void OPPROTO op_movl_imm_PC(void)
+{
+ env->pc = PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_jT(void)
+{
+ if (env->sr & SR_T)
+ GOTO_LABEL_PARAM(1);
+ RETURN();
+}
+
+void OPPROTO op_jdelayed(void)
+{
+ uint32_t flags;
+ flags = env->flags;
+ env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+ if (flags & DELAY_SLOT)
+ GOTO_LABEL_PARAM(1);
+ RETURN();
+}
+
+void OPPROTO op_movl_delayed_pc_PC(void)
+{
+ env->pc = env->delayed_pc;
+ RETURN();
+}
+
+void OPPROTO op_addl_GBR_T0(void)
+{
+ T0 += env->gbr;
+ RETURN();
+}
+
+void OPPROTO op_and_imm_T0(void)
+{
+ T0 &= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_or_imm_T0(void)
+{
+ T0 |= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_xor_imm_T0(void)
+{
+ T0 ^= PARAM1;
+ RETURN();
+}
+
+void OPPROTO op_tst_imm_T0(void)
+{
+ cond_t((T0 & PARAM1) == 0);
+ RETURN();
+}
+
+void OPPROTO op_raise_illegal_instruction(void)
+{
+ env->exception_index = 0x180;
+ do_raise_exception();
+ RETURN();
+}
+
+void OPPROTO op_raise_slot_illegal_instruction(void)
+{
+ env->exception_index = 0x1a0;
+ do_raise_exception();
+ RETURN();
+}
+
+void OPPROTO op_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+/* Load and store */
+#define MEMSUFFIX _raw
+#include "op_mem.c"
+#undef MEMSUFFIX
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.c"
+#undef MEMSUFFIX
+
+#define MEMSUFFIX _kernel
+#include "op_mem.c"
+#undef MEMSUFFIX
+#endif
diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c
new file mode 100644
index 0000000..f02fa58
--- /dev/null
+++ b/target-sh4/op_helper.c
@@ -0,0 +1,372 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <assert.h>
+#include "exec.h"
+
+void cpu_loop_exit(void)
+{
+ longjmp(env->jmp_env, 1);
+}
+
+void do_raise_exception(void)
+{
+ cpu_loop_exit();
+}
+
+#ifndef CONFIG_USER_ONLY
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_sh4_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long) retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ do_raise_exception();
+ }
+ env = saved_env;
+}
+
+#endif
+
+void helper_addc_T0_T1(void)
+{
+ uint32_t tmp0, tmp1;
+
+ tmp1 = T0 + T1;
+ tmp0 = T1;
+ T1 = tmp1 + (env->sr & 1);
+ if (tmp0 > tmp1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ if (tmp1 > T1)
+ env->sr |= SR_T;
+}
+
+void helper_addv_T0_T1(void)
+{
+ uint32_t dest, src, ans;
+
+ if ((int32_t) T1 >= 0)
+ dest = 0;
+ else
+ dest = 1;
+ if ((int32_t) T0 >= 0)
+ src = 0;
+ else
+ src = 1;
+ src += dest;
+ T1 += T0;
+ if ((int32_t) T1 >= 0)
+ ans = 0;
+ else
+ ans = 1;
+ ans += dest;
+ if (src == 0 || src == 2) {
+ if (ans == 1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ } else
+ env->sr &= ~SR_T;
+}
+
+#define T (env->sr & SR_T)
+#define Q (env->sr & SR_Q ? 1 : 0)
+#define M (env->sr & SR_M ? 1 : 0)
+#define SETT env->sr |= SR_T
+#define CLRT env->sr &= ~SR_T
+#define SETQ env->sr |= SR_Q
+#define CLRQ env->sr &= ~SR_Q
+#define SETM env->sr |= SR_M
+#define CLRM env->sr &= ~SR_M
+
+void helper_div1_T0_T1(void)
+{
+ uint32_t tmp0, tmp2;
+ uint8_t old_q, tmp1 = 0xff;
+
+ //printf("div1 T0=0x%08x T1=0x%08x M=%d Q=%d T=%d\n", T0, T1, M, Q, T);
+ old_q = Q;
+ if ((0x80000000 & T1) != 0)
+ SETQ;
+ else
+ CLRQ;
+ tmp2 = T0;
+ T1 <<= 1;
+ T1 |= T;
+ switch (old_q) {
+ case 0:
+ switch (M) {
+ case 0:
+ tmp0 = T1;
+ T1 -= tmp2;
+ tmp1 = T1 > tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ case 1:
+ tmp0 = T1;
+ T1 += tmp2;
+ tmp1 = T1 < tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ }
+ break;
+ case 1:
+ switch (M) {
+ case 0:
+ tmp0 = T1;
+ T1 += tmp2;
+ tmp1 = T1 < tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ case 1:
+ tmp0 = T1;
+ T1 -= tmp2;
+ tmp1 = T1 > tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ if (Q == M)
+ SETT;
+ else
+ CLRT;
+ //printf("Output: T1=0x%08x M=%d Q=%d T=%d\n", T1, M, Q, T);
+}
+
+void helper_dmulsl_T0_T1()
+{
+ int64_t res;
+
+ res = (int64_t) (int32_t) T0 *(int64_t) (int32_t) T1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+}
+
+void helper_dmulul_T0_T1()
+{
+ uint64_t res;
+
+ res = (uint64_t) (uint32_t) T0 *(uint64_t) (uint32_t) T1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+}
+
+void helper_macl_T0_T1()
+{
+ int64_t res;
+
+ res = ((uint64_t) env->mach << 32) | env->macl;
+ res += (int64_t) (int32_t) T0 *(int64_t) (int32_t) T1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+ if (env->sr & SR_S) {
+ if (res < 0)
+ env->mach |= 0xffff0000;
+ else
+ env->mach &= 0x00007fff;
+ }
+}
+
+void helper_macw_T0_T1()
+{
+ int64_t res;
+
+ res = ((uint64_t) env->mach << 32) | env->macl;
+ res += (int64_t) (int16_t) T0 *(int64_t) (int16_t) T1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+ if (env->sr & SR_S) {
+ if (res < -0x80000000) {
+ env->mach = 1;
+ env->macl = 0x80000000;
+ } else if (res > 0x000000007fffffff) {
+ env->mach = 1;
+ env->macl = 0x7fffffff;
+ }
+ }
+}
+
+void helper_negc_T0()
+{
+ uint32_t temp;
+
+ temp = -T0;
+ T0 = temp - (env->sr & SR_T);
+ if (0 < temp)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ if (temp < T0)
+ env->sr |= SR_T;
+}
+
+void helper_subc_T0_T1()
+{
+ uint32_t tmp0, tmp1;
+
+ tmp1 = T1 - T0;
+ tmp0 = T1;
+ T1 = tmp1 - (env->sr & SR_T);
+ if (tmp0 < tmp1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ if (tmp1 < T1)
+ env->sr |= SR_T;
+}
+
+void helper_subv_T0_T1()
+{
+ int32_t dest, src, ans;
+
+ if ((int32_t) T1 >= 0)
+ dest = 0;
+ else
+ dest = 1;
+ if ((int32_t) T0 >= 0)
+ src = 0;
+ else
+ src = 1;
+ src += dest;
+ T1 -= T0;
+ if ((int32_t) T1 >= 0)
+ ans = 0;
+ else
+ ans = 1;
+ ans += dest;
+ if (src == 1) {
+ if (ans == 1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ } else
+ env->sr &= ~SR_T;
+}
+
+void helper_rotcl(uint32_t * addr)
+{
+ uint32_t new;
+
+ new = (*addr << 1) | (env->sr & SR_T);
+ if (*addr & 0x80000000)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ *addr = new;
+}
+
+void helper_rotcr(uint32_t * addr)
+{
+ uint32_t new;
+
+ new = (*addr >> 1) | ((env->sr & SR_T) ? 0x80000000 : 0);
+ if (*addr & 1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ *addr = new;
+}
diff --git a/target-sh4/op_mem.c b/target-sh4/op_mem.c
new file mode 100644
index 0000000..ca39abf
--- /dev/null
+++ b/target-sh4/op_mem.c
@@ -0,0 +1,78 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+void glue(op_ldb_T0_T0, MEMSUFFIX) (void) {
+ T0 = glue(ldsb, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_ldub_T0_T0, MEMSUFFIX) (void) {
+ T0 = glue(ldub, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_stb_T0_T1, MEMSUFFIX) (void) {
+ glue(stb, MEMSUFFIX) (T1, T0);
+ RETURN();
+}
+
+void glue(op_ldw_T0_T0, MEMSUFFIX) (void) {
+ T0 = glue(ldsw, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_lduw_T0_T0, MEMSUFFIX) (void) {
+ T0 = glue(lduw, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_stw_T0_T1, MEMSUFFIX) (void) {
+ glue(stw, MEMSUFFIX) (T1, T0);
+ RETURN();
+}
+
+void glue(op_ldl_T0_T0, MEMSUFFIX) (void) {
+ T0 = glue(ldl, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_stl_T0_T1, MEMSUFFIX) (void) {
+ glue(stl, MEMSUFFIX) (T1, T0);
+ RETURN();
+}
+
+void glue(op_ldfl_T0_FT0, MEMSUFFIX) (void) {
+ FT0 = glue(ldfl, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_stfl_FT0_T1, MEMSUFFIX) (void) {
+ glue(stfl, MEMSUFFIX) (T1, FT0);
+ RETURN();
+}
+
+void glue(op_ldfq_T0_DT0, MEMSUFFIX) (void) {
+ DT0 = glue(ldfq, MEMSUFFIX) (T0);
+ RETURN();
+}
+
+void glue(op_stfq_DT0_T1, MEMSUFFIX) (void) {
+ glue(stfq, MEMSUFFIX) (T1, DT0);
+ RETURN();
+}
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
new file mode 100644
index 0000000..358f975
--- /dev/null
+++ b/target-sh4/translate.c
@@ -0,0 +1,1219 @@
+/*
+ * SH4 translation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#define DEBUG_DISAS
+#define SH4_DEBUG_DISAS
+//#define SH4_SINGLE_STEP
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) ((long)(x))
+#endif
+
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+
+#include "gen-op.h"
+
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc;
+ uint32_t sr;
+ uint32_t fpscr;
+ uint16_t opcode;
+ uint32_t flags;
+ int memidx;
+ uint32_t delayed_pc;
+ int singlestep_enabled;
+} DisasContext;
+
+#ifdef CONFIG_USER_ONLY
+
+#define GEN_OP_LD(width, reg) \
+ void gen_op_ld##width##_T0_##reg (DisasContext *ctx) { \
+ gen_op_ld##width##_T0_##reg##_raw(); \
+ }
+#define GEN_OP_ST(width, reg) \
+ void gen_op_st##width##_##reg##_T1 (DisasContext *ctx) { \
+ gen_op_st##width##_##reg##_T1_raw(); \
+ }
+
+#else
+
+#define GEN_OP_LD(width, reg) \
+ void gen_op_ld##width##_T0_##reg (DisasContext *ctx) { \
+ if (ctx->memidx) gen_op_ld##width##_T0_##reg##_kernel(); \
+ else gen_op_ld##width##_T0_##reg##_user();\
+ }
+#define GEN_OP_ST(width, reg) \
+ void gen_op_st##width##_##reg##_T1 (DisasContext *ctx) { \
+ if (ctx->memidx) gen_op_st##width##_##reg##_T1_kernel(); \
+ else gen_op_st##width##_##reg##_T1_user();\
+ }
+
+#endif
+
+GEN_OP_LD(ub, T0)
+GEN_OP_LD(b, T0)
+GEN_OP_ST(b, T0)
+GEN_OP_LD(uw, T0)
+GEN_OP_LD(w, T0)
+GEN_OP_ST(w, T0)
+GEN_OP_LD(l, T0)
+GEN_OP_ST(l, T0)
+GEN_OP_LD(fl, FT0)
+GEN_OP_ST(fl, FT0)
+GEN_OP_LD(fq, DT0)
+GEN_OP_ST(fq, DT0)
+
+void cpu_dump_state(CPUState * env, FILE * f,
+ int (*cpu_fprintf) (FILE * f, const char *fmt, ...),
+ int flags)
+{
+ int i;
+ cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n",
+ env->pc, env->sr, env->pr, env->fpscr);
+ for (i = 0; i < 24; i += 4) {
+ cpu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n",
+ i, env->gregs[i], i + 1, env->gregs[i + 1],
+ i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]);
+ }
+ if (env->flags & DELAY_SLOT) {
+ cpu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n",
+ env->delayed_pc);
+ } else if (env->flags & DELAY_SLOT_CONDITIONAL) {
+ cpu_fprintf(f, "in conditional delay slot (delayed_pc=0x%08x)\n",
+ env->delayed_pc);
+ }
+}
+
+void cpu_sh4_reset(CPUSH4State * env)
+{
+#if defined(CONFIG_USER_ONLY)
+ env->sr = 0x00000000;
+#else
+ env->sr = 0x700000F0; /* MD, RB, BL, I3-I0 */
+#endif
+ env->vbr = 0;
+ env->pc = 0xA0000000;
+ env->fpscr = 0x00040001;
+ env->mmucr = 0;
+}
+
+CPUSH4State *cpu_sh4_init(void)
+{
+ CPUSH4State *env;
+
+ env = qemu_mallocz(sizeof(CPUSH4State));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_sh4_reset(env);
+ tlb_flush(env, 1);
+ return env;
+}
+
+static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
+ !ctx->singlestep_enabled) {
+ /* Use a direct jump if in same page and singlestep not enabled */
+ if (n == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_op_movl_imm_T0((long) tb + n);
+ } else {
+ gen_op_movl_imm_T0(0);
+ }
+ gen_op_movl_imm_PC(dest);
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_exit_tb();
+}
+
+/* Jump to pc after an exception */
+static void gen_jump_exception(DisasContext * ctx)
+{
+ gen_op_movl_imm_T0(0);
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_exit_tb();
+}
+
+static void gen_jump(DisasContext * ctx)
+{
+ if (ctx->delayed_pc == (uint32_t) - 1) {
+ /* Target is not statically known, it comes necessarily from a
+ delayed jump as immediate jump are conditinal jumps */
+ gen_op_movl_delayed_pc_PC();
+ gen_op_movl_imm_T0(0);
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_exit_tb();
+ } else {
+ gen_goto_tb(ctx, 0, ctx->delayed_pc);
+ }
+}
+
+/* Immediate conditional jump (bt or bf) */
+static void gen_conditional_jump(DisasContext * ctx,
+ target_ulong ift, target_ulong ifnott)
+{
+ int l1;
+
+ l1 = gen_new_label();
+ gen_op_jT(l1);
+ gen_goto_tb(ctx, 0, ifnott);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 1, ift);
+}
+
+/* Delayed conditional jump (bt or bf) */
+static void gen_delayed_conditional_jump(DisasContext * ctx)
+{
+ int l1;
+
+ l1 = gen_new_label();
+ gen_op_jdelayed(l1);
+ gen_goto_tb(ctx, 1, ctx->pc);
+ gen_set_label(l1);
+ gen_jump(ctx);
+}
+
+#define B3_0 (ctx->opcode & 0xf)
+#define B6_4 ((ctx->opcode >> 4) & 0x7)
+#define B7_4 ((ctx->opcode >> 4) & 0xf)
+#define B7_0 (ctx->opcode & 0xff)
+#define B7_0s ((int32_t) (int8_t) (ctx->opcode & 0xff))
+#define B11_0s (ctx->opcode & 0x800 ? 0xfffff000 | (ctx->opcode & 0xfff) : \
+ (ctx->opcode & 0xfff))
+#define B11_8 ((ctx->opcode >> 8) & 0xf)
+#define B15_12 ((ctx->opcode >> 12) & 0xf)
+
+#define REG(x) ((x) < 8 && (ctx->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB) ? \
+ (x) + 16 : (x))
+
+#define ALTREG(x) ((x) < 8 && (ctx->sr & (SR_MD | SR_RB)) != (SR_MD | SR_RB) \
+ ? (x) + 16 : (x))
+
+#define FREG(x) (ctx->fpscr & FPSCR_FR ? (x) ^ 0x10 : (x))
+#define XHACK(x) (((x) & 1 ) << 4 | ((x) & 0xe ) << 1)
+#define XREG(x) (ctx->fpscr & FPSCR_FR ? XHACK(x) ^ 0x10 : XHACK(x))
+
+#define CHECK_NOT_DELAY_SLOT \
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \
+ {gen_op_raise_slot_illegal_instruction (); ctx->flags |= BRANCH_EXCEPTION; \
+ return;}
+
+void decode_opc(DisasContext * ctx)
+{
+#if 0
+ fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
+#endif
+ switch (ctx->opcode) {
+ case 0x0019: /* div0u */
+ gen_op_div0u();
+ return;
+ case 0x000b: /* rts */
+ CHECK_NOT_DELAY_SLOT gen_op_rts();
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0028: /* clrmac */
+ gen_op_clrmac();
+ return;
+ case 0x0048: /* clrs */
+ gen_op_clrs();
+ return;
+ case 0x0008: /* clrt */
+ gen_op_clrt();
+ return;
+ case 0x0038: /* ldtlb */
+ assert(0); /* XXXXX */
+ return;
+ case 0x004b: /* rte */
+ CHECK_NOT_DELAY_SLOT gen_op_rte();
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0058: /* sets */
+ gen_op_sets();
+ return;
+ case 0x0018: /* sett */
+ gen_op_sett();
+ return;
+ case 0xfbfb: /* frchg */
+ gen_op_frchg();
+ ctx->flags |= MODE_CHANGE;
+ return;
+ case 0xf3fb: /* fschg */
+ gen_op_fschg();
+ ctx->flags |= MODE_CHANGE;
+ return;
+ case 0x0009: /* nop */
+ return;
+ case 0x001b: /* sleep */
+ assert(0); /* XXXXX */
+ return;
+ }
+
+ switch (ctx->opcode & 0xf000) {
+ case 0x1000: /* mov.l Rm,@(disp,Rn) */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_addl_imm_T1(B3_0 * 4);
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x5000: /* mov.l @(disp,Rm),Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_addl_imm_T0(B3_0 * 4);
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0xe000: /* mov.l #imm,Rn */
+ gen_op_movl_imm_rN(B7_0s, REG(B11_8));
+ return;
+ case 0x9000: /* mov.w @(disp,PC),Rn */
+ gen_op_movl_imm_T0(ctx->pc + 4 + B7_0 * 2);
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0xd000: /* mov.l @(disp,PC),Rn */
+ gen_op_movl_imm_T0((ctx->pc + 4 + B7_0 * 4) & ~3);
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x7000: /* add.l #imm,Rn */
+ gen_op_add_imm_rN(B7_0s, REG(B11_8));
+ return;
+ case 0xa000: /* bra disp */
+ CHECK_NOT_DELAY_SLOT
+ gen_op_bra(ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2);
+ ctx->flags |= DELAY_SLOT;
+ return;
+ case 0xb000: /* bsr disp */
+ CHECK_NOT_DELAY_SLOT
+ gen_op_bsr(ctx->pc + 4, ctx->delayed_pc =
+ ctx->pc + 4 + B11_0s * 2);
+ ctx->flags |= DELAY_SLOT;
+ return;
+ }
+
+ switch (ctx->opcode & 0xf00f) {
+ case 0x6003: /* mov Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x2000: /* mov.b Rm,@Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x2001: /* mov.w Rm,@Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stw_T0_T1(ctx);
+ return;
+ case 0x2002: /* mov.l Rm,@Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x6000: /* mov.b @Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x6001: /* mov.w @Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x6002: /* mov.l @Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x2004: /* mov.b Rm,@-Rn */
+ gen_op_dec1_rN(REG(B11_8));
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x2005: /* mov.w Rm,@-Rn */
+ gen_op_dec2_rN(REG(B11_8));
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stw_T0_T1(ctx);
+ return;
+ case 0x2006: /* mov.l Rm,@-Rn */
+ gen_op_dec4_rN(REG(B11_8));
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x6004: /* mov.b @Rm+,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ gen_op_inc1_rN(REG(B7_4));
+ return;
+ case 0x6005: /* mov.w @Rm+,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ gen_op_inc2_rN(REG(B7_4));
+ return;
+ case 0x6006: /* mov.l @Rm+,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ gen_op_inc4_rN(REG(B7_4));
+ return;
+ case 0x0004: /* mov.b Rm,@(R0,Rn) */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x0005: /* mov.w Rm,@(R0,Rn) */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stw_T0_T1(ctx);
+ return;
+ case 0x0006: /* mov.l Rm,@(R0,Rn) */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x000c: /* mov.b @(R0,Rm),Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x000d: /* mov.w @(R0,Rm),Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x000e: /* mov.l @(R0,Rm),Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x6008: /* swap.b Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_swapb_T0();
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x6009: /* swap.w Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_swapw_T0();
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x200d: /* xtrct Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_xtrct_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x300c: /* add Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_T0_rN(REG(B11_8));
+ return;
+ case 0x300e: /* addc Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_addc_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x300f: /* addv Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_addv_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x2009: /* and Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_and_T0_rN(REG(B11_8));
+ return;
+ case 0x3000: /* cmp/eq Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_eq_T0_T1();
+ return;
+ case 0x3003: /* cmp/ge Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_ge_T0_T1();
+ return;
+ case 0x3007: /* cmp/gt Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_gt_T0_T1();
+ return;
+ case 0x3006: /* cmp/hi Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_hi_T0_T1();
+ return;
+ case 0x3002: /* cmp/hs Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_hs_T0_T1();
+ return;
+ case 0x200c: /* cmp/str Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_cmp_str_T0_T1();
+ return;
+ case 0x2007: /* div0s Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_div0s_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x3004: /* div1 Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_div1_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x300d: /* dmuls.l Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_dmulsl_T0_T1();
+ return;
+ case 0x3005: /* dmulu.l Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_dmulul_T0_T1();
+ return;
+ case 0x600e: /* exts.b Rm,Rn */
+ gen_op_movb_rN_T0(REG(B7_4));
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x600f: /* exts.w Rm,Rn */
+ gen_op_movw_rN_T0(REG(B7_4));
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x600c: /* extu.b Rm,Rn */
+ gen_op_movub_rN_T0(REG(B7_4));
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x600d: /* extu.w Rm,Rn */
+ gen_op_movuw_rN_T0(REG(B7_4));
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x000f: /* mac.l @Rm+,@Rn- */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_T1();
+ gen_op_movl_rN_T1(REG(B7_4));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_macl_T0_T1();
+ gen_op_inc4_rN(REG(B7_4));
+ gen_op_inc4_rN(REG(B11_8));
+ return;
+ case 0x400f: /* mac.w @Rm+,@Rn+ */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_T1();
+ gen_op_movl_rN_T1(REG(B7_4));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_macw_T0_T1();
+ gen_op_inc2_rN(REG(B7_4));
+ gen_op_inc2_rN(REG(B11_8));
+ return;
+ case 0x0007: /* mul.l Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_mull_T0_T1();
+ return;
+ case 0x200f: /* muls.w Rm,Rn */
+ gen_op_movw_rN_T0(REG(B7_4));
+ gen_op_movw_rN_T1(REG(B11_8));
+ gen_op_mulsw_T0_T1();
+ return;
+ case 0x200e: /* mulu.w Rm,Rn */
+ gen_op_movuw_rN_T0(REG(B7_4));
+ gen_op_movuw_rN_T1(REG(B11_8));
+ gen_op_muluw_T0_T1();
+ return;
+ case 0x600b: /* neg Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_neg_T0();
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x600a: /* negc Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_negc_T0();
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x6007: /* not Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_not_T0();
+ gen_op_movl_T0_rN(REG(B11_8));
+ return;
+ case 0x200b: /* or Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_or_T0_rN(REG(B11_8));
+ return;
+ case 0x400c: /* shad Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_shad_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x400d: /* shld Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_shld_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x3008: /* sub Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_sub_T0_rN(REG(B11_8));
+ return;
+ case 0x300a: /* subc Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_subc_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x300b: /* subv Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_subv_T0_T1();
+ gen_op_movl_T1_rN(REG(B11_8));
+ return;
+ case 0x2008: /* tst Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_tst_T0_T1();
+ return;
+ case 0x200a: /* xor Rm,Rn */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_xor_T0_rN(REG(B11_8));
+ return;
+ case 0xf00c: /* fmov {F,D,X}Rm,{F,D,X}Rn */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0110)
+ break; /* illegal instruction */
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else {
+ gen_op_fmov_frN_FT0(FREG(B7_4));
+ gen_op_fmov_FT0_frN(FREG(B11_8));
+ }
+ return;
+ case 0xf00a: /* fmov {F,D,X}Rm,@Rn */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfq_DT0_T1(ctx);
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0010)
+ break; /* illegal instruction */
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfq_DT0_T1(ctx);
+ } else {
+ gen_op_fmov_frN_FT0(FREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfl_FT0_T1(ctx);
+ }
+ return;
+ case 0xf008: /* fmov @Rm,{F,D,X}Rn */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfl_T0_FT0(ctx);
+ gen_op_fmov_FT0_frN(XREG(B11_8));
+ }
+ return;
+ case 0xf009: /* fmov @Rm+,{F,D,X}Rn */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ gen_op_inc8_rN(REG(B7_4));
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ gen_op_inc8_rN(REG(B7_4));
+ } else {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_ldfl_T0_FT0(ctx);
+ gen_op_fmov_FT0_frN(XREG(B11_8));
+ gen_op_inc4_rN(REG(B7_4));
+ }
+ return;
+ case 0xf00b: /* fmov {F,D,X}Rm,@-Rn */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_dec8_rN(REG(B11_8));
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfq_DT0_T1(ctx);
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ gen_op_dec8_rN(REG(B11_8));
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfq_DT0_T1(ctx);
+ } else {
+ gen_op_dec4_rN(REG(B11_8));
+ gen_op_fmov_frN_FT0(FREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stfl_FT0_T1(ctx);
+ }
+ return;
+ case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldfq_T0_DT0(ctx);
+ gen_op_fmov_DT0_drN(XREG(B11_8));
+ } else {
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_add_rN_T0(REG(0));
+ gen_op_ldfl_T0_FT0(ctx);
+ gen_op_fmov_FT0_frN(XREG(B11_8));
+ }
+ return;
+ case 0xf007: /* fmov {F,D,X}Rn,@(R0,Rn) */
+ if (ctx->fpscr & FPSCR_PR) {
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stfq_DT0_T1(ctx);
+ } else if (ctx->fpscr & FPSCR_SZ) {
+ if (ctx->opcode & 0x0010)
+ break; /* illegal instruction */
+ gen_op_fmov_drN_DT0(XREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stfq_DT0_T1(ctx);
+ } else {
+ gen_op_fmov_frN_FT0(FREG(B7_4));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_add_rN_T1(REG(0));
+ gen_op_stfl_FT0_T1(ctx);
+ }
+ return;
+ }
+
+ switch (ctx->opcode & 0xff00) {
+ case 0xc900: /* and #imm,R0 */
+ gen_op_and_imm_rN(B7_0, REG(0));
+ return;
+ case 0xcd00: /* and.b #imm,@(R0+GBR) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_addl_GBR_T0();
+ gen_op_movl_T0_T1();
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_and_imm_T0(B7_0);
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x8b00: /* bf label */
+ CHECK_NOT_DELAY_SLOT
+ gen_conditional_jump(ctx, ctx->pc + 2,
+ ctx->pc + 4 + B7_0s * 2);
+ ctx->flags |= BRANCH_CONDITIONAL;
+ return;
+ case 0x8f00: /* bf/s label */
+ CHECK_NOT_DELAY_SLOT
+ gen_op_bf_s(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2);
+ ctx->flags |= DELAY_SLOT_CONDITIONAL;
+ return;
+ case 0x8900: /* bt label */
+ CHECK_NOT_DELAY_SLOT
+ gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2,
+ ctx->pc + 2);
+ ctx->flags |= BRANCH_CONDITIONAL;
+ return;
+ case 0x8d00: /* bt/s label */
+ CHECK_NOT_DELAY_SLOT
+ gen_op_bt_s(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2);
+ ctx->flags |= DELAY_SLOT_CONDITIONAL;
+ return;
+ case 0x8800: /* cmp/eq #imm,R0 */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_cmp_eq_imm_T0(B7_0s);
+ return;
+ case 0xc400: /* mov.b @(disp,GBR),R0 */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(0));
+ return;
+ case 0xc500: /* mov.w @(disp,GBR),R0 */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(0));
+ return;
+ case 0xc600: /* mov.l @(disp,GBR),R0 */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(0));
+ return;
+ case 0xc000: /* mov.b R0,@(disp,GBR) */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_movl_T0_T1();
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0xc100: /* mov.w R0,@(disp,GBR) */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_movl_T0_T1();
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_stw_T0_T1(ctx);
+ return;
+ case 0xc200: /* mov.l R0,@(disp,GBR) */
+ gen_op_stc_gbr_T0();
+ gen_op_addl_imm_T0(B7_0);
+ gen_op_movl_T0_T1();
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x8000: /* mov.b R0,@(disp,Rn) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_movl_rN_T1(REG(B7_4));
+ gen_op_addl_imm_T1(B3_0);
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x8100: /* mov.w R0,@(disp,Rn) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_movl_rN_T1(REG(B7_4));
+ gen_op_addl_imm_T1(B3_0 * 2);
+ gen_op_stw_T0_T1(ctx);
+ return;
+ case 0x8400: /* mov.b @(disp,Rn),R0 */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_movl_rN_T1(REG(B7_4));
+ gen_op_addl_imm_T1(B3_0);
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0x8500: /* mov.w @(disp,Rn),R0 */
+ gen_op_movl_rN_T0(REG(B7_4));
+ gen_op_addl_imm_T0(B3_0 * 2);
+ gen_op_ldw_T0_T0(ctx);
+ gen_op_movl_T0_rN(REG(0));
+ return;
+ case 0xc700: /* mova @(disp,PC),R0 */
+ gen_op_movl_imm_rN(((ctx->pc & 0xfffffffc) + 4 + B7_0 * 4) & ~3,
+ REG(0));
+ return;
+ case 0xcb00: /* or #imm,R0 */
+ gen_op_or_imm_rN(B7_0, REG(0));
+ return;
+ case 0xcf00: /* or.b #imm,@(R0+GBR) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_addl_GBR_T0();
+ gen_op_movl_T0_T1();
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_or_imm_T0(B7_0);
+ gen_op_stb_T0_T1(ctx);
+ return;
+ case 0xc300: /* trapa #imm */
+ CHECK_NOT_DELAY_SLOT gen_op_movl_imm_PC(ctx->pc);
+ gen_op_trapa(B7_0);
+ ctx->flags |= BRANCH;
+ return;
+ case 0xc800: /* tst #imm,R0 */
+ gen_op_tst_imm_rN(B7_0, REG(0));
+ return;
+ case 0xcc00: /* tst #imm,@(R0+GBR) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_addl_GBR_T0();
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_tst_imm_T0(B7_0);
+ return;
+ case 0xca00: /* xor #imm,R0 */
+ gen_op_xor_imm_rN(B7_0, REG(0));
+ return;
+ case 0xce00: /* xor.b #imm,@(R0+GBR) */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_addl_GBR_T0();
+ gen_op_movl_T0_T1();
+ gen_op_ldb_T0_T0(ctx);
+ gen_op_xor_imm_T0(B7_0);
+ gen_op_stb_T0_T1(ctx);
+ return;
+ }
+
+ switch (ctx->opcode & 0xf08f) {
+ case 0x408e: /* ldc Rm,Rn_BANK */
+ gen_op_movl_rN_rN(REG(B11_8), ALTREG(B6_4));
+ return;
+ case 0x4087: /* ldc.l @Rm+,Rn_BANK */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ gen_op_movl_T0_rN(ALTREG(B6_4));
+ gen_op_inc4_rN(REG(B11_8));
+ return;
+ case 0x0082: /* stc Rm_BANK,Rn */
+ gen_op_movl_rN_rN(ALTREG(B6_4), REG(B11_8));
+ return;
+ case 0x4083: /* stc.l Rm_BANK,@-Rn */
+ gen_op_dec4_rN(REG(B11_8));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_movl_rN_T0(ALTREG(B6_4));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ }
+
+ switch (ctx->opcode & 0xf0ff) {
+ case 0x0023: /* braf Rn */
+ CHECK_NOT_DELAY_SLOT gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_braf_T0(ctx->pc + 4);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0003: /* bsrf Rn */
+ CHECK_NOT_DELAY_SLOT gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_bsrf_T0(ctx->pc + 4);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x4015: /* cmp/pl Rn */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_cmp_pl_T0();
+ return;
+ case 0x4011: /* cmp/pz Rn */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_cmp_pz_T0();
+ return;
+ case 0x4010: /* dt Rn */
+ gen_op_dt_rN(REG(B11_8));
+ return;
+ case 0x402b: /* jmp @Rn */
+ CHECK_NOT_DELAY_SLOT gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_jmp_T0();
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x400b: /* jsr @Rn */
+ CHECK_NOT_DELAY_SLOT gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_jsr_T0(ctx->pc + 4);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+#define LDST(reg,ldnum,ldpnum,ldop,stnum,stpnum,stop,extrald) \
+ case ldnum: \
+ gen_op_movl_rN_T0 (REG(B11_8)); \
+ gen_op_##ldop##_T0_##reg (); \
+ extrald \
+ return; \
+ case ldpnum: \
+ gen_op_movl_rN_T0 (REG(B11_8)); \
+ gen_op_ldl_T0_T0 (ctx); \
+ gen_op_inc4_rN (REG(B11_8)); \
+ gen_op_##ldop##_T0_##reg (); \
+ extrald \
+ return; \
+ case stnum: \
+ gen_op_##stop##_##reg##_T0 (); \
+ gen_op_movl_T0_rN (REG(B11_8)); \
+ return; \
+ case stpnum: \
+ gen_op_##stop##_##reg##_T0 (); \
+ gen_op_dec4_rN (REG(B11_8)); \
+ gen_op_movl_rN_T1 (REG(B11_8)); \
+ gen_op_stl_T0_T1 (ctx); \
+ return;
+ LDST(sr, 0x400e, 0x4007, ldc, 0x0002, 0x4003, stc, ctx->flags |=
+ MODE_CHANGE;)
+ LDST(gbr, 0x401e, 0x4017, ldc, 0x0012, 0x4013, stc,)
+ LDST(vbr, 0x402e, 0x4027, ldc, 0x0022, 0x4023, stc,)
+ LDST(ssr, 0x403e, 0x4037, ldc, 0x0032, 0x4033, stc,)
+ LDST(spc, 0x404e, 0x4047, ldc, 0x0042, 0x4043, stc,)
+ LDST(dbr, 0x40fa, 0x40f6, ldc, 0x00fa, 0x40f2, stc,)
+ LDST(mach, 0x400a, 0x4006, lds, 0x000a, 0x4002, sts,)
+ LDST(macl, 0x401a, 0x4016, lds, 0x001a, 0x4012, sts,)
+ LDST(pr, 0x402a, 0x4026, lds, 0x002a, 0x4022, sts,)
+ LDST(fpul, 0x405a, 0x4056, lds, 0x005a, 0x0052, sts,)
+ LDST(fpscr, 0x406a, 0x4066, lds, 0x006a, 0x0062, sts, ctx->flags |=
+ MODE_CHANGE;)
+ case 0x00c3: /* movca.l R0,@Rm */
+ gen_op_movl_rN_T0(REG(0));
+ gen_op_movl_rN_T1(REG(B11_8));
+ gen_op_stl_T0_T1(ctx);
+ return;
+ case 0x0029: /* movt Rn */
+ gen_op_movt_rN(REG(B11_8));
+ return;
+ case 0x0093: /* ocbi @Rn */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ return;
+ case 0x00a2: /* ocbp @Rn */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ return;
+ case 0x00b3: /* ocbwb @Rn */
+ gen_op_movl_rN_T0(REG(B11_8));
+ gen_op_ldl_T0_T0(ctx);
+ return;
+ case 0x0083: /* pref @Rn */
+ return;
+ case 0x4024: /* rotcl Rn */
+ gen_op_rotcl_Rn(REG(B11_8));
+ return;
+ case 0x4025: /* rotcr Rn */
+ gen_op_rotcr_Rn(REG(B11_8));
+ return;
+ case 0x4004: /* rotl Rn */
+ gen_op_rotl_Rn(REG(B11_8));
+ return;
+ case 0x4005: /* rotr Rn */
+ gen_op_rotr_Rn(REG(B11_8));
+ return;
+ case 0x4000: /* shll Rn */
+ case 0x4020: /* shal Rn */
+ gen_op_shal_Rn(REG(B11_8));
+ return;
+ case 0x4021: /* shar Rn */
+ gen_op_shar_Rn(REG(B11_8));
+ return;
+ case 0x4001: /* shlr Rn */
+ gen_op_shlr_Rn(REG(B11_8));
+ return;
+ case 0x4008: /* shll2 Rn */
+ gen_op_shll2_Rn(REG(B11_8));
+ return;
+ case 0x4018: /* shll8 Rn */
+ gen_op_shll8_Rn(REG(B11_8));
+ return;
+ case 0x4028: /* shll16 Rn */
+ gen_op_shll16_Rn(REG(B11_8));
+ return;
+ case 0x4009: /* shlr2 Rn */
+ gen_op_shlr2_Rn(REG(B11_8));
+ return;
+ case 0x4019: /* shlr8 Rn */
+ gen_op_shlr8_Rn(REG(B11_8));
+ return;
+ case 0x4029: /* shlr16 Rn */
+ gen_op_shlr16_Rn(REG(B11_8));
+ return;
+ case 0x401b: /* tas.b @Rn */
+ gen_op_tasb_rN(REG(B11_8));
+ return;
+ case 0xf00d: /* fsts FPUL,FRn */
+ gen_op_movl_fpul_FT0();
+ gen_op_fmov_FT0_frN(FREG(B11_8));
+ return;
+ case 0xf01d: /* flds FRm.FPUL */
+ gen_op_fmov_frN_FT0(FREG(B11_8));
+ gen_op_movl_FT0_fpul();
+ return;
+ }
+
+ fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",
+ ctx->opcode, ctx->pc);
+ gen_op_raise_illegal_instruction();
+ ctx->flags |= BRANCH_EXCEPTION;
+}
+
+int gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
+ int search_pc)
+{
+ DisasContext ctx;
+ target_ulong pc_start;
+ static uint16_t *gen_opc_end;
+ uint32_t old_flags;
+ int i, ii;
+
+ pc_start = tb->pc;
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ ctx.pc = pc_start;
+ ctx.flags = env->flags;
+ old_flags = 0;
+ ctx.sr = env->sr;
+ ctx.fpscr = env->fpscr;
+ ctx.memidx = (env->sr & SR_MD) ? 1 : 0;
+ /* We don't know if the delayed pc came from a dynamic or static branch,
+ so assume it is a dynamic branch. */
+ ctx.delayed_pc = -1;
+ ctx.tb = tb;
+ ctx.singlestep_enabled = env->singlestep_enabled;
+ nb_gen_labels = 0;
+
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_CPU) {
+ fprintf(logfile,
+ "------------------------------------------------\n");
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+#endif
+
+ ii = -1;
+ while ((old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) == 0 &&
+ (ctx.flags & (BRANCH | BRANCH_CONDITIONAL | MODE_CHANGE |
+ BRANCH_EXCEPTION)) == 0 &&
+ gen_opc_ptr < gen_opc_end && ctx.sr == env->sr) {
+ old_flags = ctx.flags;
+ if (env->nb_breakpoints > 0) {
+ for (i = 0; i < env->nb_breakpoints; i++) {
+ if (ctx.pc == env->breakpoints[i]) {
+ /* We have hit a breakpoint - make sure PC is up-to-date */
+ gen_op_movl_imm_PC(ctx.pc);
+ gen_op_debug();
+ ctx.flags |= BRANCH_EXCEPTION;
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ i = gen_opc_ptr - gen_opc_buf;
+ if (ii < i) {
+ ii++;
+ while (ii < i)
+ gen_opc_instr_start[ii++] = 0;
+ }
+ gen_opc_pc[ii] = ctx.pc;
+ gen_opc_instr_start[ii] = 1;
+ }
+#if 0
+ fprintf(stderr, "Loading opcode at address 0x%08x\n", ctx.pc);
+ fflush(stderr);
+#endif
+ ctx.opcode = lduw_code(ctx.pc);
+ decode_opc(&ctx);
+ ctx.pc += 2;
+ if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+ if (env->singlestep_enabled)
+ break;
+#ifdef SH4_SINGLE_STEP
+ break;
+#endif
+ }
+
+ if (old_flags & DELAY_SLOT_CONDITIONAL) {
+ gen_delayed_conditional_jump(&ctx);
+ } else if (old_flags & DELAY_SLOT) {
+ gen_op_clr_delay_slot();
+ gen_jump(&ctx);
+ } else if (ctx.flags & BRANCH_EXCEPTION) {
+ gen_jump_exception(&ctx);
+ } else if ((ctx.flags & (BRANCH | BRANCH_CONDITIONAL)) == 0) {
+ gen_goto_tb(&ctx, 0, ctx.pc);
+ }
+
+ if (env->singlestep_enabled) {
+ gen_op_debug();
+ }
+ *gen_opc_ptr = INDEX_op_end;
+ if (search_pc) {
+ i = gen_opc_ptr - gen_opc_buf;
+ ii++;
+ while (ii <= i)
+ gen_opc_instr_start[ii++] = 0;
+ tb->size = 0;
+ } else {
+ tb->size = ctx.pc - pc_start;
+ }
+
+#ifdef DEBUG_DISAS
+#ifdef SH4_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM)
+ fprintf(logfile, "\n");
+#endif
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "IN:\n"); /* , lookup_symbol(pc_start)); */
+ target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
+ fprintf(logfile, "\n");
+ }
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+#endif
+ return 0;
+}
+
+int gen_intermediate_code(CPUState * env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc(CPUState * env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
new file mode 100644
index 0000000..8cbf0b2
--- /dev/null
+++ b/target-sparc/cpu.h
@@ -0,0 +1,277 @@
+#ifndef CPU_SPARC_H
+#define CPU_SPARC_H
+
+#include "config.h"
+
+#if !defined(TARGET_SPARC64)
+#define TARGET_LONG_BITS 32
+#define TARGET_FPREGS 32
+#define TARGET_PAGE_BITS 12 /* 4k */
+#else
+#define TARGET_LONG_BITS 64
+#define TARGET_FPREGS 64
+#define TARGET_PAGE_BITS 12 /* XXX */
+#endif
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+/*#define EXCP_INTERRUPT 0x100*/
+
+/* trap definitions */
+#ifndef TARGET_SPARC64
+#define TT_TFAULT 0x01
+#define TT_ILL_INSN 0x02
+#define TT_PRIV_INSN 0x03
+#define TT_NFPU_INSN 0x04
+#define TT_WIN_OVF 0x05
+#define TT_WIN_UNF 0x06
+#define TT_FP_EXCP 0x08
+#define TT_DFAULT 0x09
+#define TT_EXTINT 0x10
+#define TT_DIV_ZERO 0x2a
+#define TT_TRAP 0x80
+#else
+#define TT_TFAULT 0x08
+#define TT_TMISS 0x09
+#define TT_ILL_INSN 0x10
+#define TT_PRIV_INSN 0x11
+#define TT_NFPU_INSN 0x20
+#define TT_FP_EXCP 0x21
+#define TT_CLRWIN 0x24
+#define TT_DIV_ZERO 0x28
+#define TT_DFAULT 0x30
+#define TT_DMISS 0x31
+#define TT_DPROT 0x32
+#define TT_PRIV_ACT 0x37
+#define TT_EXTINT 0x40
+#define TT_SPILL 0x80
+#define TT_FILL 0xc0
+#define TT_WOTHER 0x10
+#define TT_TRAP 0x100
+#endif
+
+#define PSR_NEG (1<<23)
+#define PSR_ZERO (1<<22)
+#define PSR_OVF (1<<21)
+#define PSR_CARRY (1<<20)
+#define PSR_ICC (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY)
+#define PSR_EF (1<<12)
+#define PSR_PIL 0xf00
+#define PSR_S (1<<7)
+#define PSR_PS (1<<6)
+#define PSR_ET (1<<5)
+#define PSR_CWP 0x1f
+
+/* Trap base register */
+#define TBR_BASE_MASK 0xfffff000
+
+#if defined(TARGET_SPARC64)
+#define PS_IG (1<<11)
+#define PS_MG (1<<10)
+#define PS_RED (1<<5)
+#define PS_PEF (1<<4)
+#define PS_AM (1<<3)
+#define PS_PRIV (1<<2)
+#define PS_IE (1<<1)
+#define PS_AG (1<<0)
+
+#define FPRS_FEF (1<<2)
+#endif
+
+/* Fcc */
+#define FSR_RD1 (1<<31)
+#define FSR_RD0 (1<<30)
+#define FSR_RD_MASK (FSR_RD1 | FSR_RD0)
+#define FSR_RD_NEAREST 0
+#define FSR_RD_ZERO FSR_RD0
+#define FSR_RD_POS FSR_RD1
+#define FSR_RD_NEG (FSR_RD1 | FSR_RD0)
+
+#define FSR_NVM (1<<27)
+#define FSR_OFM (1<<26)
+#define FSR_UFM (1<<25)
+#define FSR_DZM (1<<24)
+#define FSR_NXM (1<<23)
+#define FSR_TEM_MASK (FSR_NVM | FSR_OFM | FSR_UFM | FSR_DZM | FSR_NXM)
+
+#define FSR_NVA (1<<9)
+#define FSR_OFA (1<<8)
+#define FSR_UFA (1<<7)
+#define FSR_DZA (1<<6)
+#define FSR_NXA (1<<5)
+#define FSR_AEXC_MASK (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
+
+#define FSR_NVC (1<<4)
+#define FSR_OFC (1<<3)
+#define FSR_UFC (1<<2)
+#define FSR_DZC (1<<1)
+#define FSR_NXC (1<<0)
+#define FSR_CEXC_MASK (FSR_NVC | FSR_OFC | FSR_UFC | FSR_DZC | FSR_NXC)
+
+#define FSR_FTT2 (1<<16)
+#define FSR_FTT1 (1<<15)
+#define FSR_FTT0 (1<<14)
+#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0)
+#define FSR_FTT_IEEE_EXCP (1 << 14)
+#define FSR_FTT_UNIMPFPOP (3 << 14)
+#define FSR_FTT_INVAL_FPR (6 << 14)
+
+#define FSR_FCC1 (1<<11)
+#define FSR_FCC0 (1<<10)
+
+/* MMU */
+#define MMU_E (1<<0)
+#define MMU_NF (1<<1)
+
+#define PTE_ENTRYTYPE_MASK 3
+#define PTE_ACCESS_MASK 0x1c
+#define PTE_ACCESS_SHIFT 2
+#define PTE_PPN_SHIFT 7
+#define PTE_ADDR_MASK 0xffffff00
+
+#define PG_ACCESSED_BIT 5
+#define PG_MODIFIED_BIT 6
+#define PG_CACHE_BIT 7
+
+#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
+#define PG_MODIFIED_MASK (1 << PG_MODIFIED_BIT)
+#define PG_CACHE_MASK (1 << PG_CACHE_BIT)
+
+/* 2 <= NWINDOWS <= 32. In QEMU it must also be a power of two. */
+#define NWINDOWS 8
+
+typedef struct CPUSPARCState {
+ target_ulong gregs[8]; /* general registers */
+ target_ulong *regwptr; /* pointer to current register window */
+ float32 fpr[TARGET_FPREGS]; /* floating point registers */
+ target_ulong pc; /* program counter */
+ target_ulong npc; /* next program counter */
+ target_ulong y; /* multiply/divide register */
+ uint32_t psr; /* processor state register */
+ target_ulong fsr; /* FPU state register */
+ uint32_t cwp; /* index of current register window (extracted
+ from PSR) */
+ uint32_t wim; /* window invalid mask */
+ target_ulong tbr; /* trap base register */
+ int psrs; /* supervisor mode (extracted from PSR) */
+ int psrps; /* previous supervisor mode */
+ int psret; /* enable traps */
+ uint32_t psrpil; /* interrupt level */
+ int psref; /* enable fpu */
+ jmp_buf jmp_env;
+ int user_mode_only;
+ int exception_index;
+ int interrupt_index;
+ int interrupt_request;
+ int halted;
+ /* NOTE: we allow 8 more registers to handle wrapping */
+ target_ulong regbase[NWINDOWS * 16 + 8];
+
+ CPU_COMMON
+
+ /* MMU regs */
+#if defined(TARGET_SPARC64)
+ uint64_t lsu;
+#define DMMU_E 0x8
+#define IMMU_E 0x4
+ uint64_t immuregs[16];
+ uint64_t dmmuregs[16];
+ uint64_t itlb_tag[64];
+ uint64_t itlb_tte[64];
+ uint64_t dtlb_tag[64];
+ uint64_t dtlb_tte[64];
+#else
+ uint32_t mmuregs[16];
+#endif
+ /* temporary float registers */
+ float32 ft0, ft1;
+ float64 dt0, dt1;
+ float_status fp_status;
+#if defined(TARGET_SPARC64)
+#define MAXTL 4
+ uint64_t t0, t1, t2;
+ uint64_t tpc[MAXTL];
+ uint64_t tnpc[MAXTL];
+ uint64_t tstate[MAXTL];
+ uint32_t tt[MAXTL];
+ uint32_t xcc; /* Extended integer condition codes */
+ uint32_t asi;
+ uint32_t pstate;
+ uint32_t tl;
+ uint32_t cansave, canrestore, otherwin, wstate, cleanwin;
+ uint64_t agregs[8]; /* alternate general registers */
+ uint64_t bgregs[8]; /* backup for normal global registers */
+ uint64_t igregs[8]; /* interrupt general registers */
+ uint64_t mgregs[8]; /* mmu general registers */
+ uint64_t version;
+ uint64_t fprs;
+ uint64_t tick_cmpr, stick_cmpr;
+ uint64_t gsr;
+#endif
+#if !defined(TARGET_SPARC64) && !defined(reg_T2)
+ target_ulong t2;
+#endif
+} CPUSPARCState;
+#if defined(TARGET_SPARC64)
+#define GET_FSR32(env) (env->fsr & 0xcfc1ffff)
+#define PUT_FSR32(env, val) do { uint32_t _tmp = val; \
+ env->fsr = (_tmp & 0xcfc1c3ff) | (env->fsr & 0x3f00000000ULL); \
+ } while (0)
+#define GET_FSR64(env) (env->fsr & 0x3fcfc1ffffULL)
+#define PUT_FSR64(env, val) do { uint64_t _tmp = val; \
+ env->fsr = _tmp & 0x3fcfc1c3ffULL; \
+ } while (0)
+// Manuf 0x17, version 0x11, mask 0 (UltraSparc-II)
+#define GET_VER(env) ((0x17ULL << 48) | (0x11ULL << 32) | \
+ (0 << 24) | (MAXTL << 8) | (NWINDOWS - 1))
+#else
+#define GET_FSR32(env) (env->fsr)
+#define PUT_FSR32(env, val) do { uint32_t _tmp = val; \
+ env->fsr = _tmp & 0xcfc1ffff; \
+ } while (0)
+#endif
+
+CPUSPARCState *cpu_sparc_init(void);
+int cpu_sparc_exec(CPUSPARCState *s);
+int cpu_sparc_close(CPUSPARCState *s);
+
+/* Fake impl 0, version 4 */
+#define GET_PSR(env) ((0 << 28) | (4 << 24) | (env->psr & PSR_ICC) | \
+ (env->psref? PSR_EF : 0) | \
+ (env->psrpil << 8) | \
+ (env->psrs? PSR_S : 0) | \
+ (env->psrps? PSR_PS : 0) | \
+ (env->psret? PSR_ET : 0) | env->cwp)
+
+#ifndef NO_CPU_IO_DEFS
+void cpu_set_cwp(CPUSPARCState *env1, int new_cwp);
+#endif
+
+#define PUT_PSR(env, val) do { int _tmp = val; \
+ env->psr = _tmp & PSR_ICC; \
+ env->psref = (_tmp & PSR_EF)? 1 : 0; \
+ env->psrpil = (_tmp & PSR_PIL) >> 8; \
+ env->psrs = (_tmp & PSR_S)? 1 : 0; \
+ env->psrps = (_tmp & PSR_PS)? 1 : 0; \
+ env->psret = (_tmp & PSR_ET)? 1 : 0; \
+ cpu_set_cwp(env, _tmp & PSR_CWP & (NWINDOWS - 1)); \
+ } while (0)
+
+#ifdef TARGET_SPARC64
+#define GET_CCR(env) ((env->xcc << 4) | (env->psr & PSR_ICC))
+#define PUT_CCR(env, val) do { int _tmp = val; \
+ env->xcc = _tmp >> 4; \
+ env->psr = (_tmp & 0xf) << 20; \
+ } while (0)
+#endif
+
+struct siginfo;
+int cpu_sparc_signal_handler(int hostsignum, struct siginfo *info, void *puc);
+
+#include "cpu-all.h"
+
+#endif
diff --git a/target-sparc/exec.h b/target-sparc/exec.h
new file mode 100644
index 0000000..1b67ef4
--- /dev/null
+++ b/target-sparc/exec.h
@@ -0,0 +1,104 @@
+#ifndef EXEC_SPARC_H
+#define EXEC_SPARC_H 1
+#include "dyngen-exec.h"
+#include "config.h"
+
+register struct CPUSPARCState *env asm(AREG0);
+#ifdef TARGET_SPARC64
+#define T0 (env->t0)
+#define T1 (env->t1)
+#define T2 (env->t2)
+#define REGWPTR env->regwptr
+#else
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+
+#undef REG_REGWPTR // Broken
+#ifdef REG_REGWPTR
+register uint32_t *REGWPTR asm(AREG3);
+#define reg_REGWPTR
+
+#ifdef AREG4
+register uint32_t T2 asm(AREG4);
+#define reg_T2
+#else
+#define T2 (env->t2)
+#endif
+
+#else
+#define REGWPTR env->regwptr
+register uint32_t T2 asm(AREG3);
+#define reg_T2
+#endif
+#endif
+
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define DT0 (env->dt0)
+#define DT1 (env->dt1)
+
+#include "cpu.h"
+#include "exec-all.h"
+
+void cpu_lock(void);
+void cpu_unlock(void);
+void cpu_loop_exit(void);
+void helper_flush(target_ulong addr);
+void helper_ld_asi(int asi, int size, int sign);
+void helper_st_asi(int asi, int size, int sign);
+void helper_rett(void);
+void helper_ldfsr(void);
+void set_cwp(int new_cwp);
+void do_fitos(void);
+void do_fitod(void);
+void do_fabss(void);
+void do_fsqrts(void);
+void do_fsqrtd(void);
+void do_fcmps(void);
+void do_fcmpd(void);
+#ifdef TARGET_SPARC64
+void do_fabsd(void);
+void do_fcmps_fcc1(void);
+void do_fcmpd_fcc1(void);
+void do_fcmps_fcc2(void);
+void do_fcmpd_fcc2(void);
+void do_fcmps_fcc3(void);
+void do_fcmpd_fcc3(void);
+void do_popc();
+void do_wrpstate();
+void do_done();
+void do_retry();
+#endif
+void do_ldd_kernel(target_ulong addr);
+void do_ldd_user(target_ulong addr);
+void do_ldd_raw(target_ulong addr);
+void do_interrupt(int intno);
+void raise_exception(int tt);
+void memcpy32(target_ulong *dst, const target_ulong *src);
+target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev);
+void dump_mmu(CPUState *env);
+void helper_debug();
+void do_wrpsr();
+void do_rdpsr();
+
+/* XXX: move that to a generic header */
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline void env_to_regs(void)
+{
+#if defined(reg_REGWPTR)
+ REGWPTR = env->regbase + (env->cwp * 16);
+ env->regwptr = REGWPTR;
+#endif
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu);
+
+#endif
diff --git a/target-sparc/fbranch_template.h b/target-sparc/fbranch_template.h
new file mode 100644
index 0000000..e6bf9a2
--- /dev/null
+++ b/target-sparc/fbranch_template.h
@@ -0,0 +1,89 @@
+/* FCC1:FCC0: 0 =, 1 <, 2 >, 3 u */
+
+void OPPROTO glue(op_eval_fbne, FCC)(void)
+{
+// !0
+ T2 = FFLAG_SET(FSR_FCC0) | FFLAG_SET(FSR_FCC1); /* L or G or U */
+}
+
+void OPPROTO glue(op_eval_fblg, FCC)(void)
+{
+// 1 or 2
+ T2 = FFLAG_SET(FSR_FCC0) ^ FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbul, FCC)(void)
+{
+// 1 or 3
+ T2 = FFLAG_SET(FSR_FCC0);
+}
+
+void OPPROTO glue(op_eval_fbl, FCC)(void)
+{
+// 1
+ T2 = FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbug, FCC)(void)
+{
+// 2 or 3
+ T2 = FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbg, FCC)(void)
+{
+// 2
+ T2 = !FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbu, FCC)(void)
+{
+// 3
+ T2 = FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbe, FCC)(void)
+{
+// 0
+ T2 = !FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbue, FCC)(void)
+{
+// 0 or 3
+ T2 = !(FFLAG_SET(FSR_FCC1) ^ FFLAG_SET(FSR_FCC0));
+ FORCE_RET();
+}
+
+void OPPROTO glue(op_eval_fbge, FCC)(void)
+{
+// 0 or 2
+ T2 = !FFLAG_SET(FSR_FCC0);
+}
+
+void OPPROTO glue(op_eval_fbuge, FCC)(void)
+{
+// !1
+ T2 = !(FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1));
+}
+
+void OPPROTO glue(op_eval_fble, FCC)(void)
+{
+// 0 or 1
+ T2 = !FFLAG_SET(FSR_FCC1);
+}
+
+void OPPROTO glue(op_eval_fbule, FCC)(void)
+{
+// !2
+ T2 = !(!FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1));
+}
+
+void OPPROTO glue(op_eval_fbo, FCC)(void)
+{
+// !3
+ T2 = !(FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1));
+}
+
+#undef FCC
+#undef FFLAG_SET
diff --git a/target-sparc/fop_template.h b/target-sparc/fop_template.h
new file mode 100644
index 0000000..74988f7
--- /dev/null
+++ b/target-sparc/fop_template.h
@@ -0,0 +1,81 @@
+/*
+ * SPARC micro operations (templates for various register related
+ * operations)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* floating point registers moves */
+void OPPROTO glue(op_load_fpr_FT0_fpr, REGNAME)(void)
+{
+ FT0 = REG;
+}
+
+void OPPROTO glue(op_store_FT0_fpr_fpr, REGNAME)(void)
+{
+ REG = FT0;
+}
+
+void OPPROTO glue(op_load_fpr_FT1_fpr, REGNAME)(void)
+{
+ FT1 = REG;
+}
+
+void OPPROTO glue(op_store_FT1_fpr_fpr, REGNAME)(void)
+{
+ REG = FT1;
+}
+
+/* double floating point registers moves */
+void OPPROTO glue(op_load_fpr_DT0_fpr, REGNAME)(void)
+{
+ CPU_DoubleU u;
+ uint32_t *p = (uint32_t *)&REG;
+ u.l.lower = *(p +1);
+ u.l.upper = *p;
+ DT0 = u.d;
+}
+
+void OPPROTO glue(op_store_DT0_fpr_fpr, REGNAME)(void)
+{
+ CPU_DoubleU u;
+ uint32_t *p = (uint32_t *)&REG;
+ u.d = DT0;
+ *(p +1) = u.l.lower;
+ *p = u.l.upper;
+}
+
+void OPPROTO glue(op_load_fpr_DT1_fpr, REGNAME)(void)
+{
+ CPU_DoubleU u;
+ uint32_t *p = (uint32_t *)&REG;
+ u.l.lower = *(p +1);
+ u.l.upper = *p;
+ DT1 = u.d;
+}
+
+void OPPROTO glue(op_store_DT1_fpr_fpr, REGNAME)(void)
+{
+ CPU_DoubleU u;
+ uint32_t *p = (uint32_t *)&REG;
+ u.d = DT1;
+ *(p +1) = u.l.lower;
+ *p = u.l.upper;
+}
+
+#undef REG
+#undef REGNAME
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
new file mode 100644
index 0000000..8f12667
--- /dev/null
+++ b/target-sparc/helper.c
@@ -0,0 +1,589 @@
+/*
+ * sparc helpers
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+//#define DEBUG_MMU
+
+/* Sparc MMU emulation */
+
+/* thread support */
+
+spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+
+void cpu_lock(void)
+{
+ spin_lock(&global_cpu_lock);
+}
+
+void cpu_unlock(void)
+{
+ spin_unlock(&global_cpu_lock);
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ if (rw & 2)
+ env->exception_index = TT_TFAULT;
+ else
+ env->exception_index = TT_DFAULT;
+ return 1;
+}
+
+#else
+
+#ifndef TARGET_SPARC64
+/*
+ * Sparc V8 Reference MMU (SRMMU)
+ */
+static const int access_table[8][8] = {
+ { 0, 0, 0, 0, 2, 0, 3, 3 },
+ { 0, 0, 0, 0, 2, 0, 0, 0 },
+ { 2, 2, 0, 0, 0, 2, 3, 3 },
+ { 2, 2, 0, 0, 0, 2, 0, 0 },
+ { 2, 0, 2, 0, 2, 2, 3, 3 },
+ { 2, 0, 2, 0, 2, 0, 2, 0 },
+ { 2, 2, 2, 0, 2, 2, 3, 3 },
+ { 2, 2, 2, 0, 2, 2, 2, 0 }
+};
+
+static const int perm_table[2][8] = {
+ {
+ PAGE_READ,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC,
+ PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC
+ },
+ {
+ PAGE_READ,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC,
+ PAGE_EXEC,
+ PAGE_READ,
+ 0,
+ 0,
+ }
+};
+
+int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+ int *access_index, target_ulong address, int rw,
+ int is_user)
+{
+ int access_perms = 0;
+ target_phys_addr_t pde_ptr;
+ uint32_t pde;
+ target_ulong virt_addr;
+ int error_code = 0, is_dirty;
+ unsigned long page_offset;
+
+ virt_addr = address & TARGET_PAGE_MASK;
+ if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return 0;
+ }
+
+ *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
+ *physical = 0xfffff000;
+
+ /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
+ /* Context base + context number */
+ pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+
+ /* Ctx pde */
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return 1 << 2;
+ case 2: /* L0 PTE, maybe should not happen? */
+ case 3: /* Reserved */
+ return 4 << 2;
+ case 1: /* L0 PDE */
+ pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (1 << 8) | (1 << 2);
+ case 3: /* Reserved */
+ return (1 << 8) | (4 << 2);
+ case 1: /* L1 PDE */
+ pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (2 << 8) | (1 << 2);
+ case 3: /* Reserved */
+ return (2 << 8) | (4 << 2);
+ case 1: /* L2 PDE */
+ pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (3 << 8) | (1 << 2);
+ case 1: /* PDE, should not happen */
+ case 3: /* Reserved */
+ return (3 << 8) | (4 << 2);
+ case 2: /* L3 PTE */
+ virt_addr = address & TARGET_PAGE_MASK;
+ page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1);
+ }
+ break;
+ case 2: /* L2 PTE */
+ virt_addr = address & ~0x3ffff;
+ page_offset = address & 0x3ffff;
+ }
+ break;
+ case 2: /* L1 PTE */
+ virt_addr = address & ~0xffffff;
+ page_offset = address & 0xffffff;
+ }
+ }
+
+ /* update page modified and dirty bits */
+ is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_MODIFIED_MASK;
+ stl_phys_notdirty(pde_ptr, pde);
+ }
+ /* check access */
+ access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
+ error_code = access_table[*access_index][access_perms];
+ if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user))
+ return error_code;
+
+ /* the page can be put in the TLB */
+ *prot = perm_table[is_user][access_perms];
+ if (!(pde & PG_MODIFIED_MASK)) {
+ /* only set write access if already dirty... otherwise wait
+ for dirty access */
+ *prot &= ~PAGE_WRITE;
+ }
+
+ /* Even if large ptes, we map only one 4KB page in the cache to
+ avoid filling it too fast */
+ *physical = ((pde & PTE_ADDR_MASK) << 4) + page_offset;
+ return error_code;
+}
+
+/* Perform address translation */
+int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ target_phys_addr_t paddr;
+ unsigned long vaddr;
+ int error_code = 0, prot, ret = 0, access_index;
+
+ error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
+ if (error_code == 0) {
+ vaddr = address & TARGET_PAGE_MASK;
+ paddr &= TARGET_PAGE_MASK;
+#ifdef DEBUG_MMU
+ printf("Translate at 0x%lx -> 0x%lx, vaddr 0x%lx\n", (long)address, (long)paddr, (long)vaddr);
+#endif
+ ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+ return ret;
+ }
+
+ if (env->mmuregs[3]) /* Fault status register */
+ env->mmuregs[3] = 1; /* overflow (not read before another fault) */
+ env->mmuregs[3] |= (access_index << 5) | error_code | 2;
+ env->mmuregs[4] = address; /* Fault address register */
+
+ if ((env->mmuregs[0] & MMU_NF) || env->psret == 0) {
+ // No fault mode: if a mapping is available, just override
+ // permissions. If no mapping is available, redirect accesses to
+ // neverland. Fake/overridden mappings will be flushed when
+ // switching to normal mode.
+ vaddr = address & TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+ return ret;
+ } else {
+ if (rw & 2)
+ env->exception_index = TT_TFAULT;
+ else
+ env->exception_index = TT_DFAULT;
+ return 1;
+ }
+}
+
+target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
+{
+ target_phys_addr_t pde_ptr;
+ uint32_t pde;
+
+ /* Context base + context number */
+ pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 2: /* PTE, maybe should not happen? */
+ case 3: /* Reserved */
+ return 0;
+ case 1: /* L1 PDE */
+ if (mmulev == 3)
+ return pde;
+ pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L1 PTE */
+ return pde;
+ case 1: /* L2 PDE */
+ if (mmulev == 2)
+ return pde;
+ pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L2 PTE */
+ return pde;
+ case 1: /* L3 PDE */
+ if (mmulev == 1)
+ return pde;
+ pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 1: /* PDE, should not happen */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L3 PTE */
+ return pde;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef DEBUG_MMU
+void dump_mmu(CPUState *env)
+{
+ target_ulong va, va1, va2;
+ unsigned int n, m, o;
+ target_phys_addr_t pde_ptr, pa;
+ uint32_t pde;
+
+ printf("MMU dump:\n");
+ pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+ printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]);
+ for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
+ pde_ptr = mmu_probe(env, va, 2);
+ if (pde_ptr) {
+ pa = cpu_get_phys_page_debug(env, va);
+ printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr);
+ for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
+ pde_ptr = mmu_probe(env, va1, 1);
+ if (pde_ptr) {
+ pa = cpu_get_phys_page_debug(env, va1);
+ printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr);
+ for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
+ pde_ptr = mmu_probe(env, va2, 0);
+ if (pde_ptr) {
+ pa = cpu_get_phys_page_debug(env, va2);
+ printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr);
+ }
+ }
+ }
+ }
+ }
+ }
+ printf("MMU dump ends\n");
+}
+#endif /* DEBUG_MMU */
+
+#else /* !TARGET_SPARC64 */
+/*
+ * UltraSparc IIi I/DMMUs
+ */
+static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot,
+ int *access_index, target_ulong address, int rw,
+ int is_user)
+{
+ target_ulong mask;
+ unsigned int i;
+
+ if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+ return 0;
+ }
+
+ for (i = 0; i < 64; i++) {
+ switch ((env->dtlb_tte[i] >> 61) & 3) {
+ default:
+ case 0x0: // 8k
+ mask = 0xffffffffffffe000ULL;
+ break;
+ case 0x1: // 64k
+ mask = 0xffffffffffff0000ULL;
+ break;
+ case 0x2: // 512k
+ mask = 0xfffffffffff80000ULL;
+ break;
+ case 0x3: // 4M
+ mask = 0xffffffffffc00000ULL;
+ break;
+ }
+ // ctx match, vaddr match?
+ if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) &&
+ (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) {
+ // valid, access ok?
+ if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 ||
+ ((env->dtlb_tte[i] & 0x4) && is_user) ||
+ (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) {
+ if (env->dmmuregs[3]) /* Fault status register */
+ env->dmmuregs[3] = 2; /* overflow (not read before another fault) */
+ env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1;
+ env->dmmuregs[4] = address; /* Fault address register */
+ env->exception_index = TT_DFAULT;
+#ifdef DEBUG_MMU
+ printf("DFAULT at 0x%" PRIx64 "\n", address);
+#endif
+ return 1;
+ }
+ *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
+ *prot = PAGE_READ;
+ if (env->dtlb_tte[i] & 0x2)
+ *prot |= PAGE_WRITE;
+ return 0;
+ }
+ }
+#ifdef DEBUG_MMU
+ printf("DMISS at 0x%" PRIx64 "\n", address);
+#endif
+ env->exception_index = TT_DMISS;
+ return 1;
+}
+
+static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot,
+ int *access_index, target_ulong address, int rw,
+ int is_user)
+{
+ target_ulong mask;
+ unsigned int i;
+
+ if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */
+ *physical = address;
+ *prot = PAGE_EXEC;
+ return 0;
+ }
+
+ for (i = 0; i < 64; i++) {
+ switch ((env->itlb_tte[i] >> 61) & 3) {
+ default:
+ case 0x0: // 8k
+ mask = 0xffffffffffffe000ULL;
+ break;
+ case 0x1: // 64k
+ mask = 0xffffffffffff0000ULL;
+ break;
+ case 0x2: // 512k
+ mask = 0xfffffffffff80000ULL;
+ break;
+ case 0x3: // 4M
+ mask = 0xffffffffffc00000ULL;
+ break;
+ }
+ // ctx match, vaddr match?
+ if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) &&
+ (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) {
+ // valid, access ok?
+ if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 ||
+ ((env->itlb_tte[i] & 0x4) && is_user)) {
+ if (env->immuregs[3]) /* Fault status register */
+ env->immuregs[3] = 2; /* overflow (not read before another fault) */
+ env->immuregs[3] |= (is_user << 3) | 1;
+ env->exception_index = TT_TFAULT;
+#ifdef DEBUG_MMU
+ printf("TFAULT at 0x%" PRIx64 "\n", address);
+#endif
+ return 1;
+ }
+ *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL);
+ *prot = PAGE_EXEC;
+ return 0;
+ }
+ }
+#ifdef DEBUG_MMU
+ printf("TMISS at 0x%" PRIx64 "\n", address);
+#endif
+ env->exception_index = TT_TMISS;
+ return 1;
+}
+
+int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot,
+ int *access_index, target_ulong address, int rw,
+ int is_user)
+{
+ if (rw == 2)
+ return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user);
+ else
+ return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user);
+}
+
+/* Perform address translation */
+int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int is_user, int is_softmmu)
+{
+ target_ulong virt_addr, vaddr;
+ target_phys_addr_t paddr;
+ int error_code = 0, prot, ret = 0, access_index;
+
+ error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user);
+ if (error_code == 0) {
+ virt_addr = address & TARGET_PAGE_MASK;
+ vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1));
+#ifdef DEBUG_MMU
+ printf("Translate at 0x%" PRIx64 " -> 0x%" PRIx64 ", vaddr 0x%" PRIx64 "\n", address, paddr, vaddr);
+#endif
+ ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
+ return ret;
+ }
+ // XXX
+ return 1;
+}
+
+#ifdef DEBUG_MMU
+void dump_mmu(CPUState *env)
+{
+ unsigned int i;
+ const char *mask;
+
+ printf("MMU contexts: Primary: %" PRId64 ", Secondary: %" PRId64 "\n", env->dmmuregs[1], env->dmmuregs[2]);
+ if ((env->lsu & DMMU_E) == 0) {
+ printf("DMMU disabled\n");
+ } else {
+ printf("DMMU dump:\n");
+ for (i = 0; i < 64; i++) {
+ switch ((env->dtlb_tte[i] >> 61) & 3) {
+ default:
+ case 0x0:
+ mask = " 8k";
+ break;
+ case 0x1:
+ mask = " 64k";
+ break;
+ case 0x2:
+ mask = "512k";
+ break;
+ case 0x3:
+ mask = " 4M";
+ break;
+ }
+ if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) {
+ printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, %s, ctx %" PRId64 "\n",
+ env->dtlb_tag[i] & ~0x1fffULL,
+ env->dtlb_tte[i] & 0x1ffffffe000ULL,
+ mask,
+ env->dtlb_tte[i] & 0x4? "priv": "user",
+ env->dtlb_tte[i] & 0x2? "RW": "RO",
+ env->dtlb_tte[i] & 0x40? "locked": "unlocked",
+ env->dtlb_tag[i] & 0x1fffULL);
+ }
+ }
+ }
+ if ((env->lsu & IMMU_E) == 0) {
+ printf("IMMU disabled\n");
+ } else {
+ printf("IMMU dump:\n");
+ for (i = 0; i < 64; i++) {
+ switch ((env->itlb_tte[i] >> 61) & 3) {
+ default:
+ case 0x0:
+ mask = " 8k";
+ break;
+ case 0x1:
+ mask = " 64k";
+ break;
+ case 0x2:
+ mask = "512k";
+ break;
+ case 0x3:
+ mask = " 4M";
+ break;
+ }
+ if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) {
+ printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, ctx %" PRId64 "\n",
+ env->itlb_tag[i] & ~0x1fffULL,
+ env->itlb_tte[i] & 0x1ffffffe000ULL,
+ mask,
+ env->itlb_tte[i] & 0x4? "priv": "user",
+ env->itlb_tte[i] & 0x40? "locked": "unlocked",
+ env->itlb_tag[i] & 0x1fffULL);
+ }
+ }
+ }
+}
+#endif /* DEBUG_MMU */
+
+#endif /* TARGET_SPARC64 */
+#endif /* !CONFIG_USER_ONLY */
+
+void memcpy32(target_ulong *dst, const target_ulong *src)
+{
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ dst[4] = src[4];
+ dst[5] = src[5];
+ dst[6] = src[6];
+ dst[7] = src[7];
+}
diff --git a/target-sparc/op.c b/target-sparc/op.c
new file mode 100644
index 0000000..7ea209e
--- /dev/null
+++ b/target-sparc/op.c
@@ -0,0 +1,1597 @@
+/*
+ SPARC micro operations
+
+ Copyright (C) 2003 Thomas M. Ogrisegg <tom@fnord.at>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "exec.h"
+
+ /*XXX*/
+#define REGNAME g0
+#define REG (env->gregs[0])
+#include "op_template.h"
+#define REGNAME g1
+#define REG (env->gregs[1])
+#include "op_template.h"
+#define REGNAME g2
+#define REG (env->gregs[2])
+#include "op_template.h"
+#define REGNAME g3
+#define REG (env->gregs[3])
+#include "op_template.h"
+#define REGNAME g4
+#define REG (env->gregs[4])
+#include "op_template.h"
+#define REGNAME g5
+#define REG (env->gregs[5])
+#include "op_template.h"
+#define REGNAME g6
+#define REG (env->gregs[6])
+#include "op_template.h"
+#define REGNAME g7
+#define REG (env->gregs[7])
+#include "op_template.h"
+#define REGNAME i0
+#define REG (REGWPTR[16])
+#include "op_template.h"
+#define REGNAME i1
+#define REG (REGWPTR[17])
+#include "op_template.h"
+#define REGNAME i2
+#define REG (REGWPTR[18])
+#include "op_template.h"
+#define REGNAME i3
+#define REG (REGWPTR[19])
+#include "op_template.h"
+#define REGNAME i4
+#define REG (REGWPTR[20])
+#include "op_template.h"
+#define REGNAME i5
+#define REG (REGWPTR[21])
+#include "op_template.h"
+#define REGNAME i6
+#define REG (REGWPTR[22])
+#include "op_template.h"
+#define REGNAME i7
+#define REG (REGWPTR[23])
+#include "op_template.h"
+#define REGNAME l0
+#define REG (REGWPTR[8])
+#include "op_template.h"
+#define REGNAME l1
+#define REG (REGWPTR[9])
+#include "op_template.h"
+#define REGNAME l2
+#define REG (REGWPTR[10])
+#include "op_template.h"
+#define REGNAME l3
+#define REG (REGWPTR[11])
+#include "op_template.h"
+#define REGNAME l4
+#define REG (REGWPTR[12])
+#include "op_template.h"
+#define REGNAME l5
+#define REG (REGWPTR[13])
+#include "op_template.h"
+#define REGNAME l6
+#define REG (REGWPTR[14])
+#include "op_template.h"
+#define REGNAME l7
+#define REG (REGWPTR[15])
+#include "op_template.h"
+#define REGNAME o0
+#define REG (REGWPTR[0])
+#include "op_template.h"
+#define REGNAME o1
+#define REG (REGWPTR[1])
+#include "op_template.h"
+#define REGNAME o2
+#define REG (REGWPTR[2])
+#include "op_template.h"
+#define REGNAME o3
+#define REG (REGWPTR[3])
+#include "op_template.h"
+#define REGNAME o4
+#define REG (REGWPTR[4])
+#include "op_template.h"
+#define REGNAME o5
+#define REG (REGWPTR[5])
+#include "op_template.h"
+#define REGNAME o6
+#define REG (REGWPTR[6])
+#include "op_template.h"
+#define REGNAME o7
+#define REG (REGWPTR[7])
+#include "op_template.h"
+
+#define REGNAME f0
+#define REG (env->fpr[0])
+#include "fop_template.h"
+#define REGNAME f1
+#define REG (env->fpr[1])
+#include "fop_template.h"
+#define REGNAME f2
+#define REG (env->fpr[2])
+#include "fop_template.h"
+#define REGNAME f3
+#define REG (env->fpr[3])
+#include "fop_template.h"
+#define REGNAME f4
+#define REG (env->fpr[4])
+#include "fop_template.h"
+#define REGNAME f5
+#define REG (env->fpr[5])
+#include "fop_template.h"
+#define REGNAME f6
+#define REG (env->fpr[6])
+#include "fop_template.h"
+#define REGNAME f7
+#define REG (env->fpr[7])
+#include "fop_template.h"
+#define REGNAME f8
+#define REG (env->fpr[8])
+#include "fop_template.h"
+#define REGNAME f9
+#define REG (env->fpr[9])
+#include "fop_template.h"
+#define REGNAME f10
+#define REG (env->fpr[10])
+#include "fop_template.h"
+#define REGNAME f11
+#define REG (env->fpr[11])
+#include "fop_template.h"
+#define REGNAME f12
+#define REG (env->fpr[12])
+#include "fop_template.h"
+#define REGNAME f13
+#define REG (env->fpr[13])
+#include "fop_template.h"
+#define REGNAME f14
+#define REG (env->fpr[14])
+#include "fop_template.h"
+#define REGNAME f15
+#define REG (env->fpr[15])
+#include "fop_template.h"
+#define REGNAME f16
+#define REG (env->fpr[16])
+#include "fop_template.h"
+#define REGNAME f17
+#define REG (env->fpr[17])
+#include "fop_template.h"
+#define REGNAME f18
+#define REG (env->fpr[18])
+#include "fop_template.h"
+#define REGNAME f19
+#define REG (env->fpr[19])
+#include "fop_template.h"
+#define REGNAME f20
+#define REG (env->fpr[20])
+#include "fop_template.h"
+#define REGNAME f21
+#define REG (env->fpr[21])
+#include "fop_template.h"
+#define REGNAME f22
+#define REG (env->fpr[22])
+#include "fop_template.h"
+#define REGNAME f23
+#define REG (env->fpr[23])
+#include "fop_template.h"
+#define REGNAME f24
+#define REG (env->fpr[24])
+#include "fop_template.h"
+#define REGNAME f25
+#define REG (env->fpr[25])
+#include "fop_template.h"
+#define REGNAME f26
+#define REG (env->fpr[26])
+#include "fop_template.h"
+#define REGNAME f27
+#define REG (env->fpr[27])
+#include "fop_template.h"
+#define REGNAME f28
+#define REG (env->fpr[28])
+#include "fop_template.h"
+#define REGNAME f29
+#define REG (env->fpr[29])
+#include "fop_template.h"
+#define REGNAME f30
+#define REG (env->fpr[30])
+#include "fop_template.h"
+#define REGNAME f31
+#define REG (env->fpr[31])
+#include "fop_template.h"
+
+#ifdef TARGET_SPARC64
+#define REGNAME f32
+#define REG (env->fpr[32])
+#include "fop_template.h"
+#define REGNAME f34
+#define REG (env->fpr[34])
+#include "fop_template.h"
+#define REGNAME f36
+#define REG (env->fpr[36])
+#include "fop_template.h"
+#define REGNAME f38
+#define REG (env->fpr[38])
+#include "fop_template.h"
+#define REGNAME f40
+#define REG (env->fpr[40])
+#include "fop_template.h"
+#define REGNAME f42
+#define REG (env->fpr[42])
+#include "fop_template.h"
+#define REGNAME f44
+#define REG (env->fpr[44])
+#include "fop_template.h"
+#define REGNAME f46
+#define REG (env->fpr[46])
+#include "fop_template.h"
+#define REGNAME f48
+#define REG (env->fpr[47])
+#include "fop_template.h"
+#define REGNAME f50
+#define REG (env->fpr[50])
+#include "fop_template.h"
+#define REGNAME f52
+#define REG (env->fpr[52])
+#include "fop_template.h"
+#define REGNAME f54
+#define REG (env->fpr[54])
+#include "fop_template.h"
+#define REGNAME f56
+#define REG (env->fpr[56])
+#include "fop_template.h"
+#define REGNAME f58
+#define REG (env->fpr[58])
+#include "fop_template.h"
+#define REGNAME f60
+#define REG (env->fpr[60])
+#include "fop_template.h"
+#define REGNAME f62
+#define REG (env->fpr[62])
+#include "fop_template.h"
+#endif
+
+#ifdef TARGET_SPARC64
+#ifdef WORDS_BIGENDIAN
+typedef union UREG64 {
+ struct { uint16_t v3, v2, v1, v0; } w;
+ struct { uint32_t v1, v0; } l;
+ uint64_t q;
+} UREG64;
+#else
+typedef union UREG64 {
+ struct { uint16_t v0, v1, v2, v3; } w;
+ struct { uint32_t v0, v1; } l;
+ uint64_t q;
+} UREG64;
+#endif
+
+#define PARAMQ1 \
+({\
+ UREG64 __p;\
+ __p.l.v1 = PARAM1;\
+ __p.l.v0 = PARAM2;\
+ __p.q;\
+})
+
+void OPPROTO op_movq_T0_im64(void)
+{
+ T0 = PARAMQ1;
+}
+
+void OPPROTO op_movq_T1_im64(void)
+{
+ T1 = PARAMQ1;
+}
+
+#define XFLAG_SET(x) ((env->xcc&x)?1:0)
+
+#else
+#define EIP (env->pc)
+#endif
+
+#define FLAG_SET(x) ((env->psr&x)?1:0)
+
+void OPPROTO op_movl_T0_0(void)
+{
+ T0 = 0;
+}
+
+void OPPROTO op_movl_T0_im(void)
+{
+ T0 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T1_im(void)
+{
+ T1 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T2_im(void)
+{
+ T2 = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T0_sim(void)
+{
+ T0 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T1_sim(void)
+{
+ T1 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T2_sim(void)
+{
+ T2 = (int32_t)PARAM1;
+}
+
+void OPPROTO op_movl_T0_env(void)
+{
+ T0 = *(uint32_t *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movl_env_T0(void)
+{
+ *(uint32_t *)((char *)env + PARAM1) = T0;
+}
+
+void OPPROTO op_movtl_T0_env(void)
+{
+ T0 = *(target_ulong *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movtl_env_T0(void)
+{
+ *(target_ulong *)((char *)env + PARAM1) = T0;
+}
+
+void OPPROTO op_add_T1_T0(void)
+{
+ T0 += T1;
+}
+
+void OPPROTO op_add_T1_T0_cc(void)
+{
+ target_ulong src1;
+
+ src1 = T0;
+ T0 += T1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if (!(T0 & 0xffffffff))
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if ((T0 & 0xffffffff) < (src1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff) ^ -1) &
+ ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31))
+ env->psr |= PSR_OVF;
+
+ env->xcc = 0;
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+ if (T0 < src1)
+ env->xcc |= PSR_CARRY;
+ if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1ULL << 63))
+ env->xcc |= PSR_OVF;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (T0 < src1)
+ env->psr |= PSR_CARRY;
+ if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1 << 31))
+ env->psr |= PSR_OVF;
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_addx_T1_T0(void)
+{
+ T0 += T1 + FLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_addx_T1_T0_cc(void)
+{
+ target_ulong src1;
+ src1 = T0;
+ if (FLAG_SET(PSR_CARRY))
+ {
+ T0 += T1 + 1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if ((T0 & 0xffffffff) <= (src1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ env->xcc = 0;
+ if (T0 <= src1)
+ env->xcc |= PSR_CARRY;
+#else
+ if (T0 <= src1)
+ env->psr |= PSR_CARRY;
+#endif
+ }
+ else
+ {
+ T0 += T1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if ((T0 & 0xffffffff) < (src1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ env->xcc = 0;
+ if (T0 < src1)
+ env->xcc |= PSR_CARRY;
+#else
+ if (T0 < src1)
+ env->psr |= PSR_CARRY;
+#endif
+ }
+#ifdef TARGET_SPARC64
+ if (!(T0 & 0xffffffff))
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff) ^ -1) &
+ ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31))
+ env->psr |= PSR_OVF;
+
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+ if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1ULL << 63))
+ env->xcc |= PSR_OVF;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1 << 31))
+ env->psr |= PSR_OVF;
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_sub_T1_T0(void)
+{
+ T0 -= T1;
+}
+
+void OPPROTO op_sub_T1_T0_cc(void)
+{
+ target_ulong src1;
+
+ src1 = T0;
+ T0 -= T1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if (!(T0 & 0xffffffff))
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if ((src1 & 0xffffffff) < (T1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff)) &
+ ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31))
+ env->psr |= PSR_OVF;
+
+ env->xcc = 0;
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+ if (src1 < T1)
+ env->xcc |= PSR_CARRY;
+ if (((src1 ^ T1) & (src1 ^ T0)) & (1ULL << 63))
+ env->xcc |= PSR_OVF;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (src1 < T1)
+ env->psr |= PSR_CARRY;
+ if (((src1 ^ T1) & (src1 ^ T0)) & (1 << 31))
+ env->psr |= PSR_OVF;
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_subx_T1_T0(void)
+{
+ T0 -= T1 + FLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_subx_T1_T0_cc(void)
+{
+ target_ulong src1;
+ src1 = T0;
+ if (FLAG_SET(PSR_CARRY))
+ {
+ T0 -= T1 + 1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if ((src1 & 0xffffffff) <= (T1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ env->xcc = 0;
+ if (src1 <= T1)
+ env->xcc |= PSR_CARRY;
+#else
+ if (src1 <= T1)
+ env->psr |= PSR_CARRY;
+#endif
+ }
+ else
+ {
+ T0 -= T1;
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if ((src1 & 0xffffffff) < (T1 & 0xffffffff))
+ env->psr |= PSR_CARRY;
+ env->xcc = 0;
+ if (src1 < T1)
+ env->xcc |= PSR_CARRY;
+#else
+ if (src1 < T1)
+ env->psr |= PSR_CARRY;
+#endif
+ }
+#ifdef TARGET_SPARC64
+ if (!(T0 & 0xffffffff))
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff)) &
+ ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31))
+ env->psr |= PSR_OVF;
+
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+ if (((src1 ^ T1) & (src1 ^ T0)) & (1ULL << 63))
+ env->xcc |= PSR_OVF;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (((src1 ^ T1) & (src1 ^ T0)) & (1 << 31))
+ env->psr |= PSR_OVF;
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_and_T1_T0(void)
+{
+ T0 &= T1;
+}
+
+void OPPROTO op_or_T1_T0(void)
+{
+ T0 |= T1;
+}
+
+void OPPROTO op_xor_T1_T0(void)
+{
+ T0 ^= T1;
+}
+
+void OPPROTO op_andn_T1_T0(void)
+{
+ T0 &= ~T1;
+}
+
+void OPPROTO op_orn_T1_T0(void)
+{
+ T0 |= ~T1;
+}
+
+void OPPROTO op_xnor_T1_T0(void)
+{
+ T0 ^= ~T1;
+}
+
+void OPPROTO op_umul_T1_T0(void)
+{
+ uint64_t res;
+ res = (uint64_t) T0 * (uint64_t) T1;
+#ifdef TARGET_SPARC64
+ T0 = res;
+#else
+ T0 = res & 0xffffffff;
+#endif
+ env->y = res >> 32;
+}
+
+void OPPROTO op_smul_T1_T0(void)
+{
+ uint64_t res;
+ res = (int64_t) ((int32_t) T0) * (int64_t) ((int32_t) T1);
+#ifdef TARGET_SPARC64
+ T0 = res;
+#else
+ T0 = res & 0xffffffff;
+#endif
+ env->y = res >> 32;
+}
+
+void OPPROTO op_mulscc_T1_T0(void)
+{
+ unsigned int b1, N, V, b2;
+ target_ulong src1;
+
+ N = FLAG_SET(PSR_NEG);
+ V = FLAG_SET(PSR_OVF);
+ b1 = N ^ V;
+ b2 = T0 & 1;
+ T0 = (b1 << 31) | (T0 >> 1);
+ if (!(env->y & 1))
+ T1 = 0;
+ /* do addition and update flags */
+ src1 = T0;
+ T0 += T1;
+ env->psr = 0;
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (T0 < src1)
+ env->psr |= PSR_CARRY;
+ if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1 << 31))
+ env->psr |= PSR_OVF;
+ env->y = (b2 << 31) | (env->y >> 1);
+ FORCE_RET();
+}
+
+void OPPROTO op_udiv_T1_T0(void)
+{
+ uint64_t x0;
+ uint32_t x1;
+
+ x0 = T0 | ((uint64_t) (env->y) << 32);
+ x1 = T1;
+ x0 = x0 / x1;
+ if (x0 > 0xffffffff) {
+ T0 = 0xffffffff;
+ T1 = 1;
+ } else {
+ T0 = x0;
+ T1 = 0;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_sdiv_T1_T0(void)
+{
+ int64_t x0;
+ int32_t x1;
+
+ x0 = T0 | ((int64_t) (env->y) << 32);
+ x1 = T1;
+ x0 = x0 / x1;
+ if ((int32_t) x0 != x0) {
+ T0 = x0 < 0? 0x80000000: 0x7fffffff;
+ T1 = 1;
+ } else {
+ T0 = x0;
+ T1 = 0;
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_div_cc(void)
+{
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (T1)
+ env->psr |= PSR_OVF;
+
+ env->xcc = 0;
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+ if (T1)
+ env->psr |= PSR_OVF;
+#endif
+ FORCE_RET();
+}
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_mulx_T1_T0(void)
+{
+ T0 *= T1;
+ FORCE_RET();
+}
+
+void OPPROTO op_udivx_T1_T0(void)
+{
+ T0 /= T1;
+ FORCE_RET();
+}
+
+void OPPROTO op_sdivx_T1_T0(void)
+{
+ if (T0 == INT64_MIN && T1 == -1)
+ T0 = INT64_MIN;
+ else
+ T0 /= (target_long) T1;
+ FORCE_RET();
+}
+#endif
+
+void OPPROTO op_logic_T0_cc(void)
+{
+ env->psr = 0;
+#ifdef TARGET_SPARC64
+ if (!(T0 & 0xffffffff))
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+
+ env->xcc = 0;
+ if (!T0)
+ env->xcc |= PSR_ZERO;
+ if ((int64_t) T0 < 0)
+ env->xcc |= PSR_NEG;
+#else
+ if (!T0)
+ env->psr |= PSR_ZERO;
+ if ((int32_t) T0 < 0)
+ env->psr |= PSR_NEG;
+#endif
+ FORCE_RET();
+}
+
+void OPPROTO op_sll(void)
+{
+ T0 <<= T1;
+}
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_srl(void)
+{
+ T0 = (T0 & 0xffffffff) >> T1;
+}
+
+void OPPROTO op_srlx(void)
+{
+ T0 >>= T1;
+}
+
+void OPPROTO op_sra(void)
+{
+ T0 = ((int32_t) (T0 & 0xffffffff)) >> T1;
+}
+
+void OPPROTO op_srax(void)
+{
+ T0 = ((int64_t) T0) >> T1;
+}
+#else
+void OPPROTO op_srl(void)
+{
+ T0 >>= T1;
+}
+
+void OPPROTO op_sra(void)
+{
+ T0 = ((int32_t) T0) >> T1;
+}
+#endif
+
+/* Load and store */
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
+
+void OPPROTO op_ldfsr(void)
+{
+ PUT_FSR32(env, *((uint32_t *) &FT0));
+ helper_ldfsr();
+}
+
+void OPPROTO op_stfsr(void)
+{
+ *((uint32_t *) &FT0) = GET_FSR32(env);
+}
+
+#ifndef TARGET_SPARC64
+void OPPROTO op_rdpsr(void)
+{
+ do_rdpsr();
+}
+
+void OPPROTO op_wrpsr(void)
+{
+ do_wrpsr();
+ FORCE_RET();
+}
+
+void OPPROTO op_rett(void)
+{
+ helper_rett();
+ FORCE_RET();
+}
+
+/* XXX: use another pointer for %iN registers to avoid slow wrapping
+ handling ? */
+void OPPROTO op_save(void)
+{
+ uint32_t cwp;
+ cwp = (env->cwp - 1) & (NWINDOWS - 1);
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_OVF);
+ }
+ set_cwp(cwp);
+ FORCE_RET();
+}
+
+void OPPROTO op_restore(void)
+{
+ uint32_t cwp;
+ cwp = (env->cwp + 1) & (NWINDOWS - 1);
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_UNF);
+ }
+ set_cwp(cwp);
+ FORCE_RET();
+}
+#else
+void OPPROTO op_rdccr(void)
+{
+ T0 = GET_CCR(env);
+}
+
+void OPPROTO op_wrccr(void)
+{
+ PUT_CCR(env, T0);
+}
+
+void OPPROTO op_rdtick(void)
+{
+ T0 = 0; // XXX read cycle counter and bit 31
+}
+
+void OPPROTO op_wrtick(void)
+{
+ // XXX write cycle counter and bit 31
+}
+
+void OPPROTO op_rdtpc(void)
+{
+ T0 = env->tpc[env->tl];
+}
+
+void OPPROTO op_wrtpc(void)
+{
+ env->tpc[env->tl] = T0;
+}
+
+void OPPROTO op_rdtnpc(void)
+{
+ T0 = env->tnpc[env->tl];
+}
+
+void OPPROTO op_wrtnpc(void)
+{
+ env->tnpc[env->tl] = T0;
+}
+
+void OPPROTO op_rdtstate(void)
+{
+ T0 = env->tstate[env->tl];
+}
+
+void OPPROTO op_wrtstate(void)
+{
+ env->tstate[env->tl] = T0;
+}
+
+void OPPROTO op_rdtt(void)
+{
+ T0 = env->tt[env->tl];
+}
+
+void OPPROTO op_wrtt(void)
+{
+ env->tt[env->tl] = T0;
+}
+
+void OPPROTO op_rdpstate(void)
+{
+ T0 = env->pstate;
+}
+
+void OPPROTO op_wrpstate(void)
+{
+ do_wrpstate();
+}
+
+// CWP handling is reversed in V9, but we still use the V8 register
+// order.
+void OPPROTO op_rdcwp(void)
+{
+ T0 = NWINDOWS - 1 - env->cwp;
+}
+
+void OPPROTO op_wrcwp(void)
+{
+ env->cwp = NWINDOWS - 1 - T0;
+}
+
+/* XXX: use another pointer for %iN registers to avoid slow wrapping
+ handling ? */
+void OPPROTO op_save(void)
+{
+ uint32_t cwp;
+ cwp = (env->cwp - 1) & (NWINDOWS - 1);
+ if (env->cansave == 0) {
+ raise_exception(TT_SPILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ } else {
+ if (env->cleanwin - env->canrestore == 0) {
+ // XXX Clean windows without trap
+ raise_exception(TT_CLRWIN);
+ } else {
+ env->cansave--;
+ env->canrestore++;
+ set_cwp(cwp);
+ }
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_restore(void)
+{
+ uint32_t cwp;
+ cwp = (env->cwp + 1) & (NWINDOWS - 1);
+ if (env->canrestore == 0) {
+ raise_exception(TT_FILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ } else {
+ env->cansave++;
+ env->canrestore--;
+ set_cwp(cwp);
+ }
+ FORCE_RET();
+}
+#endif
+
+void OPPROTO op_exception(void)
+{
+ env->exception_index = PARAM1;
+ cpu_loop_exit();
+}
+
+void OPPROTO op_trap_T0(void)
+{
+ env->exception_index = TT_TRAP + (T0 & 0x7f);
+ cpu_loop_exit();
+}
+
+void OPPROTO op_trapcc_T0(void)
+{
+ if (T2) {
+ env->exception_index = TT_TRAP + (T0 & 0x7f);
+ cpu_loop_exit();
+ }
+ FORCE_RET();
+}
+
+void OPPROTO op_fpexception_im(void)
+{
+ env->exception_index = TT_FP_EXCP;
+ env->fsr &= ~FSR_FTT_MASK;
+ env->fsr |= PARAM1;
+ cpu_loop_exit();
+ FORCE_RET();
+}
+
+void OPPROTO op_debug(void)
+{
+ helper_debug();
+}
+
+void OPPROTO op_exit_tb(void)
+{
+ EXIT_TB();
+}
+
+void OPPROTO op_eval_ba(void)
+{
+ T2 = 1;
+}
+
+void OPPROTO op_eval_be(void)
+{
+ T2 = FLAG_SET(PSR_ZERO);
+}
+
+void OPPROTO op_eval_ble(void)
+{
+ target_ulong Z = FLAG_SET(PSR_ZERO), N = FLAG_SET(PSR_NEG), V = FLAG_SET(PSR_OVF);
+
+ T2 = Z | (N ^ V);
+}
+
+void OPPROTO op_eval_bl(void)
+{
+ target_ulong N = FLAG_SET(PSR_NEG), V = FLAG_SET(PSR_OVF);
+
+ T2 = N ^ V;
+}
+
+void OPPROTO op_eval_bleu(void)
+{
+ target_ulong Z = FLAG_SET(PSR_ZERO), C = FLAG_SET(PSR_CARRY);
+
+ T2 = C | Z;
+}
+
+void OPPROTO op_eval_bcs(void)
+{
+ T2 = FLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_eval_bvs(void)
+{
+ T2 = FLAG_SET(PSR_OVF);
+}
+
+void OPPROTO op_eval_bn(void)
+{
+ T2 = 0;
+}
+
+void OPPROTO op_eval_bneg(void)
+{
+ T2 = FLAG_SET(PSR_NEG);
+}
+
+void OPPROTO op_eval_bne(void)
+{
+ T2 = !FLAG_SET(PSR_ZERO);
+}
+
+void OPPROTO op_eval_bg(void)
+{
+ target_ulong Z = FLAG_SET(PSR_ZERO), N = FLAG_SET(PSR_NEG), V = FLAG_SET(PSR_OVF);
+
+ T2 = !(Z | (N ^ V));
+}
+
+void OPPROTO op_eval_bge(void)
+{
+ target_ulong N = FLAG_SET(PSR_NEG), V = FLAG_SET(PSR_OVF);
+
+ T2 = !(N ^ V);
+}
+
+void OPPROTO op_eval_bgu(void)
+{
+ target_ulong Z = FLAG_SET(PSR_ZERO), C = FLAG_SET(PSR_CARRY);
+
+ T2 = !(C | Z);
+}
+
+void OPPROTO op_eval_bcc(void)
+{
+ T2 = !FLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_eval_bpos(void)
+{
+ T2 = !FLAG_SET(PSR_NEG);
+}
+
+void OPPROTO op_eval_bvc(void)
+{
+ T2 = !FLAG_SET(PSR_OVF);
+}
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_eval_xbe(void)
+{
+ T2 = XFLAG_SET(PSR_ZERO);
+}
+
+void OPPROTO op_eval_xble(void)
+{
+ target_ulong Z = XFLAG_SET(PSR_ZERO), N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF);
+
+ T2 = Z | (N ^ V);
+}
+
+void OPPROTO op_eval_xbl(void)
+{
+ target_ulong N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF);
+
+ T2 = N ^ V;
+}
+
+void OPPROTO op_eval_xbleu(void)
+{
+ target_ulong Z = XFLAG_SET(PSR_ZERO), C = XFLAG_SET(PSR_CARRY);
+
+ T2 = C | Z;
+}
+
+void OPPROTO op_eval_xbcs(void)
+{
+ T2 = XFLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_eval_xbvs(void)
+{
+ T2 = XFLAG_SET(PSR_OVF);
+}
+
+void OPPROTO op_eval_xbneg(void)
+{
+ T2 = XFLAG_SET(PSR_NEG);
+}
+
+void OPPROTO op_eval_xbne(void)
+{
+ T2 = !XFLAG_SET(PSR_ZERO);
+}
+
+void OPPROTO op_eval_xbg(void)
+{
+ target_ulong Z = XFLAG_SET(PSR_ZERO), N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF);
+
+ T2 = !(Z | (N ^ V));
+}
+
+void OPPROTO op_eval_xbge(void)
+{
+ target_ulong N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF);
+
+ T2 = !(N ^ V);
+}
+
+void OPPROTO op_eval_xbgu(void)
+{
+ target_ulong Z = XFLAG_SET(PSR_ZERO), C = XFLAG_SET(PSR_CARRY);
+
+ T2 = !(C | Z);
+}
+
+void OPPROTO op_eval_xbcc(void)
+{
+ T2 = !XFLAG_SET(PSR_CARRY);
+}
+
+void OPPROTO op_eval_xbpos(void)
+{
+ T2 = !XFLAG_SET(PSR_NEG);
+}
+
+void OPPROTO op_eval_xbvc(void)
+{
+ T2 = !XFLAG_SET(PSR_OVF);
+}
+#endif
+
+#define FCC
+#define FFLAG_SET(x) (env->fsr & x? 1: 0)
+#include "fbranch_template.h"
+
+#ifdef TARGET_SPARC64
+#define FCC _fcc1
+#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 32))? 1: 0)
+#include "fbranch_template.h"
+#define FCC _fcc2
+#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 34))? 1: 0)
+#include "fbranch_template.h"
+#define FCC _fcc3
+#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 36))? 1: 0)
+#include "fbranch_template.h"
+#endif
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_eval_brz(void)
+{
+ T2 = (T0 == 0);
+}
+
+void OPPROTO op_eval_brnz(void)
+{
+ T2 = (T0 != 0);
+}
+
+void OPPROTO op_eval_brlz(void)
+{
+ T2 = ((int64_t)T0 < 0);
+}
+
+void OPPROTO op_eval_brlez(void)
+{
+ T2 = ((int64_t)T0 <= 0);
+}
+
+void OPPROTO op_eval_brgz(void)
+{
+ T2 = ((int64_t)T0 > 0);
+}
+
+void OPPROTO op_eval_brgez(void)
+{
+ T2 = ((int64_t)T0 >= 0);
+}
+
+void OPPROTO op_jmp_im64(void)
+{
+ env->pc = PARAMQ1;
+}
+
+void OPPROTO op_movq_npc_im64(void)
+{
+ env->npc = PARAMQ1;
+}
+#endif
+
+void OPPROTO op_jmp_im(void)
+{
+ env->pc = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_npc_im(void)
+{
+ env->npc = (uint32_t)PARAM1;
+}
+
+void OPPROTO op_movl_npc_T0(void)
+{
+ env->npc = T0;
+}
+
+void OPPROTO op_mov_pc_npc(void)
+{
+ env->pc = env->npc;
+}
+
+void OPPROTO op_next_insn(void)
+{
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+}
+
+void OPPROTO op_goto_tb0(void)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+void OPPROTO op_goto_tb1(void)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+void OPPROTO op_jmp_label(void)
+{
+ GOTO_LABEL_PARAM(1);
+}
+
+void OPPROTO op_jnz_T2_label(void)
+{
+ if (T2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_jz_T2_label(void)
+{
+ if (!T2)
+ GOTO_LABEL_PARAM(1);
+ FORCE_RET();
+}
+
+void OPPROTO op_flush_T0(void)
+{
+ helper_flush(T0);
+}
+
+#define F_OP(name, p) void OPPROTO op_f##name##p(void)
+
+#define F_BINOP(name) \
+ F_OP(name, s) \
+ { \
+ FT0 = float32_ ## name (FT0, FT1, &env->fp_status); \
+ } \
+ F_OP(name, d) \
+ { \
+ DT0 = float64_ ## name (DT0, DT1, &env->fp_status); \
+ }
+
+F_BINOP(add);
+F_BINOP(sub);
+F_BINOP(mul);
+F_BINOP(div);
+#undef F_BINOP
+
+void OPPROTO op_fsmuld(void)
+{
+ DT0 = float64_mul(float32_to_float64(FT0, &env->fp_status),
+ float32_to_float64(FT1, &env->fp_status),
+ &env->fp_status);
+}
+
+#define F_HELPER(name) \
+ F_OP(name, s) \
+ { \
+ do_f##name##s(); \
+ } \
+ F_OP(name, d) \
+ { \
+ do_f##name##d(); \
+ }
+
+F_HELPER(sqrt);
+
+F_OP(neg, s)
+{
+ FT0 = float32_chs(FT1);
+}
+
+F_OP(abs, s)
+{
+ do_fabss();
+}
+
+F_HELPER(cmp);
+
+#ifdef TARGET_SPARC64
+F_OP(neg, d)
+{
+ DT0 = float64_chs(DT1);
+}
+
+F_OP(abs, d)
+{
+ do_fabsd();
+}
+
+void OPPROTO op_fcmps_fcc1(void)
+{
+ do_fcmps_fcc1();
+}
+
+void OPPROTO op_fcmpd_fcc1(void)
+{
+ do_fcmpd_fcc1();
+}
+
+void OPPROTO op_fcmps_fcc2(void)
+{
+ do_fcmps_fcc2();
+}
+
+void OPPROTO op_fcmpd_fcc2(void)
+{
+ do_fcmpd_fcc2();
+}
+
+void OPPROTO op_fcmps_fcc3(void)
+{
+ do_fcmps_fcc3();
+}
+
+void OPPROTO op_fcmpd_fcc3(void)
+{
+ do_fcmpd_fcc3();
+}
+#endif
+
+/* Integer to float conversion. */
+#ifdef USE_INT_TO_FLOAT_HELPERS
+F_HELPER(ito);
+#else
+F_OP(ito, s)
+{
+ FT0 = int32_to_float32(*((int32_t *)&FT1), &env->fp_status);
+}
+
+F_OP(ito, d)
+{
+ DT0 = int32_to_float64(*((int32_t *)&FT1), &env->fp_status);
+}
+
+#ifdef TARGET_SPARC64
+F_OP(xto, s)
+{
+ FT0 = int64_to_float32(*((int64_t *)&DT1), &env->fp_status);
+}
+
+F_OP(xto, d)
+{
+ DT0 = int64_to_float64(*((int64_t *)&DT1), &env->fp_status);
+}
+#endif
+#endif
+#undef F_HELPER
+
+/* floating point conversion */
+void OPPROTO op_fdtos(void)
+{
+ FT0 = float64_to_float32(DT1, &env->fp_status);
+}
+
+void OPPROTO op_fstod(void)
+{
+ DT0 = float32_to_float64(FT1, &env->fp_status);
+}
+
+/* Float to integer conversion. */
+void OPPROTO op_fstoi(void)
+{
+ *((int32_t *)&FT0) = float32_to_int32(FT1, &env->fp_status);
+}
+
+void OPPROTO op_fdtoi(void)
+{
+ *((int32_t *)&FT0) = float64_to_int32(DT1, &env->fp_status);
+}
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_fstox(void)
+{
+ *((int64_t *)&DT0) = float32_to_int64(FT1, &env->fp_status);
+}
+
+void OPPROTO op_fdtox(void)
+{
+ *((int64_t *)&DT0) = float64_to_int64(DT1, &env->fp_status);
+}
+
+void OPPROTO op_fmovs_cc(void)
+{
+ if (T2)
+ FT0 = FT1;
+}
+
+void OPPROTO op_fmovd_cc(void)
+{
+ if (T2)
+ DT0 = DT1;
+}
+
+void OPPROTO op_mov_cc(void)
+{
+ if (T2)
+ T0 = T1;
+}
+
+void OPPROTO op_flushw(void)
+{
+ if (env->cansave != NWINDOWS - 2) {
+ raise_exception(TT_SPILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ }
+}
+
+void OPPROTO op_saved(void)
+{
+ env->cansave++;
+ if (env->otherwin == 0)
+ env->canrestore--;
+ else
+ env->otherwin--;
+ FORCE_RET();
+}
+
+void OPPROTO op_restored(void)
+{
+ env->canrestore++;
+ if (env->cleanwin < NWINDOWS - 1)
+ env->cleanwin++;
+ if (env->otherwin == 0)
+ env->cansave--;
+ else
+ env->otherwin--;
+ FORCE_RET();
+}
+
+void OPPROTO op_popc(void)
+{
+ do_popc();
+}
+
+void OPPROTO op_done(void)
+{
+ do_done();
+}
+
+void OPPROTO op_retry(void)
+{
+ do_retry();
+}
+
+void OPPROTO op_sir(void)
+{
+ // XXX
+
+}
+
+void OPPROTO op_ld_asi_reg()
+{
+ T0 += PARAM1;
+ helper_ld_asi(env->asi, PARAM2, PARAM3);
+}
+
+void OPPROTO op_st_asi_reg()
+{
+ T0 += PARAM1;
+ helper_st_asi(env->asi, PARAM2, PARAM3);
+}
+#endif
+
+void OPPROTO op_ld_asi()
+{
+ helper_ld_asi(PARAM1, PARAM2, PARAM3);
+}
+
+void OPPROTO op_st_asi()
+{
+ helper_st_asi(PARAM1, PARAM2, PARAM3);
+}
+
+#ifdef TARGET_SPARC64
+void OPPROTO op_alignaddr()
+{
+ uint64_t tmp;
+
+ tmp = T0 + T1;
+ env->gsr &= ~7ULL;
+ env->gsr |= tmp & 7ULL;
+ T0 = tmp & ~7ULL;
+}
+
+void OPPROTO op_faligndata()
+{
+ uint64_t tmp;
+
+ tmp = (*((uint64_t *)&DT0)) << ((env->gsr & 7) * 8);
+ tmp |= (*((uint64_t *)&DT1)) >> (64 - (env->gsr & 7) * 8);
+ (*((uint64_t *)&DT0)) = tmp;
+}
+#endif
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
new file mode 100644
index 0000000..f4f725d
--- /dev/null
+++ b/target-sparc/op_helper.c
@@ -0,0 +1,918 @@
+#include "exec.h"
+
+//#define DEBUG_PCALL
+//#define DEBUG_MMU
+
+void raise_exception(int tt)
+{
+ env->exception_index = tt;
+ cpu_loop_exit();
+}
+
+#ifdef USE_INT_TO_FLOAT_HELPERS
+void do_fitos(void)
+{
+ FT0 = int32_to_float32(*((int32_t *)&FT1));
+}
+
+void do_fitod(void)
+{
+ DT0 = int32_to_float64(*((int32_t *)&FT1));
+}
+#endif
+
+void do_fabss(void)
+{
+ FT0 = float32_abs(FT1);
+}
+
+#ifdef TARGET_SPARC64
+void do_fabsd(void)
+{
+ DT0 = float64_abs(DT1);
+}
+#endif
+
+void do_fsqrts(void)
+{
+ FT0 = float32_sqrt(FT1, &env->fp_status);
+}
+
+void do_fsqrtd(void)
+{
+ DT0 = float64_sqrt(DT1, &env->fp_status);
+}
+
+#define GEN_FCMP(name, size, reg1, reg2, FS) \
+ void glue(do_, name) (void) \
+ { \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \
+ case float_relation_unordered: \
+ T0 = (FSR_FCC1 | FSR_FCC0) << FS; \
+ if (env->fsr & FSR_NVM) { \
+ env->fsr |= T0; \
+ raise_exception(TT_FP_EXCP); \
+ } else { \
+ env->fsr |= FSR_NVA; \
+ } \
+ break; \
+ case float_relation_less: \
+ T0 = FSR_FCC0 << FS; \
+ break; \
+ case float_relation_greater: \
+ T0 = FSR_FCC1 << FS; \
+ break; \
+ default: \
+ T0 = 0; \
+ break; \
+ } \
+ env->fsr |= T0; \
+ }
+
+GEN_FCMP(fcmps, float32, FT0, FT1, 0);
+GEN_FCMP(fcmpd, float64, DT0, DT1, 0);
+
+#ifdef TARGET_SPARC64
+GEN_FCMP(fcmps_fcc1, float32, FT0, FT1, 22);
+GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22);
+
+GEN_FCMP(fcmps_fcc2, float32, FT0, FT1, 24);
+GEN_FCMP(fcmpd_fcc2, float64, DT0, DT1, 24);
+
+GEN_FCMP(fcmps_fcc3, float32, FT0, FT1, 26);
+GEN_FCMP(fcmpd_fcc3, float64, DT0, DT1, 26);
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+void helper_ld_asi(int asi, int size, int sign)
+{
+}
+
+void helper_st_asi(int asi, int size, int sign)
+{
+}
+#else
+#ifndef TARGET_SPARC64
+void helper_ld_asi(int asi, int size, int sign)
+{
+ uint32_t ret = 0;
+
+ switch (asi) {
+ case 3: /* MMU probe */
+ {
+ int mmulev;
+
+ mmulev = (T0 >> 8) & 15;
+ if (mmulev > 4)
+ ret = 0;
+ else {
+ ret = mmu_probe(env, T0, mmulev);
+ //bswap32s(&ret);
+ }
+#ifdef DEBUG_MMU
+ printf("mmu_probe: 0x%08x (lev %d) -> 0x%08x\n", T0, mmulev, ret);
+#endif
+ }
+ break;
+ case 4: /* read MMU regs */
+ {
+ int reg = (T0 >> 8) & 0xf;
+
+ ret = env->mmuregs[reg];
+ if (reg == 3) /* Fault status cleared on read */
+ env->mmuregs[reg] = 0;
+#ifdef DEBUG_MMU
+ printf("mmu_read: reg[%d] = 0x%08x\n", reg, ret);
+#endif
+ }
+ break;
+ case 0x20 ... 0x2f: /* MMU passthrough */
+ switch(size) {
+ case 1:
+ ret = ldub_phys(T0);
+ break;
+ case 2:
+ ret = lduw_phys(T0 & ~1);
+ break;
+ default:
+ case 4:
+ ret = ldl_phys(T0 & ~3);
+ break;
+ case 8:
+ ret = ldl_phys(T0 & ~3);
+ T0 = ldl_phys((T0 + 4) & ~3);
+ break;
+ }
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ T1 = ret;
+}
+
+void helper_st_asi(int asi, int size, int sign)
+{
+ switch(asi) {
+ case 3: /* MMU flush */
+ {
+ int mmulev;
+
+ mmulev = (T0 >> 8) & 15;
+#ifdef DEBUG_MMU
+ printf("mmu flush level %d\n", mmulev);
+#endif
+ switch (mmulev) {
+ case 0: // flush page
+ tlb_flush_page(env, T0 & 0xfffff000);
+ break;
+ case 1: // flush segment (256k)
+ case 2: // flush region (16M)
+ case 3: // flush context (4G)
+ case 4: // flush entire
+ tlb_flush(env, 1);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MMU
+ dump_mmu(env);
+#endif
+ return;
+ }
+ case 4: /* write MMU regs */
+ {
+ int reg = (T0 >> 8) & 0xf;
+ uint32_t oldreg;
+
+ oldreg = env->mmuregs[reg];
+ switch(reg) {
+ case 0:
+ env->mmuregs[reg] &= ~(MMU_E | MMU_NF);
+ env->mmuregs[reg] |= T1 & (MMU_E | MMU_NF);
+ // Mappings generated during no-fault mode or MMU
+ // disabled mode are invalid in normal mode
+ if (oldreg != env->mmuregs[reg])
+ tlb_flush(env, 1);
+ break;
+ case 2:
+ env->mmuregs[reg] = T1;
+ if (oldreg != env->mmuregs[reg]) {
+ /* we flush when the MMU context changes because
+ QEMU has no MMU context support */
+ tlb_flush(env, 1);
+ }
+ break;
+ case 3:
+ case 4:
+ break;
+ default:
+ env->mmuregs[reg] = T1;
+ break;
+ }
+#ifdef DEBUG_MMU
+ if (oldreg != env->mmuregs[reg]) {
+ printf("mmu change reg[%d]: 0x%08x -> 0x%08x\n", reg, oldreg, env->mmuregs[reg]);
+ }
+ dump_mmu(env);
+#endif
+ return;
+ }
+ case 0x17: /* Block copy, sta access */
+ {
+ // value (T1) = src
+ // address (T0) = dst
+ // copy 32 bytes
+ uint32_t src = T1, dst = T0;
+ uint8_t temp[32];
+
+ tswap32s(&src);
+
+ cpu_physical_memory_read(src, (void *) &temp, 32);
+ cpu_physical_memory_write(dst, (void *) &temp, 32);
+ }
+ return;
+ case 0x1f: /* Block fill, stda access */
+ {
+ // value (T1, T2)
+ // address (T0) = dst
+ // fill 32 bytes
+ int i;
+ uint32_t dst = T0;
+ uint64_t val;
+
+ val = (((uint64_t)T1) << 32) | T2;
+ tswap64s(&val);
+
+ for (i = 0; i < 32; i += 8, dst += 8) {
+ cpu_physical_memory_write(dst, (void *) &val, 8);
+ }
+ }
+ return;
+ case 0x20 ... 0x2f: /* MMU passthrough */
+ {
+ switch(size) {
+ case 1:
+ stb_phys(T0, T1);
+ break;
+ case 2:
+ stw_phys(T0 & ~1, T1);
+ break;
+ case 4:
+ default:
+ stl_phys(T0 & ~3, T1);
+ break;
+ case 8:
+ stl_phys(T0 & ~3, T1);
+ stl_phys((T0 + 4) & ~3, T2);
+ break;
+ }
+ }
+ return;
+ default:
+ return;
+ }
+}
+
+#else
+
+void helper_ld_asi(int asi, int size, int sign)
+{
+ uint64_t ret = 0;
+
+ if (asi < 0x80 && (env->pstate & PS_PRIV) == 0)
+ raise_exception(TT_PRIV_ACT);
+
+ switch (asi) {
+ case 0x14: // Bypass
+ case 0x15: // Bypass, non-cacheable
+ {
+ switch(size) {
+ case 1:
+ ret = ldub_phys(T0);
+ break;
+ case 2:
+ ret = lduw_phys(T0 & ~1);
+ break;
+ case 4:
+ ret = ldl_phys(T0 & ~3);
+ break;
+ default:
+ case 8:
+ ret = ldq_phys(T0 & ~7);
+ break;
+ }
+ break;
+ }
+ case 0x04: // Nucleus
+ case 0x0c: // Nucleus Little Endian (LE)
+ case 0x10: // As if user primary
+ case 0x11: // As if user secondary
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ case 0x24: // Nucleus quad LDD 128 bit atomic
+ case 0x2c: // Nucleus quad LDD 128 bit atomic
+ case 0x4a: // UPA config
+ case 0x82: // Primary no-fault
+ case 0x83: // Secondary no-fault
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ case 0x8a: // Primary no-fault LE
+ case 0x8b: // Secondary no-fault LE
+ // XXX
+ break;
+ case 0x45: // LSU
+ ret = env->lsu;
+ break;
+ case 0x50: // I-MMU regs
+ {
+ int reg = (T0 >> 3) & 0xf;
+
+ ret = env->immuregs[reg];
+ break;
+ }
+ case 0x51: // I-MMU 8k TSB pointer
+ case 0x52: // I-MMU 64k TSB pointer
+ case 0x55: // I-MMU data access
+ // XXX
+ break;
+ case 0x56: // I-MMU tag read
+ {
+ unsigned int i;
+
+ for (i = 0; i < 64; i++) {
+ // Valid, ctx match, vaddr match
+ if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0 &&
+ env->itlb_tag[i] == T0) {
+ ret = env->itlb_tag[i];
+ break;
+ }
+ }
+ break;
+ }
+ case 0x58: // D-MMU regs
+ {
+ int reg = (T0 >> 3) & 0xf;
+
+ ret = env->dmmuregs[reg];
+ break;
+ }
+ case 0x5e: // D-MMU tag read
+ {
+ unsigned int i;
+
+ for (i = 0; i < 64; i++) {
+ // Valid, ctx match, vaddr match
+ if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0 &&
+ env->dtlb_tag[i] == T0) {
+ ret = env->dtlb_tag[i];
+ break;
+ }
+ }
+ break;
+ }
+ case 0x59: // D-MMU 8k TSB pointer
+ case 0x5a: // D-MMU 64k TSB pointer
+ case 0x5b: // D-MMU data pointer
+ case 0x5d: // D-MMU data access
+ case 0x48: // Interrupt dispatch, RO
+ case 0x49: // Interrupt data receive
+ case 0x7f: // Incoming interrupt vector, RO
+ // XXX
+ break;
+ case 0x54: // I-MMU data in, WO
+ case 0x57: // I-MMU demap, WO
+ case 0x5c: // D-MMU data in, WO
+ case 0x5f: // D-MMU demap, WO
+ case 0x77: // Interrupt vector, WO
+ default:
+ ret = 0;
+ break;
+ }
+ T1 = ret;
+}
+
+void helper_st_asi(int asi, int size, int sign)
+{
+ if (asi < 0x80 && (env->pstate & PS_PRIV) == 0)
+ raise_exception(TT_PRIV_ACT);
+
+ switch(asi) {
+ case 0x14: // Bypass
+ case 0x15: // Bypass, non-cacheable
+ {
+ switch(size) {
+ case 1:
+ stb_phys(T0, T1);
+ break;
+ case 2:
+ stw_phys(T0 & ~1, T1);
+ break;
+ case 4:
+ stl_phys(T0 & ~3, T1);
+ break;
+ case 8:
+ default:
+ stq_phys(T0 & ~7, T1);
+ break;
+ }
+ }
+ return;
+ case 0x04: // Nucleus
+ case 0x0c: // Nucleus Little Endian (LE)
+ case 0x10: // As if user primary
+ case 0x11: // As if user secondary
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ case 0x24: // Nucleus quad LDD 128 bit atomic
+ case 0x2c: // Nucleus quad LDD 128 bit atomic
+ case 0x4a: // UPA config
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ // XXX
+ return;
+ case 0x45: // LSU
+ {
+ uint64_t oldreg;
+
+ oldreg = env->lsu;
+ env->lsu = T1 & (DMMU_E | IMMU_E);
+ // Mappings generated during D/I MMU disabled mode are
+ // invalid in normal mode
+ if (oldreg != env->lsu) {
+#ifdef DEBUG_MMU
+ printf("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", oldreg, env->lsu);
+ dump_mmu(env);
+#endif
+ tlb_flush(env, 1);
+ }
+ return;
+ }
+ case 0x50: // I-MMU regs
+ {
+ int reg = (T0 >> 3) & 0xf;
+ uint64_t oldreg;
+
+ oldreg = env->immuregs[reg];
+ switch(reg) {
+ case 0: // RO
+ case 4:
+ return;
+ case 1: // Not in I-MMU
+ case 2:
+ case 7:
+ case 8:
+ return;
+ case 3: // SFSR
+ if ((T1 & 1) == 0)
+ T1 = 0; // Clear SFSR
+ break;
+ case 5: // TSB access
+ case 6: // Tag access
+ default:
+ break;
+ }
+ env->immuregs[reg] = T1;
+#ifdef DEBUG_MMU
+ if (oldreg != env->immuregs[reg]) {
+ printf("mmu change reg[%d]: 0x%08" PRIx64 " -> 0x%08" PRIx64 "\n", reg, oldreg, env->immuregs[reg]);
+ }
+ dump_mmu(env);
+#endif
+ return;
+ }
+ case 0x54: // I-MMU data in
+ {
+ unsigned int i;
+
+ // Try finding an invalid entry
+ for (i = 0; i < 64; i++) {
+ if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0) {
+ env->itlb_tag[i] = env->immuregs[6];
+ env->itlb_tte[i] = T1;
+ return;
+ }
+ }
+ // Try finding an unlocked entry
+ for (i = 0; i < 64; i++) {
+ if ((env->itlb_tte[i] & 0x40) == 0) {
+ env->itlb_tag[i] = env->immuregs[6];
+ env->itlb_tte[i] = T1;
+ return;
+ }
+ }
+ // error state?
+ return;
+ }
+ case 0x55: // I-MMU data access
+ {
+ unsigned int i = (T0 >> 3) & 0x3f;
+
+ env->itlb_tag[i] = env->immuregs[6];
+ env->itlb_tte[i] = T1;
+ return;
+ }
+ case 0x57: // I-MMU demap
+ // XXX
+ return;
+ case 0x58: // D-MMU regs
+ {
+ int reg = (T0 >> 3) & 0xf;
+ uint64_t oldreg;
+
+ oldreg = env->dmmuregs[reg];
+ switch(reg) {
+ case 0: // RO
+ case 4:
+ return;
+ case 3: // SFSR
+ if ((T1 & 1) == 0) {
+ T1 = 0; // Clear SFSR, Fault address
+ env->dmmuregs[4] = 0;
+ }
+ env->dmmuregs[reg] = T1;
+ break;
+ case 1: // Primary context
+ case 2: // Secondary context
+ case 5: // TSB access
+ case 6: // Tag access
+ case 7: // Virtual Watchpoint
+ case 8: // Physical Watchpoint
+ default:
+ break;
+ }
+ env->dmmuregs[reg] = T1;
+#ifdef DEBUG_MMU
+ if (oldreg != env->dmmuregs[reg]) {
+ printf("mmu change reg[%d]: 0x%08" PRIx64 " -> 0x%08" PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]);
+ }
+ dump_mmu(env);
+#endif
+ return;
+ }
+ case 0x5c: // D-MMU data in
+ {
+ unsigned int i;
+
+ // Try finding an invalid entry
+ for (i = 0; i < 64; i++) {
+ if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0) {
+ env->dtlb_tag[i] = env->dmmuregs[6];
+ env->dtlb_tte[i] = T1;
+ return;
+ }
+ }
+ // Try finding an unlocked entry
+ for (i = 0; i < 64; i++) {
+ if ((env->dtlb_tte[i] & 0x40) == 0) {
+ env->dtlb_tag[i] = env->dmmuregs[6];
+ env->dtlb_tte[i] = T1;
+ return;
+ }
+ }
+ // error state?
+ return;
+ }
+ case 0x5d: // D-MMU data access
+ {
+ unsigned int i = (T0 >> 3) & 0x3f;
+
+ env->dtlb_tag[i] = env->dmmuregs[6];
+ env->dtlb_tte[i] = T1;
+ return;
+ }
+ case 0x5f: // D-MMU demap
+ case 0x49: // Interrupt data receive
+ // XXX
+ return;
+ case 0x51: // I-MMU 8k TSB pointer, RO
+ case 0x52: // I-MMU 64k TSB pointer, RO
+ case 0x56: // I-MMU tag read, RO
+ case 0x59: // D-MMU 8k TSB pointer, RO
+ case 0x5a: // D-MMU 64k TSB pointer, RO
+ case 0x5b: // D-MMU data pointer, RO
+ case 0x5e: // D-MMU tag read, RO
+ case 0x48: // Interrupt dispatch, RO
+ case 0x7f: // Incoming interrupt vector, RO
+ case 0x82: // Primary no-fault, RO
+ case 0x83: // Secondary no-fault, RO
+ case 0x8a: // Primary no-fault LE, RO
+ case 0x8b: // Secondary no-fault LE, RO
+ default:
+ return;
+ }
+}
+#endif
+#endif /* !CONFIG_USER_ONLY */
+
+#ifndef TARGET_SPARC64
+void helper_rett()
+{
+ unsigned int cwp;
+
+ env->psret = 1;
+ cwp = (env->cwp + 1) & (NWINDOWS - 1);
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_UNF);
+ }
+ set_cwp(cwp);
+ env->psrs = env->psrps;
+}
+#endif
+
+void helper_ldfsr(void)
+{
+ int rnd_mode;
+ switch (env->fsr & FSR_RD_MASK) {
+ case FSR_RD_NEAREST:
+ rnd_mode = float_round_nearest_even;
+ break;
+ default:
+ case FSR_RD_ZERO:
+ rnd_mode = float_round_to_zero;
+ break;
+ case FSR_RD_POS:
+ rnd_mode = float_round_up;
+ break;
+ case FSR_RD_NEG:
+ rnd_mode = float_round_down;
+ break;
+ }
+ set_float_rounding_mode(rnd_mode, &env->fp_status);
+}
+
+void helper_debug()
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+#ifndef TARGET_SPARC64
+void do_wrpsr()
+{
+ PUT_PSR(env, T0);
+}
+
+void do_rdpsr()
+{
+ T0 = GET_PSR(env);
+}
+
+#else
+
+void do_popc()
+{
+ T0 = (T1 & 0x5555555555555555ULL) + ((T1 >> 1) & 0x5555555555555555ULL);
+ T0 = (T0 & 0x3333333333333333ULL) + ((T0 >> 2) & 0x3333333333333333ULL);
+ T0 = (T0 & 0x0f0f0f0f0f0f0f0fULL) + ((T0 >> 4) & 0x0f0f0f0f0f0f0f0fULL);
+ T0 = (T0 & 0x00ff00ff00ff00ffULL) + ((T0 >> 8) & 0x00ff00ff00ff00ffULL);
+ T0 = (T0 & 0x0000ffff0000ffffULL) + ((T0 >> 16) & 0x0000ffff0000ffffULL);
+ T0 = (T0 & 0x00000000ffffffffULL) + ((T0 >> 32) & 0x00000000ffffffffULL);
+}
+
+static inline uint64_t *get_gregset(uint64_t pstate)
+{
+ switch (pstate) {
+ default:
+ case 0:
+ return env->bgregs;
+ case PS_AG:
+ return env->agregs;
+ case PS_MG:
+ return env->mgregs;
+ case PS_IG:
+ return env->igregs;
+ }
+}
+
+void do_wrpstate()
+{
+ uint64_t new_pstate, pstate_regs, new_pstate_regs;
+ uint64_t *src, *dst;
+
+ new_pstate = T0 & 0xf3f;
+ pstate_regs = env->pstate & 0xc01;
+ new_pstate_regs = new_pstate & 0xc01;
+ if (new_pstate_regs != pstate_regs) {
+ // Switch global register bank
+ src = get_gregset(new_pstate_regs);
+ dst = get_gregset(pstate_regs);
+ memcpy32(dst, env->gregs);
+ memcpy32(env->gregs, src);
+ }
+ env->pstate = new_pstate;
+}
+
+void do_done(void)
+{
+ env->tl--;
+ env->pc = env->tnpc[env->tl];
+ env->npc = env->tnpc[env->tl] + 4;
+ PUT_CCR(env, env->tstate[env->tl] >> 32);
+ env->asi = (env->tstate[env->tl] >> 24) & 0xff;
+ env->pstate = (env->tstate[env->tl] >> 8) & 0xfff;
+ set_cwp(env->tstate[env->tl] & 0xff);
+}
+
+void do_retry(void)
+{
+ env->tl--;
+ env->pc = env->tpc[env->tl];
+ env->npc = env->tnpc[env->tl];
+ PUT_CCR(env, env->tstate[env->tl] >> 32);
+ env->asi = (env->tstate[env->tl] >> 24) & 0xff;
+ env->pstate = (env->tstate[env->tl] >> 8) & 0xfff;
+ set_cwp(env->tstate[env->tl] & 0xff);
+}
+#endif
+
+void set_cwp(int new_cwp)
+{
+ /* put the modified wrap registers at their proper location */
+ if (env->cwp == (NWINDOWS - 1))
+ memcpy32(env->regbase, env->regbase + NWINDOWS * 16);
+ env->cwp = new_cwp;
+ /* put the wrap registers at their temporary location */
+ if (new_cwp == (NWINDOWS - 1))
+ memcpy32(env->regbase + NWINDOWS * 16, env->regbase);
+ env->regwptr = env->regbase + (new_cwp * 16);
+ REGWPTR = env->regwptr;
+}
+
+void cpu_set_cwp(CPUState *env1, int new_cwp)
+{
+ CPUState *saved_env;
+#ifdef reg_REGWPTR
+ target_ulong *saved_regwptr;
+#endif
+
+ saved_env = env;
+#ifdef reg_REGWPTR
+ saved_regwptr = REGWPTR;
+#endif
+ env = env1;
+ set_cwp(new_cwp);
+ env = saved_env;
+#ifdef reg_REGWPTR
+ REGWPTR = saved_regwptr;
+#endif
+}
+
+#ifdef TARGET_SPARC64
+void do_interrupt(int intno)
+{
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_INT) {
+ static int count;
+ fprintf(logfile, "%6d: v=%04x pc=%016" PRIx64 " npc=%016" PRIx64 " SP=%016" PRIx64 "\n",
+ count, intno,
+ env->pc,
+ env->npc, env->regwptr[6]);
+ cpu_dump_state(env, logfile, fprintf, 0);
+#if 0
+ {
+ int i;
+ uint8_t *ptr;
+
+ fprintf(logfile, " code=");
+ ptr = (uint8_t *)env->pc;
+ for(i = 0; i < 16; i++) {
+ fprintf(logfile, " %02x", ldub(ptr + i));
+ }
+ fprintf(logfile, "\n");
+ }
+#endif
+ count++;
+ }
+#endif
+#if !defined(CONFIG_USER_ONLY)
+ if (env->tl == MAXTL) {
+ cpu_abort(env, "Trap 0x%04x while trap level is MAXTL, Error state", env->exception_index);
+ return;
+ }
+#endif
+ env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) |
+ ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff);
+ env->tpc[env->tl] = env->pc;
+ env->tnpc[env->tl] = env->npc;
+ env->tt[env->tl] = intno;
+ env->pstate = PS_PEF | PS_PRIV | PS_AG;
+ env->tbr &= ~0x7fffULL;
+ env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+ if (env->tl < MAXTL - 1) {
+ env->tl++;
+ } else {
+ env->pstate |= PS_RED;
+ if (env->tl != MAXTL)
+ env->tl++;
+ }
+ env->pc = env->tbr;
+ env->npc = env->pc + 4;
+ env->exception_index = 0;
+}
+#else
+void do_interrupt(int intno)
+{
+ int cwp;
+
+#ifdef DEBUG_PCALL
+ if (loglevel & CPU_LOG_INT) {
+ static int count;
+ fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n",
+ count, intno,
+ env->pc,
+ env->npc, env->regwptr[6]);
+ cpu_dump_state(env, logfile, fprintf, 0);
+#if 0
+ {
+ int i;
+ uint8_t *ptr;
+
+ fprintf(logfile, " code=");
+ ptr = (uint8_t *)env->pc;
+ for(i = 0; i < 16; i++) {
+ fprintf(logfile, " %02x", ldub(ptr + i));
+ }
+ fprintf(logfile, "\n");
+ }
+#endif
+ count++;
+ }
+#endif
+#if !defined(CONFIG_USER_ONLY)
+ if (env->psret == 0) {
+ cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index);
+ return;
+ }
+#endif
+ env->psret = 0;
+ cwp = (env->cwp - 1) & (NWINDOWS - 1);
+ set_cwp(cwp);
+ env->regwptr[9] = env->pc;
+ env->regwptr[10] = env->npc;
+ env->psrps = env->psrs;
+ env->psrs = 1;
+ env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
+ env->pc = env->tbr;
+ env->npc = env->pc + 4;
+ env->exception_index = 0;
+}
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ int ret;
+ unsigned long pc;
+ CPUState *saved_env;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, (void *)T2);
+ }
+ }
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-sparc/op_mem.h b/target-sparc/op_mem.h
new file mode 100644
index 0000000..f5dbd26
--- /dev/null
+++ b/target-sparc/op_mem.h
@@ -0,0 +1,114 @@
+/*** Integer load ***/
+#define SPARC_LD_OP(name, qp) \
+void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \
+{ \
+ T1 = (target_ulong)glue(qp, MEMSUFFIX)(T0); \
+}
+
+#define SPARC_LD_OP_S(name, qp) \
+ void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \
+ { \
+ T1 = (target_long)glue(qp, MEMSUFFIX)(T0); \
+ }
+
+#define SPARC_ST_OP(name, op) \
+void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \
+{ \
+ glue(op, MEMSUFFIX)(T0, T1); \
+}
+
+SPARC_LD_OP(ld, ldl);
+SPARC_LD_OP(ldub, ldub);
+SPARC_LD_OP(lduh, lduw);
+SPARC_LD_OP_S(ldsb, ldsb);
+SPARC_LD_OP_S(ldsh, ldsw);
+
+/*** Integer store ***/
+SPARC_ST_OP(st, stl);
+SPARC_ST_OP(stb, stb);
+SPARC_ST_OP(sth, stw);
+
+void OPPROTO glue(op_std, MEMSUFFIX)(void)
+{
+ glue(stl, MEMSUFFIX)(T0, T1);
+ glue(stl, MEMSUFFIX)((T0 + 4), T2);
+}
+
+void OPPROTO glue(op_ldstub, MEMSUFFIX)(void)
+{
+ T1 = glue(ldub, MEMSUFFIX)(T0);
+ glue(stb, MEMSUFFIX)(T0, 0xff); /* XXX: Should be Atomically */
+}
+
+void OPPROTO glue(op_swap, MEMSUFFIX)(void)
+{
+ target_ulong tmp = glue(ldl, MEMSUFFIX)(T0);
+ glue(stl, MEMSUFFIX)(T0, T1); /* XXX: Should be Atomically */
+ T1 = tmp;
+}
+
+void OPPROTO glue(op_ldd, MEMSUFFIX)(void)
+{
+ T1 = glue(ldl, MEMSUFFIX)(T0);
+ T0 = glue(ldl, MEMSUFFIX)((T0 + 4));
+}
+
+/*** Floating-point store ***/
+void OPPROTO glue(op_stf, MEMSUFFIX) (void)
+{
+ glue(stfl, MEMSUFFIX)(T0, FT0);
+}
+
+void OPPROTO glue(op_stdf, MEMSUFFIX) (void)
+{
+ glue(stfq, MEMSUFFIX)(T0, DT0);
+}
+
+/*** Floating-point load ***/
+void OPPROTO glue(op_ldf, MEMSUFFIX) (void)
+{
+ FT0 = glue(ldfl, MEMSUFFIX)(T0);
+}
+
+void OPPROTO glue(op_lddf, MEMSUFFIX) (void)
+{
+ DT0 = glue(ldfq, MEMSUFFIX)(T0);
+}
+
+#ifdef TARGET_SPARC64
+/* XXX: Should be Atomically */
+/* XXX: There are no cas[x] instructions, only cas[x]a */
+void OPPROTO glue(op_cas, MEMSUFFIX)(void)
+{
+ uint32_t tmp;
+
+ tmp = glue(ldl, MEMSUFFIX)(T0);
+ T2 &= 0xffffffffULL;
+ if (tmp == (T1 & 0xffffffffULL)) {
+ glue(stl, MEMSUFFIX)(T0, T2);
+ }
+ T2 = tmp;
+}
+
+void OPPROTO glue(op_casx, MEMSUFFIX)(void)
+{
+ uint64_t tmp;
+
+ // XXX
+ tmp = (uint64_t)glue(ldl, MEMSUFFIX)(T0) << 32;
+ tmp |= glue(ldl, MEMSUFFIX)(T0);
+ if (tmp == T1) {
+ glue(stq, MEMSUFFIX)(T0, T2);
+ }
+ T2 = tmp;
+}
+
+void OPPROTO glue(op_ldsw, MEMSUFFIX)(void)
+{
+ T1 = (int64_t)(glue(ldl, MEMSUFFIX)(T0) & 0xffffffff);
+}
+
+SPARC_LD_OP(ldx, ldq);
+SPARC_ST_OP(stx, stq);
+#endif
+#undef MEMSUFFIX
diff --git a/target-sparc/op_template.h b/target-sparc/op_template.h
new file mode 100644
index 0000000..ecf65fd
--- /dev/null
+++ b/target-sparc/op_template.h
@@ -0,0 +1,48 @@
+/*
+ * SPARC micro operations (templates for various register related
+ * operations)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+void OPPROTO glue(op_movl_T0_, REGNAME)(void)
+{
+ T0 = REG;
+}
+
+void OPPROTO glue(op_movl_T1_, REGNAME)(void)
+{
+ T1 = REG;
+}
+
+void OPPROTO glue(op_movl_T2_, REGNAME)(void)
+{
+ T2 = REG;
+}
+
+void OPPROTO glue(glue(op_movl_, REGNAME), _T0)(void)
+{
+ REG = T0;
+}
+
+void OPPROTO glue(glue(op_movl_, REGNAME), _T1)(void)
+{
+ REG = T1;
+}
+
+#undef REG
+#undef REGNAME
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
new file mode 100644
index 0000000..a522d77
--- /dev/null
+++ b/target-sparc/translate.c
@@ -0,0 +1,2845 @@
+/*
+ SPARC translation
+
+ Copyright (C) 2003 Thomas M. Ogrisegg <tom@fnord.at>
+ Copyright (C) 2003-2005 Fabrice Bellard
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ TODO-list:
+
+ Rest of V9 instructions, VIS instructions
+ NPC/PC static optimisations (use JUMP_TB when possible)
+ Optimize synthetic instructions
+ Optional alignment check
+ 128-bit float
+ Tagged add/sub
+*/
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+#define DEBUG_DISAS
+
+#define DYNAMIC_PC 1 /* dynamic pc value */
+#define JUMP_PC 2 /* dynamic pc value which takes only two values
+ according to jump_pc[T2] */
+
+typedef struct DisasContext {
+ target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */
+ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */
+ target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */
+ int is_br;
+ int mem_idx;
+ int fpu_enabled;
+ struct TranslationBlock *tb;
+} DisasContext;
+
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+extern FILE *logfile;
+extern int loglevel;
+
+enum {
+#define DEF(s,n,copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS
+};
+
+#include "gen-op.h"
+
+// This function uses non-native bit order
+#define GET_FIELD(X, FROM, TO) \
+ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
+
+// This function uses the order in the manuals, i.e. bit 0 is 2^0
+#define GET_FIELD_SP(X, FROM, TO) \
+ GET_FIELD(X, 31 - (TO), 31 - (FROM))
+
+#define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1)
+#define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), 32 - ((b) - (a) + 1))
+
+#ifdef TARGET_SPARC64
+#define DFPREG(r) (((r & 1) << 6) | (r & 0x1e))
+#else
+#define DFPREG(r) (r)
+#endif
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+static int sign_extend(int x, int len)
+{
+ len = 32 - len;
+ return (x << len) >> len;
+}
+
+#define IS_IMM (insn & (1<<13))
+
+static void disas_sparc_insn(DisasContext * dc);
+
+static GenOpFunc *gen_op_movl_TN_reg[2][32] = {
+ {
+ gen_op_movl_g0_T0,
+ gen_op_movl_g1_T0,
+ gen_op_movl_g2_T0,
+ gen_op_movl_g3_T0,
+ gen_op_movl_g4_T0,
+ gen_op_movl_g5_T0,
+ gen_op_movl_g6_T0,
+ gen_op_movl_g7_T0,
+ gen_op_movl_o0_T0,
+ gen_op_movl_o1_T0,
+ gen_op_movl_o2_T0,
+ gen_op_movl_o3_T0,
+ gen_op_movl_o4_T0,
+ gen_op_movl_o5_T0,
+ gen_op_movl_o6_T0,
+ gen_op_movl_o7_T0,
+ gen_op_movl_l0_T0,
+ gen_op_movl_l1_T0,
+ gen_op_movl_l2_T0,
+ gen_op_movl_l3_T0,
+ gen_op_movl_l4_T0,
+ gen_op_movl_l5_T0,
+ gen_op_movl_l6_T0,
+ gen_op_movl_l7_T0,
+ gen_op_movl_i0_T0,
+ gen_op_movl_i1_T0,
+ gen_op_movl_i2_T0,
+ gen_op_movl_i3_T0,
+ gen_op_movl_i4_T0,
+ gen_op_movl_i5_T0,
+ gen_op_movl_i6_T0,
+ gen_op_movl_i7_T0,
+ },
+ {
+ gen_op_movl_g0_T1,
+ gen_op_movl_g1_T1,
+ gen_op_movl_g2_T1,
+ gen_op_movl_g3_T1,
+ gen_op_movl_g4_T1,
+ gen_op_movl_g5_T1,
+ gen_op_movl_g6_T1,
+ gen_op_movl_g7_T1,
+ gen_op_movl_o0_T1,
+ gen_op_movl_o1_T1,
+ gen_op_movl_o2_T1,
+ gen_op_movl_o3_T1,
+ gen_op_movl_o4_T1,
+ gen_op_movl_o5_T1,
+ gen_op_movl_o6_T1,
+ gen_op_movl_o7_T1,
+ gen_op_movl_l0_T1,
+ gen_op_movl_l1_T1,
+ gen_op_movl_l2_T1,
+ gen_op_movl_l3_T1,
+ gen_op_movl_l4_T1,
+ gen_op_movl_l5_T1,
+ gen_op_movl_l6_T1,
+ gen_op_movl_l7_T1,
+ gen_op_movl_i0_T1,
+ gen_op_movl_i1_T1,
+ gen_op_movl_i2_T1,
+ gen_op_movl_i3_T1,
+ gen_op_movl_i4_T1,
+ gen_op_movl_i5_T1,
+ gen_op_movl_i6_T1,
+ gen_op_movl_i7_T1,
+ }
+};
+
+static GenOpFunc *gen_op_movl_reg_TN[3][32] = {
+ {
+ gen_op_movl_T0_g0,
+ gen_op_movl_T0_g1,
+ gen_op_movl_T0_g2,
+ gen_op_movl_T0_g3,
+ gen_op_movl_T0_g4,
+ gen_op_movl_T0_g5,
+ gen_op_movl_T0_g6,
+ gen_op_movl_T0_g7,
+ gen_op_movl_T0_o0,
+ gen_op_movl_T0_o1,
+ gen_op_movl_T0_o2,
+ gen_op_movl_T0_o3,
+ gen_op_movl_T0_o4,
+ gen_op_movl_T0_o5,
+ gen_op_movl_T0_o6,
+ gen_op_movl_T0_o7,
+ gen_op_movl_T0_l0,
+ gen_op_movl_T0_l1,
+ gen_op_movl_T0_l2,
+ gen_op_movl_T0_l3,
+ gen_op_movl_T0_l4,
+ gen_op_movl_T0_l5,
+ gen_op_movl_T0_l6,
+ gen_op_movl_T0_l7,
+ gen_op_movl_T0_i0,
+ gen_op_movl_T0_i1,
+ gen_op_movl_T0_i2,
+ gen_op_movl_T0_i3,
+ gen_op_movl_T0_i4,
+ gen_op_movl_T0_i5,
+ gen_op_movl_T0_i6,
+ gen_op_movl_T0_i7,
+ },
+ {
+ gen_op_movl_T1_g0,
+ gen_op_movl_T1_g1,
+ gen_op_movl_T1_g2,
+ gen_op_movl_T1_g3,
+ gen_op_movl_T1_g4,
+ gen_op_movl_T1_g5,
+ gen_op_movl_T1_g6,
+ gen_op_movl_T1_g7,
+ gen_op_movl_T1_o0,
+ gen_op_movl_T1_o1,
+ gen_op_movl_T1_o2,
+ gen_op_movl_T1_o3,
+ gen_op_movl_T1_o4,
+ gen_op_movl_T1_o5,
+ gen_op_movl_T1_o6,
+ gen_op_movl_T1_o7,
+ gen_op_movl_T1_l0,
+ gen_op_movl_T1_l1,
+ gen_op_movl_T1_l2,
+ gen_op_movl_T1_l3,
+ gen_op_movl_T1_l4,
+ gen_op_movl_T1_l5,
+ gen_op_movl_T1_l6,
+ gen_op_movl_T1_l7,
+ gen_op_movl_T1_i0,
+ gen_op_movl_T1_i1,
+ gen_op_movl_T1_i2,
+ gen_op_movl_T1_i3,
+ gen_op_movl_T1_i4,
+ gen_op_movl_T1_i5,
+ gen_op_movl_T1_i6,
+ gen_op_movl_T1_i7,
+ },
+ {
+ gen_op_movl_T2_g0,
+ gen_op_movl_T2_g1,
+ gen_op_movl_T2_g2,
+ gen_op_movl_T2_g3,
+ gen_op_movl_T2_g4,
+ gen_op_movl_T2_g5,
+ gen_op_movl_T2_g6,
+ gen_op_movl_T2_g7,
+ gen_op_movl_T2_o0,
+ gen_op_movl_T2_o1,
+ gen_op_movl_T2_o2,
+ gen_op_movl_T2_o3,
+ gen_op_movl_T2_o4,
+ gen_op_movl_T2_o5,
+ gen_op_movl_T2_o6,
+ gen_op_movl_T2_o7,
+ gen_op_movl_T2_l0,
+ gen_op_movl_T2_l1,
+ gen_op_movl_T2_l2,
+ gen_op_movl_T2_l3,
+ gen_op_movl_T2_l4,
+ gen_op_movl_T2_l5,
+ gen_op_movl_T2_l6,
+ gen_op_movl_T2_l7,
+ gen_op_movl_T2_i0,
+ gen_op_movl_T2_i1,
+ gen_op_movl_T2_i2,
+ gen_op_movl_T2_i3,
+ gen_op_movl_T2_i4,
+ gen_op_movl_T2_i5,
+ gen_op_movl_T2_i6,
+ gen_op_movl_T2_i7,
+ }
+};
+
+static GenOpFunc1 *gen_op_movl_TN_im[3] = {
+ gen_op_movl_T0_im,
+ gen_op_movl_T1_im,
+ gen_op_movl_T2_im
+};
+
+// Sign extending version
+static GenOpFunc1 * const gen_op_movl_TN_sim[3] = {
+ gen_op_movl_T0_sim,
+ gen_op_movl_T1_sim,
+ gen_op_movl_T2_sim
+};
+
+#ifdef TARGET_SPARC64
+#define GEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [64] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+NAME ## 32, 0, NAME ## 34, 0, NAME ## 36, 0, NAME ## 38, 0, \
+NAME ## 40, 0, NAME ## 42, 0, NAME ## 44, 0, NAME ## 46, 0, \
+NAME ## 48, 0, NAME ## 50, 0, NAME ## 52, 0, NAME ## 54, 0, \
+NAME ## 56, 0, NAME ## 58, 0, NAME ## 60, 0, NAME ## 62, 0, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+#else
+#define GEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+#endif
+
+/* floating point registers moves */
+GEN32(gen_op_load_fpr_FT0, gen_op_load_fpr_FT0_fprf);
+GEN32(gen_op_load_fpr_FT1, gen_op_load_fpr_FT1_fprf);
+GEN32(gen_op_store_FT0_fpr, gen_op_store_FT0_fpr_fprf);
+GEN32(gen_op_store_FT1_fpr, gen_op_store_FT1_fpr_fprf);
+
+GEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fprf);
+GEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fprf);
+GEN32(gen_op_store_DT0_fpr, gen_op_store_DT0_fpr_fprf);
+GEN32(gen_op_store_DT1_fpr, gen_op_store_DT1_fpr_fprf);
+
+#ifdef TARGET_SPARC64
+// 'a' versions allowed to user depending on asi
+#if defined(CONFIG_USER_ONLY)
+#define supervisor(dc) 0
+#define gen_op_ldst(name) gen_op_##name##_raw()
+#define OP_LD_TABLE(width) \
+ static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \
+ { \
+ int asi, offset; \
+ \
+ if (IS_IMM) { \
+ offset = GET_FIELD(insn, 25, 31); \
+ if (is_ld) \
+ gen_op_ld_asi_reg(offset, size, sign); \
+ else \
+ gen_op_st_asi_reg(offset, size, sign); \
+ return; \
+ } \
+ asi = GET_FIELD(insn, 19, 26); \
+ switch (asi) { \
+ case 0x80: /* Primary address space */ \
+ gen_op_##width##_raw(); \
+ break; \
+ case 0x82: /* Primary address space, non-faulting load */ \
+ gen_op_##width##_raw(); \
+ break; \
+ default: \
+ break; \
+ } \
+ }
+
+#else
+#define gen_op_ldst(name) (*gen_op_##name[dc->mem_idx])()
+#define OP_LD_TABLE(width) \
+ static GenOpFunc *gen_op_##width[] = { \
+ &gen_op_##width##_user, \
+ &gen_op_##width##_kernel, \
+ }; \
+ \
+ static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \
+ { \
+ int asi, offset; \
+ \
+ if (IS_IMM) { \
+ offset = GET_FIELD(insn, 25, 31); \
+ if (is_ld) \
+ gen_op_ld_asi_reg(offset, size, sign); \
+ else \
+ gen_op_st_asi_reg(offset, size, sign); \
+ return; \
+ } \
+ asi = GET_FIELD(insn, 19, 26); \
+ if (is_ld) \
+ gen_op_ld_asi(asi, size, sign); \
+ else \
+ gen_op_st_asi(asi, size, sign); \
+ }
+
+#define supervisor(dc) (dc->mem_idx == 1)
+#endif
+#else
+#if defined(CONFIG_USER_ONLY)
+#define gen_op_ldst(name) gen_op_##name##_raw()
+#define OP_LD_TABLE(width)
+#define supervisor(dc) 0
+#else
+#define gen_op_ldst(name) (*gen_op_##name[dc->mem_idx])()
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_##width[] = { \
+ &gen_op_##width##_user, \
+ &gen_op_##width##_kernel, \
+}; \
+ \
+static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \
+{ \
+ int asi; \
+ \
+ asi = GET_FIELD(insn, 19, 26); \
+ switch (asi) { \
+ case 10: /* User data access */ \
+ gen_op_##width##_user(); \
+ break; \
+ case 11: /* Supervisor data access */ \
+ gen_op_##width##_kernel(); \
+ break; \
+ case 0x20 ... 0x2f: /* MMU passthrough */ \
+ if (is_ld) \
+ gen_op_ld_asi(asi, size, sign); \
+ else \
+ gen_op_st_asi(asi, size, sign); \
+ break; \
+ default: \
+ if (is_ld) \
+ gen_op_ld_asi(asi, size, sign); \
+ else \
+ gen_op_st_asi(asi, size, sign); \
+ break; \
+ } \
+}
+
+#define supervisor(dc) (dc->mem_idx == 1)
+#endif
+#endif
+
+OP_LD_TABLE(ld);
+OP_LD_TABLE(st);
+OP_LD_TABLE(ldub);
+OP_LD_TABLE(lduh);
+OP_LD_TABLE(ldsb);
+OP_LD_TABLE(ldsh);
+OP_LD_TABLE(stb);
+OP_LD_TABLE(sth);
+OP_LD_TABLE(std);
+OP_LD_TABLE(ldstub);
+OP_LD_TABLE(swap);
+OP_LD_TABLE(ldd);
+OP_LD_TABLE(stf);
+OP_LD_TABLE(stdf);
+OP_LD_TABLE(ldf);
+OP_LD_TABLE(lddf);
+
+#ifdef TARGET_SPARC64
+OP_LD_TABLE(ldsw);
+OP_LD_TABLE(ldx);
+OP_LD_TABLE(stx);
+OP_LD_TABLE(cas);
+OP_LD_TABLE(casx);
+#endif
+
+static inline void gen_movl_imm_TN(int reg, uint32_t imm)
+{
+ gen_op_movl_TN_im[reg](imm);
+}
+
+static inline void gen_movl_imm_T1(uint32_t val)
+{
+ gen_movl_imm_TN(1, val);
+}
+
+static inline void gen_movl_imm_T0(uint32_t val)
+{
+ gen_movl_imm_TN(0, val);
+}
+
+static inline void gen_movl_simm_TN(int reg, int32_t imm)
+{
+ gen_op_movl_TN_sim[reg](imm);
+}
+
+static inline void gen_movl_simm_T1(int32_t val)
+{
+ gen_movl_simm_TN(1, val);
+}
+
+static inline void gen_movl_simm_T0(int32_t val)
+{
+ gen_movl_simm_TN(0, val);
+}
+
+static inline void gen_movl_reg_TN(int reg, int t)
+{
+ if (reg)
+ gen_op_movl_reg_TN[t][reg] ();
+ else
+ gen_movl_imm_TN(t, 0);
+}
+
+static inline void gen_movl_reg_T0(int reg)
+{
+ gen_movl_reg_TN(reg, 0);
+}
+
+static inline void gen_movl_reg_T1(int reg)
+{
+ gen_movl_reg_TN(reg, 1);
+}
+
+static inline void gen_movl_reg_T2(int reg)
+{
+ gen_movl_reg_TN(reg, 2);
+}
+
+static inline void gen_movl_TN_reg(int reg, int t)
+{
+ if (reg)
+ gen_op_movl_TN_reg[t][reg] ();
+}
+
+static inline void gen_movl_T0_reg(int reg)
+{
+ gen_movl_TN_reg(reg, 0);
+}
+
+static inline void gen_movl_T1_reg(int reg)
+{
+ gen_movl_TN_reg(reg, 1);
+}
+
+static inline void gen_jmp_im(target_ulong pc)
+{
+#ifdef TARGET_SPARC64
+ if (pc == (uint32_t)pc) {
+ gen_op_jmp_im(pc);
+ } else {
+ gen_op_jmp_im64(pc >> 32, pc);
+ }
+#else
+ gen_op_jmp_im(pc);
+#endif
+}
+
+static inline void gen_movl_npc_im(target_ulong npc)
+{
+#ifdef TARGET_SPARC64
+ if (npc == (uint32_t)npc) {
+ gen_op_movl_npc_im(npc);
+ } else {
+ gen_op_movq_npc_im64(npc >> 32, npc);
+ }
+#else
+ gen_op_movl_npc_im(npc);
+#endif
+}
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num,
+ target_ulong pc, target_ulong npc)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) &&
+ (npc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK)) {
+ /* jump to same page: we can use a direct jump */
+ if (tb_num == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_jmp_im(pc);
+ gen_movl_npc_im(npc);
+ gen_op_movl_T0_im((long)tb + tb_num);
+ gen_op_exit_tb();
+ } else {
+ /* jump to another page: currently not optimized */
+ gen_jmp_im(pc);
+ gen_movl_npc_im(npc);
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ }
+}
+
+static inline void gen_branch2(DisasContext *dc, long tb, target_ulong pc1, target_ulong pc2)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ gen_op_jz_T2_label(l1);
+
+ gen_goto_tb(dc, 0, pc1, pc1 + 4);
+
+ gen_set_label(l1);
+ gen_goto_tb(dc, 1, pc2, pc2 + 4);
+}
+
+static inline void gen_branch_a(DisasContext *dc, long tb, target_ulong pc1, target_ulong pc2)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ gen_op_jz_T2_label(l1);
+
+ gen_goto_tb(dc, 0, pc2, pc1);
+
+ gen_set_label(l1);
+ gen_goto_tb(dc, 1, pc2 + 4, pc2 + 8);
+}
+
+static inline void gen_branch(DisasContext *dc, long tb, target_ulong pc, target_ulong npc)
+{
+ gen_goto_tb(dc, 0, pc, npc);
+}
+
+static inline void gen_generic_branch(DisasContext *dc, target_ulong npc1, target_ulong npc2)
+{
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ gen_op_jz_T2_label(l1);
+
+ gen_movl_npc_im(npc1);
+ gen_op_jmp_label(l2);
+
+ gen_set_label(l1);
+ gen_movl_npc_im(npc2);
+ gen_set_label(l2);
+}
+
+/* call this function before using T2 as it may have been set for a jump */
+static inline void flush_T2(DisasContext * dc)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]);
+ dc->npc = DYNAMIC_PC;
+ }
+}
+
+static inline void save_npc(DisasContext * dc)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]);
+ dc->npc = DYNAMIC_PC;
+ } else if (dc->npc != DYNAMIC_PC) {
+ gen_movl_npc_im(dc->npc);
+ }
+}
+
+static inline void save_state(DisasContext * dc)
+{
+ gen_jmp_im(dc->pc);
+ save_npc(dc);
+}
+
+static inline void gen_mov_pc_npc(DisasContext * dc)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]);
+ gen_op_mov_pc_npc();
+ dc->pc = DYNAMIC_PC;
+ } else if (dc->npc == DYNAMIC_PC) {
+ gen_op_mov_pc_npc();
+ dc->pc = DYNAMIC_PC;
+ } else {
+ dc->pc = dc->npc;
+ }
+}
+
+static GenOpFunc * const gen_cond[2][16] = {
+ {
+ gen_op_eval_ba,
+ gen_op_eval_be,
+ gen_op_eval_ble,
+ gen_op_eval_bl,
+ gen_op_eval_bleu,
+ gen_op_eval_bcs,
+ gen_op_eval_bneg,
+ gen_op_eval_bvs,
+ gen_op_eval_bn,
+ gen_op_eval_bne,
+ gen_op_eval_bg,
+ gen_op_eval_bge,
+ gen_op_eval_bgu,
+ gen_op_eval_bcc,
+ gen_op_eval_bpos,
+ gen_op_eval_bvc,
+ },
+ {
+#ifdef TARGET_SPARC64
+ gen_op_eval_ba,
+ gen_op_eval_xbe,
+ gen_op_eval_xble,
+ gen_op_eval_xbl,
+ gen_op_eval_xbleu,
+ gen_op_eval_xbcs,
+ gen_op_eval_xbneg,
+ gen_op_eval_xbvs,
+ gen_op_eval_bn,
+ gen_op_eval_xbne,
+ gen_op_eval_xbg,
+ gen_op_eval_xbge,
+ gen_op_eval_xbgu,
+ gen_op_eval_xbcc,
+ gen_op_eval_xbpos,
+ gen_op_eval_xbvc,
+#endif
+ },
+};
+
+static GenOpFunc * const gen_fcond[4][16] = {
+ {
+ gen_op_eval_ba,
+ gen_op_eval_fbne,
+ gen_op_eval_fblg,
+ gen_op_eval_fbul,
+ gen_op_eval_fbl,
+ gen_op_eval_fbug,
+ gen_op_eval_fbg,
+ gen_op_eval_fbu,
+ gen_op_eval_bn,
+ gen_op_eval_fbe,
+ gen_op_eval_fbue,
+ gen_op_eval_fbge,
+ gen_op_eval_fbuge,
+ gen_op_eval_fble,
+ gen_op_eval_fbule,
+ gen_op_eval_fbo,
+ },
+#ifdef TARGET_SPARC64
+ {
+ gen_op_eval_ba,
+ gen_op_eval_fbne_fcc1,
+ gen_op_eval_fblg_fcc1,
+ gen_op_eval_fbul_fcc1,
+ gen_op_eval_fbl_fcc1,
+ gen_op_eval_fbug_fcc1,
+ gen_op_eval_fbg_fcc1,
+ gen_op_eval_fbu_fcc1,
+ gen_op_eval_bn,
+ gen_op_eval_fbe_fcc1,
+ gen_op_eval_fbue_fcc1,
+ gen_op_eval_fbge_fcc1,
+ gen_op_eval_fbuge_fcc1,
+ gen_op_eval_fble_fcc1,
+ gen_op_eval_fbule_fcc1,
+ gen_op_eval_fbo_fcc1,
+ },
+ {
+ gen_op_eval_ba,
+ gen_op_eval_fbne_fcc2,
+ gen_op_eval_fblg_fcc2,
+ gen_op_eval_fbul_fcc2,
+ gen_op_eval_fbl_fcc2,
+ gen_op_eval_fbug_fcc2,
+ gen_op_eval_fbg_fcc2,
+ gen_op_eval_fbu_fcc2,
+ gen_op_eval_bn,
+ gen_op_eval_fbe_fcc2,
+ gen_op_eval_fbue_fcc2,
+ gen_op_eval_fbge_fcc2,
+ gen_op_eval_fbuge_fcc2,
+ gen_op_eval_fble_fcc2,
+ gen_op_eval_fbule_fcc2,
+ gen_op_eval_fbo_fcc2,
+ },
+ {
+ gen_op_eval_ba,
+ gen_op_eval_fbne_fcc3,
+ gen_op_eval_fblg_fcc3,
+ gen_op_eval_fbul_fcc3,
+ gen_op_eval_fbl_fcc3,
+ gen_op_eval_fbug_fcc3,
+ gen_op_eval_fbg_fcc3,
+ gen_op_eval_fbu_fcc3,
+ gen_op_eval_bn,
+ gen_op_eval_fbe_fcc3,
+ gen_op_eval_fbue_fcc3,
+ gen_op_eval_fbge_fcc3,
+ gen_op_eval_fbuge_fcc3,
+ gen_op_eval_fble_fcc3,
+ gen_op_eval_fbule_fcc3,
+ gen_op_eval_fbo_fcc3,
+ },
+#else
+ {}, {}, {},
+#endif
+};
+
+#ifdef TARGET_SPARC64
+static void gen_cond_reg(int cond)
+{
+ switch (cond) {
+ case 0x1:
+ gen_op_eval_brz();
+ break;
+ case 0x2:
+ gen_op_eval_brlez();
+ break;
+ case 0x3:
+ gen_op_eval_brlz();
+ break;
+ case 0x5:
+ gen_op_eval_brnz();
+ break;
+ case 0x6:
+ gen_op_eval_brgz();
+ break;
+ default:
+ case 0x7:
+ gen_op_eval_brgez();
+ break;
+ }
+}
+#endif
+
+/* XXX: potentially incorrect if dynamic npc */
+static void do_branch(DisasContext * dc, int32_t offset, uint32_t insn, int cc)
+{
+ unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ if (cond == 0x0) {
+ /* unconditional not taken */
+ if (a) {
+ dc->pc = dc->npc + 4;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->pc + 4;
+ }
+ } else if (cond == 0x8) {
+ /* unconditional taken */
+ if (a) {
+ dc->pc = target;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = target;
+ }
+ } else {
+ flush_T2(dc);
+ gen_cond[cc][cond]();
+ if (a) {
+ gen_branch_a(dc, (long)dc->tb, target, dc->npc);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+ }
+}
+
+/* XXX: potentially incorrect if dynamic npc */
+static void do_fbranch(DisasContext * dc, int32_t offset, uint32_t insn, int cc)
+{
+ unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ if (cond == 0x0) {
+ /* unconditional not taken */
+ if (a) {
+ dc->pc = dc->npc + 4;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->pc + 4;
+ }
+ } else if (cond == 0x8) {
+ /* unconditional taken */
+ if (a) {
+ dc->pc = target;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = target;
+ }
+ } else {
+ flush_T2(dc);
+ gen_fcond[cc][cond]();
+ if (a) {
+ gen_branch_a(dc, (long)dc->tb, target, dc->npc);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+ }
+}
+
+#ifdef TARGET_SPARC64
+/* XXX: potentially incorrect if dynamic npc */
+static void do_branch_reg(DisasContext * dc, int32_t offset, uint32_t insn)
+{
+ unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ flush_T2(dc);
+ gen_cond_reg(cond);
+ if (a) {
+ gen_branch_a(dc, (long)dc->tb, target, dc->npc);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+}
+
+static GenOpFunc * const gen_fcmps[4] = {
+ gen_op_fcmps,
+ gen_op_fcmps_fcc1,
+ gen_op_fcmps_fcc2,
+ gen_op_fcmps_fcc3,
+};
+
+static GenOpFunc * const gen_fcmpd[4] = {
+ gen_op_fcmpd,
+ gen_op_fcmpd_fcc1,
+ gen_op_fcmpd_fcc2,
+ gen_op_fcmpd_fcc3,
+};
+#endif
+
+static int gen_trap_ifnofpu(DisasContext * dc)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!dc->fpu_enabled) {
+ save_state(dc);
+ gen_op_exception(TT_NFPU_INSN);
+ dc->is_br = 1;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/* before an instruction, dc->pc must be static */
+static void disas_sparc_insn(DisasContext * dc)
+{
+ unsigned int insn, opc, rs1, rs2, rd;
+
+ insn = ldl_code(dc->pc);
+ opc = GET_FIELD(insn, 0, 1);
+
+ rd = GET_FIELD(insn, 2, 6);
+ switch (opc) {
+ case 0: /* branches/sethi */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 9);
+ int32_t target;
+ switch (xop) {
+#ifdef TARGET_SPARC64
+ case 0x1: /* V9 BPcc */
+ {
+ int cc;
+
+ target = GET_FIELD_SP(insn, 0, 18);
+ target = sign_extend(target, 18);
+ target <<= 2;
+ cc = GET_FIELD_SP(insn, 20, 21);
+ if (cc == 0)
+ do_branch(dc, target, insn, 0);
+ else if (cc == 2)
+ do_branch(dc, target, insn, 1);
+ else
+ goto illegal_insn;
+ goto jmp_insn;
+ }
+ case 0x3: /* V9 BPr */
+ {
+ target = GET_FIELD_SP(insn, 0, 13) |
+ (GET_FIELD_SP(insn, 20, 21) << 14);
+ target = sign_extend(target, 16);
+ target <<= 2;
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ do_branch_reg(dc, target, insn);
+ goto jmp_insn;
+ }
+ case 0x5: /* V9 FBPcc */
+ {
+ int cc = GET_FIELD_SP(insn, 20, 21);
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ target = GET_FIELD_SP(insn, 0, 18);
+ target = sign_extend(target, 19);
+ target <<= 2;
+ do_fbranch(dc, target, insn, cc);
+ goto jmp_insn;
+ }
+#endif
+ case 0x2: /* BN+x */
+ {
+ target = GET_FIELD(insn, 10, 31);
+ target = sign_extend(target, 22);
+ target <<= 2;
+ do_branch(dc, target, insn, 0);
+ goto jmp_insn;
+ }
+ case 0x6: /* FBN+x */
+ {
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ target = GET_FIELD(insn, 10, 31);
+ target = sign_extend(target, 22);
+ target <<= 2;
+ do_fbranch(dc, target, insn, 0);
+ goto jmp_insn;
+ }
+ case 0x4: /* SETHI */
+#define OPTIM
+#if defined(OPTIM)
+ if (rd) { // nop
+#endif
+ uint32_t value = GET_FIELD(insn, 10, 31);
+ gen_movl_imm_T0(value << 10);
+ gen_movl_T0_reg(rd);
+#if defined(OPTIM)
+ }
+#endif
+ break;
+ case 0x0: /* UNIMPL */
+ default:
+ goto illegal_insn;
+ }
+ break;
+ }
+ break;
+ case 1:
+ /*CALL*/ {
+ target_long target = GET_FIELDs(insn, 2, 31) << 2;
+
+#ifdef TARGET_SPARC64
+ if (dc->pc == (uint32_t)dc->pc) {
+ gen_op_movl_T0_im(dc->pc);
+ } else {
+ gen_op_movq_T0_im64(dc->pc >> 32, dc->pc);
+ }
+#else
+ gen_op_movl_T0_im(dc->pc);
+#endif
+ gen_movl_T0_reg(15);
+ target += dc->pc;
+ gen_mov_pc_npc(dc);
+ dc->npc = target;
+ }
+ goto jmp_insn;
+ case 2: /* FPU & Logical Operations */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 12);
+ if (xop == 0x3a) { /* generate trap */
+ int cond;
+
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) {
+ rs2 = GET_FIELD(insn, 25, 31);
+#if defined(OPTIM)
+ if (rs2 != 0) {
+#endif
+ gen_movl_simm_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ } else {
+ rs2 = GET_FIELD(insn, 27, 31);
+#if defined(OPTIM)
+ if (rs2 != 0) {
+#endif
+ gen_movl_reg_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ }
+ cond = GET_FIELD(insn, 3, 6);
+ if (cond == 0x8) {
+ save_state(dc);
+ gen_op_trap_T0();
+ } else if (cond != 0) {
+#ifdef TARGET_SPARC64
+ /* V9 icc/xcc */
+ int cc = GET_FIELD_SP(insn, 11, 12);
+ flush_T2(dc);
+ save_state(dc);
+ if (cc == 0)
+ gen_cond[0][cond]();
+ else if (cc == 2)
+ gen_cond[1][cond]();
+ else
+ goto illegal_insn;
+#else
+ flush_T2(dc);
+ save_state(dc);
+ gen_cond[0][cond]();
+#endif
+ gen_op_trapcc_T0();
+ }
+ gen_op_next_insn();
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ dc->is_br = 1;
+ goto jmp_insn;
+ } else if (xop == 0x28) {
+ rs1 = GET_FIELD(insn, 13, 17);
+ switch(rs1) {
+ case 0: /* rdy */
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, y));
+ gen_movl_T0_reg(rd);
+ break;
+ case 15: /* stbar / V9 membar */
+ break; /* no effect? */
+#ifdef TARGET_SPARC64
+ case 0x2: /* V9 rdccr */
+ gen_op_rdccr();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x3: /* V9 rdasi */
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, asi));
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x4: /* V9 rdtick */
+ gen_op_rdtick();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x5: /* V9 rdpc */
+ if (dc->pc == (uint32_t)dc->pc) {
+ gen_op_movl_T0_im(dc->pc);
+ } else {
+ gen_op_movq_T0_im64(dc->pc >> 32, dc->pc);
+ }
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x6: /* V9 rdfprs */
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, fprs));
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x13: /* Graphics Status */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, gsr));
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x17: /* Tick compare */
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, tick_cmpr));
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x18: /* System tick */
+ gen_op_rdtick(); // XXX
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x19: /* System tick compare */
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, stick_cmpr));
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x10: /* Performance Control */
+ case 0x11: /* Performance Instrumentation Counter */
+ case 0x12: /* Dispatch Control */
+ case 0x14: /* Softint set, WO */
+ case 0x15: /* Softint clear, WO */
+ case 0x16: /* Softint write */
+#endif
+ default:
+ goto illegal_insn;
+ }
+#if !defined(CONFIG_USER_ONLY)
+#ifndef TARGET_SPARC64
+ } else if (xop == 0x29) { /* rdpsr / V9 unimp */
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_op_rdpsr();
+ gen_movl_T0_reg(rd);
+ break;
+#endif
+ } else if (xop == 0x2a) { /* rdwim / V9 rdpr */
+ if (!supervisor(dc))
+ goto priv_insn;
+#ifdef TARGET_SPARC64
+ rs1 = GET_FIELD(insn, 13, 17);
+ switch (rs1) {
+ case 0: // tpc
+ gen_op_rdtpc();
+ break;
+ case 1: // tnpc
+ gen_op_rdtnpc();
+ break;
+ case 2: // tstate
+ gen_op_rdtstate();
+ break;
+ case 3: // tt
+ gen_op_rdtt();
+ break;
+ case 4: // tick
+ gen_op_rdtick();
+ break;
+ case 5: // tba
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, tbr));
+ break;
+ case 6: // pstate
+ gen_op_rdpstate();
+ break;
+ case 7: // tl
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, tl));
+ break;
+ case 8: // pil
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, psrpil));
+ break;
+ case 9: // cwp
+ gen_op_rdcwp();
+ break;
+ case 10: // cansave
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, cansave));
+ break;
+ case 11: // canrestore
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, canrestore));
+ break;
+ case 12: // cleanwin
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, cleanwin));
+ break;
+ case 13: // otherwin
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, otherwin));
+ break;
+ case 14: // wstate
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, wstate));
+ break;
+ case 31: // ver
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, version));
+ break;
+ case 15: // fq
+ default:
+ goto illegal_insn;
+ }
+#else
+ gen_op_movl_T0_env(offsetof(CPUSPARCState, wim));
+#endif
+ gen_movl_T0_reg(rd);
+ break;
+ } else if (xop == 0x2b) { /* rdtbr / V9 flushw */
+#ifdef TARGET_SPARC64
+ gen_op_flushw();
+#else
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_op_movtl_T0_env(offsetof(CPUSPARCState, tbr));
+ gen_movl_T0_reg(rd);
+#endif
+ break;
+#endif
+ } else if (xop == 0x34) { /* FPU Operations */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+ xop = GET_FIELD(insn, 18, 26);
+ switch (xop) {
+ case 0x1: /* fmovs */
+ gen_op_load_fpr_FT0(rs2);
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x5: /* fnegs */
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fnegs();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x9: /* fabss */
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fabss();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x29: /* fsqrts */
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fsqrts();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x2a: /* fsqrtd */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fsqrtd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x2b: /* fsqrtq */
+ goto nfpu_insn;
+ case 0x41:
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fadds();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x42:
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_faddd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x43: /* faddq */
+ goto nfpu_insn;
+ case 0x45:
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fsubs();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x46:
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fsubd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x47: /* fsubq */
+ goto nfpu_insn;
+ case 0x49:
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fmuls();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x4a:
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fmuld();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x4b: /* fmulq */
+ goto nfpu_insn;
+ case 0x4d:
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fdivs();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x4e:
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fdivd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x4f: /* fdivq */
+ goto nfpu_insn;
+ case 0x69:
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fsmuld();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x6e: /* fdmulq */
+ goto nfpu_insn;
+ case 0xc4:
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fitos();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0xc6:
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fdtos();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0xc7: /* fqtos */
+ goto nfpu_insn;
+ case 0xc8:
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fitod();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xc9:
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fstod();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xcb: /* fqtod */
+ goto nfpu_insn;
+ case 0xcc: /* fitoq */
+ goto nfpu_insn;
+ case 0xcd: /* fstoq */
+ goto nfpu_insn;
+ case 0xce: /* fdtoq */
+ goto nfpu_insn;
+ case 0xd1:
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fstoi();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0xd2:
+ gen_op_load_fpr_DT1(rs2);
+ gen_op_fdtoi();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0xd3: /* fqtoi */
+ goto nfpu_insn;
+#ifdef TARGET_SPARC64
+ case 0x2: /* V9 fmovd */
+ gen_op_load_fpr_DT0(DFPREG(rs2));
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x6: /* V9 fnegd */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fnegd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xa: /* V9 fabsd */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fabsd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x81: /* V9 fstox */
+ gen_op_load_fpr_FT1(rs2);
+ gen_op_fstox();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x82: /* V9 fdtox */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fdtox();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x84: /* V9 fxtos */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fxtos();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x88: /* V9 fxtod */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fxtod();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x3: /* V9 fmovq */
+ case 0x7: /* V9 fnegq */
+ case 0xb: /* V9 fabsq */
+ case 0x83: /* V9 fqtox */
+ case 0x8c: /* V9 fxtoq */
+ goto nfpu_insn;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop == 0x35) { /* FPU Operations */
+#ifdef TARGET_SPARC64
+ int cond;
+#endif
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+ xop = GET_FIELD(insn, 18, 26);
+#ifdef TARGET_SPARC64
+ if ((xop & 0x11f) == 0x005) { // V9 fmovsr
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ flush_T2(dc);
+ gen_cond_reg(cond);
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ gen_cond_reg(cond);
+ gen_op_fmovs_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr
+ goto nfpu_insn;
+ }
+#endif
+ switch (xop) {
+#ifdef TARGET_SPARC64
+ case 0x001: /* V9 fmovscc %fcc0 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_fcond[0][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x002: /* V9 fmovdcc %fcc0 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_fcond[0][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x003: /* V9 fmovqcc %fcc0 */
+ goto nfpu_insn;
+ case 0x041: /* V9 fmovscc %fcc1 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_fcond[1][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x042: /* V9 fmovdcc %fcc1 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_fcond[1][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x043: /* V9 fmovqcc %fcc1 */
+ goto nfpu_insn;
+ case 0x081: /* V9 fmovscc %fcc2 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_fcond[2][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x082: /* V9 fmovdcc %fcc2 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_fcond[2][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x083: /* V9 fmovqcc %fcc2 */
+ goto nfpu_insn;
+ case 0x0c1: /* V9 fmovscc %fcc3 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_fcond[3][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x0c2: /* V9 fmovdcc %fcc3 */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_fcond[3][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x0c3: /* V9 fmovqcc %fcc3 */
+ goto nfpu_insn;
+ case 0x101: /* V9 fmovscc %icc */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_cond[0][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x102: /* V9 fmovdcc %icc */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_cond[0][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x103: /* V9 fmovqcc %icc */
+ goto nfpu_insn;
+ case 0x181: /* V9 fmovscc %xcc */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_FT0(rd);
+ gen_op_load_fpr_FT1(rs2);
+ flush_T2(dc);
+ gen_cond[1][cond]();
+ gen_op_fmovs_cc();
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x182: /* V9 fmovdcc %xcc */
+ cond = GET_FIELD_SP(insn, 14, 17);
+ gen_op_load_fpr_DT0(rd);
+ gen_op_load_fpr_DT1(rs2);
+ flush_T2(dc);
+ gen_cond[1][cond]();
+ gen_op_fmovd_cc();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ case 0x183: /* V9 fmovqcc %xcc */
+ goto nfpu_insn;
+#endif
+ case 0x51: /* V9 %fcc */
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+#ifdef TARGET_SPARC64
+ gen_fcmps[rd & 3]();
+#else
+ gen_op_fcmps();
+#endif
+ break;
+ case 0x52: /* V9 %fcc */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+#ifdef TARGET_SPARC64
+ gen_fcmpd[rd & 3]();
+#else
+ gen_op_fcmpd();
+#endif
+ break;
+ case 0x53: /* fcmpq */
+ goto nfpu_insn;
+ case 0x55: /* fcmpes, V9 %fcc */
+ gen_op_load_fpr_FT0(rs1);
+ gen_op_load_fpr_FT1(rs2);
+#ifdef TARGET_SPARC64
+ gen_fcmps[rd & 3]();
+#else
+ gen_op_fcmps(); /* XXX should trap if qNaN or sNaN */
+#endif
+ break;
+ case 0x56: /* fcmped, V9 %fcc */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+#ifdef TARGET_SPARC64
+ gen_fcmpd[rd & 3]();
+#else
+ gen_op_fcmpd(); /* XXX should trap if qNaN or sNaN */
+#endif
+ break;
+ case 0x57: /* fcmpeq */
+ goto nfpu_insn;
+ default:
+ goto illegal_insn;
+ }
+#if defined(OPTIM)
+ } else if (xop == 0x2) {
+ // clr/mov shortcut
+
+ rs1 = GET_FIELD(insn, 13, 17);
+ if (rs1 == 0) {
+ // or %g0, x, y -> mov T1, x; mov y, T1
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 19, 31);
+ gen_movl_simm_T1(rs2);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_T1(rs2);
+ }
+ gen_movl_T1_reg(rd);
+ } else {
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ // or x, #0, y -> mov T1, x; mov y, T1
+ rs2 = GET_FIELDs(insn, 19, 31);
+ if (rs2 != 0) {
+ gen_movl_simm_T1(rs2);
+ gen_op_or_T1_T0();
+ }
+ } else { /* register */
+ // or x, %g0, y -> mov T1, x; mov y, T1
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2 != 0) {
+ gen_movl_reg_T1(rs2);
+ gen_op_or_T1_T0();
+ }
+ }
+ gen_movl_T0_reg(rd);
+ }
+#endif
+#ifdef TARGET_SPARC64
+ } else if (xop == 0x25) { /* sll, V9 sllx ( == sll) */
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 20, 31);
+ gen_movl_simm_T1(rs2);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_T1(rs2);
+ }
+ gen_op_sll();
+ gen_movl_T0_reg(rd);
+ } else if (xop == 0x26) { /* srl, V9 srlx */
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 20, 31);
+ gen_movl_simm_T1(rs2);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_T1(rs2);
+ }
+ if (insn & (1 << 12))
+ gen_op_srlx();
+ else
+ gen_op_srl();
+ gen_movl_T0_reg(rd);
+ } else if (xop == 0x27) { /* sra, V9 srax */
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 20, 31);
+ gen_movl_simm_T1(rs2);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_T1(rs2);
+ }
+ if (insn & (1 << 12))
+ gen_op_srax();
+ else
+ gen_op_sra();
+ gen_movl_T0_reg(rd);
+#endif
+ } else if (xop < 0x38) {
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 19, 31);
+ gen_movl_simm_T1(rs2);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_T1(rs2);
+ }
+ if (xop < 0x20) {
+ switch (xop & ~0x10) {
+ case 0x0:
+ if (xop & 0x10)
+ gen_op_add_T1_T0_cc();
+ else
+ gen_op_add_T1_T0();
+ break;
+ case 0x1:
+ gen_op_and_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x2:
+ gen_op_or_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x3:
+ gen_op_xor_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x4:
+ if (xop & 0x10)
+ gen_op_sub_T1_T0_cc();
+ else
+ gen_op_sub_T1_T0();
+ break;
+ case 0x5:
+ gen_op_andn_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x6:
+ gen_op_orn_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x7:
+ gen_op_xnor_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x8:
+ if (xop & 0x10)
+ gen_op_addx_T1_T0_cc();
+ else
+ gen_op_addx_T1_T0();
+ break;
+#ifdef TARGET_SPARC64
+ case 0x9: /* V9 mulx */
+ gen_op_mulx_T1_T0();
+ break;
+#endif
+ case 0xa:
+ gen_op_umul_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0xb:
+ gen_op_smul_T1_T0();
+ if (xop & 0x10)
+ gen_op_logic_T0_cc();
+ break;
+ case 0xc:
+ if (xop & 0x10)
+ gen_op_subx_T1_T0_cc();
+ else
+ gen_op_subx_T1_T0();
+ break;
+#ifdef TARGET_SPARC64
+ case 0xd: /* V9 udivx */
+ gen_op_udivx_T1_T0();
+ break;
+#endif
+ case 0xe:
+ gen_op_udiv_T1_T0();
+ if (xop & 0x10)
+ gen_op_div_cc();
+ break;
+ case 0xf:
+ gen_op_sdiv_T1_T0();
+ if (xop & 0x10)
+ gen_op_div_cc();
+ break;
+ default:
+ goto illegal_insn;
+ }
+ gen_movl_T0_reg(rd);
+ } else {
+ switch (xop) {
+ case 0x20: /* taddcc */
+ case 0x21: /* tsubcc */
+ case 0x22: /* taddcctv */
+ case 0x23: /* tsubcctv */
+ goto illegal_insn;
+ case 0x24: /* mulscc */
+ gen_op_mulscc_T1_T0();
+ gen_movl_T0_reg(rd);
+ break;
+#ifndef TARGET_SPARC64
+ case 0x25: /* sll */
+ gen_op_sll();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x26: /* srl */
+ gen_op_srl();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x27: /* sra */
+ gen_op_sra();
+ gen_movl_T0_reg(rd);
+ break;
+#endif
+ case 0x30:
+ {
+ switch(rd) {
+ case 0: /* wry */
+ gen_op_xor_T1_T0();
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, y));
+ break;
+#ifdef TARGET_SPARC64
+ case 0x2: /* V9 wrccr */
+ gen_op_wrccr();
+ break;
+ case 0x3: /* V9 wrasi */
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, asi));
+ break;
+ case 0x6: /* V9 wrfprs */
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, fprs));
+ break;
+ case 0xf: /* V9 sir, nop if user */
+#if !defined(CONFIG_USER_ONLY)
+ if (supervisor(dc))
+ gen_op_sir();
+#endif
+ break;
+ case 0x13: /* Graphics Status */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, gsr));
+ break;
+ case 0x17: /* Tick compare */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, tick_cmpr));
+ break;
+ case 0x18: /* System tick */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, stick_cmpr));
+ break;
+ case 0x19: /* System tick compare */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, stick_cmpr));
+ break;
+
+ case 0x10: /* Performance Control */
+ case 0x11: /* Performance Instrumentation Counter */
+ case 0x12: /* Dispatch Control */
+ case 0x14: /* Softint set */
+ case 0x15: /* Softint clear */
+ case 0x16: /* Softint write */
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+#if !defined(CONFIG_USER_ONLY)
+ case 0x31: /* wrpsr, V9 saved, restored */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+#ifdef TARGET_SPARC64
+ switch (rd) {
+ case 0:
+ gen_op_saved();
+ break;
+ case 1:
+ gen_op_restored();
+ break;
+ default:
+ goto illegal_insn;
+ }
+#else
+ gen_op_xor_T1_T0();
+ gen_op_wrpsr();
+ save_state(dc);
+ gen_op_next_insn();
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ dc->is_br = 1;
+#endif
+ }
+ break;
+ case 0x32: /* wrwim, V9 wrpr */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_op_xor_T1_T0();
+#ifdef TARGET_SPARC64
+ switch (rd) {
+ case 0: // tpc
+ gen_op_wrtpc();
+ break;
+ case 1: // tnpc
+ gen_op_wrtnpc();
+ break;
+ case 2: // tstate
+ gen_op_wrtstate();
+ break;
+ case 3: // tt
+ gen_op_wrtt();
+ break;
+ case 4: // tick
+ gen_op_wrtick();
+ break;
+ case 5: // tba
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, tbr));
+ break;
+ case 6: // pstate
+ gen_op_wrpstate();
+ save_state(dc);
+ gen_op_next_insn();
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ dc->is_br = 1;
+ break;
+ case 7: // tl
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, tl));
+ break;
+ case 8: // pil
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, psrpil));
+ break;
+ case 9: // cwp
+ gen_op_wrcwp();
+ break;
+ case 10: // cansave
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, cansave));
+ break;
+ case 11: // canrestore
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, canrestore));
+ break;
+ case 12: // cleanwin
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, cleanwin));
+ break;
+ case 13: // otherwin
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, otherwin));
+ break;
+ case 14: // wstate
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, wstate));
+ break;
+ default:
+ goto illegal_insn;
+ }
+#else
+ gen_op_movl_env_T0(offsetof(CPUSPARCState, wim));
+#endif
+ }
+ break;
+#ifndef TARGET_SPARC64
+ case 0x33: /* wrtbr, V9 unimp */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_op_xor_T1_T0();
+ gen_op_movtl_env_T0(offsetof(CPUSPARCState, tbr));
+ }
+ break;
+#endif
+#endif
+#ifdef TARGET_SPARC64
+ case 0x2c: /* V9 movcc */
+ {
+ int cc = GET_FIELD_SP(insn, 11, 12);
+ int cond = GET_FIELD_SP(insn, 14, 17);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELD_SPs(insn, 0, 10);
+ gen_movl_simm_T1(rs2);
+ }
+ else {
+ rs2 = GET_FIELD_SP(insn, 0, 4);
+ gen_movl_reg_T1(rs2);
+ }
+ gen_movl_reg_T0(rd);
+ flush_T2(dc);
+ if (insn & (1 << 18)) {
+ if (cc == 0)
+ gen_cond[0][cond]();
+ else if (cc == 2)
+ gen_cond[1][cond]();
+ else
+ goto illegal_insn;
+ } else {
+ gen_fcond[cc][cond]();
+ }
+ gen_op_mov_cc();
+ gen_movl_T0_reg(rd);
+ break;
+ }
+ case 0x2d: /* V9 sdivx */
+ gen_op_sdivx_T1_T0();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x2e: /* V9 popc */
+ {
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELD_SPs(insn, 0, 12);
+ gen_movl_simm_T1(rs2);
+ // XXX optimize: popc(constant)
+ }
+ else {
+ rs2 = GET_FIELD_SP(insn, 0, 4);
+ gen_movl_reg_T1(rs2);
+ }
+ gen_op_popc();
+ gen_movl_T0_reg(rd);
+ }
+ case 0x2f: /* V9 movr */
+ {
+ int cond = GET_FIELD_SP(insn, 10, 12);
+ rs1 = GET_FIELD(insn, 13, 17);
+ flush_T2(dc);
+ gen_movl_reg_T0(rs1);
+ gen_cond_reg(cond);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELD_SPs(insn, 0, 10);
+ gen_movl_simm_T1(rs2);
+ }
+ else {
+ rs2 = GET_FIELD_SP(insn, 0, 4);
+ gen_movl_reg_T1(rs2);
+ }
+ gen_movl_reg_T0(rd);
+ gen_op_mov_cc();
+ gen_movl_T0_reg(rd);
+ break;
+ }
+ case 0x36: /* UltraSparc shutdown, VIS */
+ {
+ int opf = GET_FIELD_SP(insn, 5, 13);
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+
+ switch (opf) {
+ case 0x018: /* VIS I alignaddr */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ gen_movl_reg_T0(rs1);
+ gen_movl_reg_T1(rs2);
+ gen_op_alignaddr();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x01a: /* VIS I alignaddrl */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ // XXX
+ break;
+ case 0x048: /* VIS I faligndata */
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ gen_op_load_fpr_DT0(rs1);
+ gen_op_load_fpr_DT1(rs2);
+ gen_op_faligndata();
+ gen_op_store_DT0_fpr(rd);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ break;
+ }
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+#ifdef TARGET_SPARC64
+ } else if (xop == 0x39) { /* V9 return */
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 19, 31);
+#if defined(OPTIM)
+ if (rs2) {
+#endif
+ gen_movl_simm_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+#if defined(OPTIM)
+ if (rs2) {
+#endif
+ gen_movl_reg_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ }
+ gen_op_restore();
+ gen_mov_pc_npc(dc);
+ gen_op_movl_npc_T0();
+ dc->npc = DYNAMIC_PC;
+ goto jmp_insn;
+#endif
+ } else {
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 19, 31);
+#if defined(OPTIM)
+ if (rs2) {
+#endif
+ gen_movl_simm_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+#if defined(OPTIM)
+ if (rs2) {
+#endif
+ gen_movl_reg_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ }
+ switch (xop) {
+ case 0x38: /* jmpl */
+ {
+ if (rd != 0) {
+#ifdef TARGET_SPARC64
+ if (dc->pc == (uint32_t)dc->pc) {
+ gen_op_movl_T1_im(dc->pc);
+ } else {
+ gen_op_movq_T1_im64(dc->pc >> 32, dc->pc);
+ }
+#else
+ gen_op_movl_T1_im(dc->pc);
+#endif
+ gen_movl_T1_reg(rd);
+ }
+ gen_mov_pc_npc(dc);
+ gen_op_movl_npc_T0();
+ dc->npc = DYNAMIC_PC;
+ }
+ goto jmp_insn;
+#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64)
+ case 0x39: /* rett, V9 return */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_mov_pc_npc(dc);
+ gen_op_movl_npc_T0();
+ dc->npc = DYNAMIC_PC;
+ gen_op_rett();
+ }
+ goto jmp_insn;
+#endif
+ case 0x3b: /* flush */
+ gen_op_flush_T0();
+ break;
+ case 0x3c: /* save */
+ save_state(dc);
+ gen_op_save();
+ gen_movl_T0_reg(rd);
+ break;
+ case 0x3d: /* restore */
+ save_state(dc);
+ gen_op_restore();
+ gen_movl_T0_reg(rd);
+ break;
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
+ case 0x3e: /* V9 done/retry */
+ {
+ switch (rd) {
+ case 0:
+ if (!supervisor(dc))
+ goto priv_insn;
+ dc->npc = DYNAMIC_PC;
+ dc->pc = DYNAMIC_PC;
+ gen_op_done();
+ goto jmp_insn;
+ case 1:
+ if (!supervisor(dc))
+ goto priv_insn;
+ dc->npc = DYNAMIC_PC;
+ dc->pc = DYNAMIC_PC;
+ gen_op_retry();
+ goto jmp_insn;
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+ }
+ break;
+ case 3: /* load/store instructions */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 12);
+ rs1 = GET_FIELD(insn, 13, 17);
+ gen_movl_reg_T0(rs1);
+ if (IS_IMM) { /* immediate */
+ rs2 = GET_FIELDs(insn, 19, 31);
+#if defined(OPTIM)
+ if (rs2 != 0) {
+#endif
+ gen_movl_simm_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+#if defined(OPTIM)
+ if (rs2 != 0) {
+#endif
+ gen_movl_reg_T1(rs2);
+ gen_op_add_T1_T0();
+#if defined(OPTIM)
+ }
+#endif
+ }
+ if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || \
+ (xop > 0x17 && xop < 0x1d ) || \
+ (xop > 0x2c && xop < 0x33) || xop == 0x1f) {
+ switch (xop) {
+ case 0x0: /* load word */
+ gen_op_ldst(ld);
+ break;
+ case 0x1: /* load unsigned byte */
+ gen_op_ldst(ldub);
+ break;
+ case 0x2: /* load unsigned halfword */
+ gen_op_ldst(lduh);
+ break;
+ case 0x3: /* load double word */
+ gen_op_ldst(ldd);
+ gen_movl_T0_reg(rd + 1);
+ break;
+ case 0x9: /* load signed byte */
+ gen_op_ldst(ldsb);
+ break;
+ case 0xa: /* load signed halfword */
+ gen_op_ldst(ldsh);
+ break;
+ case 0xd: /* ldstub -- XXX: should be atomically */
+ gen_op_ldst(ldstub);
+ break;
+ case 0x0f: /* swap register with memory. Also atomically */
+ gen_movl_reg_T1(rd);
+ gen_op_ldst(swap);
+ break;
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+ case 0x10: /* load word alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_lda(insn, 1, 4, 0);
+ break;
+ case 0x11: /* load unsigned byte alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_lduba(insn, 1, 1, 0);
+ break;
+ case 0x12: /* load unsigned halfword alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_lduha(insn, 1, 2, 0);
+ break;
+ case 0x13: /* load double word alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_ldda(insn, 1, 8, 0);
+ gen_movl_T0_reg(rd + 1);
+ break;
+ case 0x19: /* load signed byte alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_ldsba(insn, 1, 1, 1);
+ break;
+ case 0x1a: /* load signed halfword alternate */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_ldsha(insn, 1, 2 ,1);
+ break;
+ case 0x1d: /* ldstuba -- XXX: should be atomically */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_ldstuba(insn, 1, 1, 0);
+ break;
+ case 0x1f: /* swap reg with alt. memory. Also atomically */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_movl_reg_T1(rd);
+ gen_op_swapa(insn, 1, 4, 0);
+ break;
+
+#ifndef TARGET_SPARC64
+ /* avoid warnings */
+ (void) &gen_op_stfa;
+ (void) &gen_op_stdfa;
+ (void) &gen_op_ldfa;
+ (void) &gen_op_lddfa;
+#else
+#if !defined(CONFIG_USER_ONLY)
+ (void) &gen_op_cas;
+ (void) &gen_op_casx;
+#endif
+#endif
+#endif
+#ifdef TARGET_SPARC64
+ case 0x08: /* V9 ldsw */
+ gen_op_ldst(ldsw);
+ break;
+ case 0x0b: /* V9 ldx */
+ gen_op_ldst(ldx);
+ break;
+ case 0x18: /* V9 ldswa */
+ gen_op_ldswa(insn, 1, 4, 1);
+ break;
+ case 0x1b: /* V9 ldxa */
+ gen_op_ldxa(insn, 1, 8, 0);
+ break;
+ case 0x2d: /* V9 prefetch, no effect */
+ goto skip_move;
+ case 0x30: /* V9 ldfa */
+ gen_op_ldfa(insn, 1, 8, 0); // XXX
+ break;
+ case 0x33: /* V9 lddfa */
+ gen_op_lddfa(insn, 1, 8, 0); // XXX
+
+ break;
+ case 0x3d: /* V9 prefetcha, no effect */
+ goto skip_move;
+ case 0x32: /* V9 ldqfa */
+ goto nfpu_insn;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ gen_movl_T1_reg(rd);
+#ifdef TARGET_SPARC64
+ skip_move: ;
+#endif
+ } else if (xop >= 0x20 && xop < 0x24) {
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ switch (xop) {
+ case 0x20: /* load fpreg */
+ gen_op_ldst(ldf);
+ gen_op_store_FT0_fpr(rd);
+ break;
+ case 0x21: /* load fsr */
+ gen_op_ldst(ldf);
+ gen_op_ldfsr();
+ break;
+ case 0x22: /* load quad fpreg */
+ goto nfpu_insn;
+ case 0x23: /* load double fpreg */
+ gen_op_ldst(lddf);
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || \
+ xop == 0xe || xop == 0x1e) {
+ gen_movl_reg_T1(rd);
+ switch (xop) {
+ case 0x4:
+ gen_op_ldst(st);
+ break;
+ case 0x5:
+ gen_op_ldst(stb);
+ break;
+ case 0x6:
+ gen_op_ldst(sth);
+ break;
+ case 0x7:
+ flush_T2(dc);
+ gen_movl_reg_T2(rd + 1);
+ gen_op_ldst(std);
+ break;
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+ case 0x14:
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_sta(insn, 0, 4, 0);
+ break;
+ case 0x15:
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_stba(insn, 0, 1, 0);
+ break;
+ case 0x16:
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ gen_op_stha(insn, 0, 2, 0);
+ break;
+ case 0x17:
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ flush_T2(dc);
+ gen_movl_reg_T2(rd + 1);
+ gen_op_stda(insn, 0, 8, 0);
+ break;
+#endif
+#ifdef TARGET_SPARC64
+ case 0x0e: /* V9 stx */
+ gen_op_ldst(stx);
+ break;
+ case 0x1e: /* V9 stxa */
+ gen_op_stxa(insn, 0, 8, 0); // XXX
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop > 0x23 && xop < 0x28) {
+ if (gen_trap_ifnofpu(dc))
+ goto jmp_insn;
+ switch (xop) {
+ case 0x24:
+ gen_op_load_fpr_FT0(rd);
+ gen_op_ldst(stf);
+ break;
+ case 0x25: /* stfsr, V9 stxfsr */
+ gen_op_stfsr();
+ gen_op_ldst(stf);
+ break;
+ case 0x26: /* stdfq */
+ goto nfpu_insn;
+ case 0x27:
+ gen_op_load_fpr_DT0(DFPREG(rd));
+ gen_op_ldst(stdf);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop > 0x33 && xop < 0x3f) {
+#ifdef TARGET_SPARC64
+ switch (xop) {
+ case 0x34: /* V9 stfa */
+ gen_op_stfa(insn, 0, 0, 0); // XXX
+ break;
+ case 0x37: /* V9 stdfa */
+ gen_op_stdfa(insn, 0, 0, 0); // XXX
+ break;
+ case 0x3c: /* V9 casa */
+ gen_op_casa(insn, 0, 4, 0); // XXX
+ break;
+ case 0x3e: /* V9 casxa */
+ gen_op_casxa(insn, 0, 8, 0); // XXX
+ break;
+ case 0x36: /* V9 stqfa */
+ goto nfpu_insn;
+ default:
+ goto illegal_insn;
+ }
+#else
+ goto illegal_insn;
+#endif
+ }
+ else
+ goto illegal_insn;
+ }
+ break;
+ }
+ /* default case for non jump instructions */
+ if (dc->npc == DYNAMIC_PC) {
+ dc->pc = DYNAMIC_PC;
+ gen_op_next_insn();
+ } else if (dc->npc == JUMP_PC) {
+ /* we can do a static jump */
+ gen_branch2(dc, (long)dc->tb, dc->jump_pc[0], dc->jump_pc[1]);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->npc + 4;
+ }
+ jmp_insn:
+ return;
+ illegal_insn:
+ save_state(dc);
+ gen_op_exception(TT_ILL_INSN);
+ dc->is_br = 1;
+ return;
+#if !defined(CONFIG_USER_ONLY)
+ priv_insn:
+ save_state(dc);
+ gen_op_exception(TT_PRIV_INSN);
+ dc->is_br = 1;
+ return;
+#endif
+ nfpu_insn:
+ save_state(dc);
+ gen_op_fpexception_im(FSR_FTT_UNIMPFPOP);
+ dc->is_br = 1;
+}
+
+static inline int gen_intermediate_code_internal(TranslationBlock * tb,
+ int spc, CPUSPARCState *env)
+{
+ target_ulong pc_start, last_pc;
+ uint16_t *gen_opc_end;
+ DisasContext dc1, *dc = &dc1;
+ int j, lj = -1;
+
+ memset(dc, 0, sizeof(DisasContext));
+ dc->tb = tb;
+ pc_start = tb->pc;
+ dc->pc = pc_start;
+ last_pc = dc->pc;
+ dc->npc = (target_ulong) tb->cs_base;
+#if defined(CONFIG_USER_ONLY)
+ dc->mem_idx = 0;
+ dc->fpu_enabled = 1;
+#else
+ dc->mem_idx = ((env->psrs) != 0);
+#ifdef TARGET_SPARC64
+ dc->fpu_enabled = (((env->pstate & PS_PEF) != 0) && ((env->fprs & FPRS_FEF) != 0));
+#else
+ dc->fpu_enabled = ((env->psref) != 0);
+#endif
+#endif
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ nb_gen_labels = 0;
+
+ do {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == dc->pc) {
+ if (dc->pc != pc_start)
+ save_state(dc);
+ gen_op_debug();
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ dc->is_br = 1;
+ goto exit_gen_loop;
+ }
+ }
+ }
+ if (spc) {
+ if (loglevel > 0)
+ fprintf(logfile, "Search PC...\n");
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ gen_opc_pc[lj] = dc->pc;
+ gen_opc_npc[lj] = dc->npc;
+ gen_opc_instr_start[lj] = 1;
+ }
+ }
+ last_pc = dc->pc;
+ disas_sparc_insn(dc);
+
+ if (dc->is_br)
+ break;
+ /* if the next PC is different, we abort now */
+ if (dc->pc != (last_pc + 4))
+ break;
+ /* if we reach a page boundary, we stop generation so that the
+ PC of a TT_TFAULT exception is always in the right page */
+ if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+ /* if single step mode, we generate only one instruction and
+ generate an exception */
+ if (env->singlestep_enabled) {
+ gen_jmp_im(dc->pc);
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ break;
+ }
+ } while ((gen_opc_ptr < gen_opc_end) &&
+ (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32));
+
+ exit_gen_loop:
+ if (!dc->is_br) {
+ if (dc->pc != DYNAMIC_PC &&
+ (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) {
+ /* static PC and NPC: we can use direct chaining */
+ gen_branch(dc, (long)tb, dc->pc, dc->npc);
+ } else {
+ if (dc->pc != DYNAMIC_PC)
+ gen_jmp_im(dc->pc);
+ save_npc(dc);
+ gen_op_movl_T0_0();
+ gen_op_exit_tb();
+ }
+ }
+ *gen_opc_ptr = INDEX_op_end;
+ if (spc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ tb->size = 0;
+#if 0
+ if (loglevel > 0) {
+ page_dump(logfile);
+ }
+#endif
+ gen_opc_jump_pc[0] = dc->jump_pc[0];
+ gen_opc_jump_pc[1] = dc->jump_pc[1];
+ } else {
+ tb->size = last_pc + 4 - pc_start;
+ }
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "--------------\n");
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+ target_disas(logfile, pc_start, last_pc + 4 - pc_start, 0);
+ fprintf(logfile, "\n");
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+ }
+#endif
+ return 0;
+}
+
+int gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb)
+{
+ return gen_intermediate_code_internal(tb, 0, env);
+}
+
+int gen_intermediate_code_pc(CPUSPARCState * env, TranslationBlock * tb)
+{
+ return gen_intermediate_code_internal(tb, 1, env);
+}
+
+extern int ram_size;
+
+void cpu_reset(CPUSPARCState *env)
+{
+ memset(env, 0, sizeof(*env));
+ tlb_flush(env, 1);
+ env->cwp = 0;
+ env->wim = 1;
+ env->regwptr = env->regbase + (env->cwp * 16);
+#if defined(CONFIG_USER_ONLY)
+ env->user_mode_only = 1;
+#ifdef TARGET_SPARC64
+ env->cleanwin = NWINDOWS - 1;
+ env->cansave = NWINDOWS - 1;
+#endif
+#else
+ env->psrs = 1;
+ env->psrps = 1;
+ env->gregs[1] = ram_size;
+#ifdef TARGET_SPARC64
+ env->pstate = PS_PRIV;
+ env->version = GET_VER(env);
+ env->pc = 0x1fff0000000ULL;
+#else
+ env->mmuregs[0] = (0x04 << 24); /* Impl 0, ver 4, MMU disabled */
+ env->pc = 0xffd00000;
+#endif
+ env->npc = env->pc + 4;
+#endif
+}
+
+CPUSPARCState *cpu_sparc_init(void)
+{
+ CPUSPARCState *env;
+
+ env = qemu_mallocz(sizeof(CPUSPARCState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_reset(env);
+ return (env);
+}
+
+#define GET_FLAG(a,b) ((env->psr & a)?b:'-')
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ int i, x;
+
+ cpu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc, env->npc);
+ cpu_fprintf(f, "General Registers:\n");
+ for (i = 0; i < 4; i++)
+ cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]);
+ cpu_fprintf(f, "\n");
+ for (; i < 8; i++)
+ cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]);
+ cpu_fprintf(f, "\nCurrent Register Window:\n");
+ for (x = 0; x < 3; x++) {
+ for (i = 0; i < 4; i++)
+ cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t",
+ (x == 0 ? 'o' : (x == 1 ? 'l' : 'i')), i,
+ env->regwptr[i + x * 8]);
+ cpu_fprintf(f, "\n");
+ for (; i < 8; i++)
+ cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t",
+ (x == 0 ? 'o' : x == 1 ? 'l' : 'i'), i,
+ env->regwptr[i + x * 8]);
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "\nFloating Point Registers:\n");
+ for (i = 0; i < 32; i++) {
+ if ((i & 3) == 0)
+ cpu_fprintf(f, "%%f%02d:", i);
+ cpu_fprintf(f, " %016lf", env->fpr[i]);
+ if ((i & 3) == 3)
+ cpu_fprintf(f, "\n");
+ }
+#ifdef TARGET_SPARC64
+ cpu_fprintf(f, "pstate: 0x%08x ccr: 0x%02x asi: 0x%02x tl: %d\n",
+ env->pstate, GET_CCR(env), env->asi, env->tl);
+ cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate %d cleanwin %d cwp %d\n",
+ env->cansave, env->canrestore, env->otherwin, env->wstate,
+ env->cleanwin, NWINDOWS - 1 - env->cwp);
+#else
+ cpu_fprintf(f, "psr: 0x%08x -> %c%c%c%c %c%c%c wim: 0x%08x\n", GET_PSR(env),
+ GET_FLAG(PSR_ZERO, 'Z'), GET_FLAG(PSR_OVF, 'V'),
+ GET_FLAG(PSR_NEG, 'N'), GET_FLAG(PSR_CARRY, 'C'),
+ env->psrs?'S':'-', env->psrps?'P':'-',
+ env->psret?'E':'-', env->wim);
+#endif
+ cpu_fprintf(f, "fsr: 0x%08x\n", GET_FSR32(env));
+}
+
+#if defined(CONFIG_USER_ONLY)
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+
+#else
+extern int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+ int *access_index, target_ulong address, int rw,
+ int is_user);
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ target_phys_addr_t phys_addr;
+ int prot, access_index;
+
+ if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, 0) != 0)
+ if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 0, 0) != 0)
+ return -1;
+ return phys_addr;
+}
+#endif
+
+void helper_flush(target_ulong addr)
+{
+ addr &= ~7;
+ tb_invalidate_page_range(addr, addr + 8);
+}
diff --git a/tests/.cvsignore b/tests/.cvsignore
new file mode 100644
index 0000000..18a8298
--- /dev/null
+++ b/tests/.cvsignore
@@ -0,0 +1,23 @@
+ gmon.out
+ testsig
+ hello-i386
+ hello-arm
+ sha1.test.c
+ sha1.c
+ test-i386
+ sha1
+ testclone
+ .gdb_history
+ testthread
+ test-i386.s
+ test-i386.ref
+ sha1-i386
+ runcom
+ debug.com
+ test-i386.out
+ speed.txt
+ test-i386.ref.P3
+ pi_10.com
+ test-i386.ref.P4
+ ldso.c
+ test_path
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..d7abae6
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,93 @@
+-include ../config-host.mak
+
+CFLAGS=-Wall -O2 -g
+#CFLAGS+=-msse2
+LDFLAGS=
+
+ifeq ($(ARCH),i386)
+TESTS=linux-test testthread sha1-i386 test-i386 runcom
+endif
+ifeq ($(ARCH),x86_64)
+TESTS=test-x86_64
+endif
+TESTS+=sha1# test_path
+#TESTS+=test_path
+
+QEMU=../i386-user/qemu-i386
+
+all: $(TESTS)
+
+hello-i386: hello-i386.c
+ $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
+ strip $@
+
+testthread: testthread.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
+
+test_path: test_path.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+ ./$@ || { rm $@; exit 1; }
+
+# i386/x86_64 emulation test (test various opcodes) */
+test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
+ test-i386.h test-i386-shift.h test-i386-muldiv.h
+ $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ \
+ test-i386.c test-i386-code16.S test-i386-vm86.S -lm
+
+test-x86_64: test-i386.c \
+ test-i386.h test-i386-shift.h test-i386-muldiv.h
+ $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c -lm
+
+ifeq ($(ARCH),i386)
+test: test-i386
+ ./test-i386 > test-i386.ref
+else
+test:
+endif
+ $(QEMU) test-i386 > test-i386.out
+ @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi
+ifeq ($(ARCH),i386)
+ $(QEMU) -no-code-copy test-i386 > test-i386.out
+ @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK (no code copy)"; fi
+endif
+
+# generic Linux and CPU test
+linux-test: linux-test.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm
+
+# speed test
+sha1-i386: sha1.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+sha1: sha1.c
+ $(HOST_CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+speed: sha1 sha1-i386
+ time ./sha1
+ time $(QEMU) ./sha1-i386
+
+# vm86 test
+runcom: runcom.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+# NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu
+qruncom: qruncom.c ../i386-user/libqemu.a
+ $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \
+ -o $@ $< -L../i386-user -lqemu -lm
+
+# arm test
+hello-arm: hello-arm.o
+ arm-linux-ld -o $@ $<
+
+hello-arm.o: hello-arm.c
+ arm-linux-gcc -Wall -g -O2 -c -o $@ $<
+
+# XXX: find a way to compile easily a test for each arch
+test2:
+ @for arch in i386 arm armeb sparc ppc mips mipsel; do \
+ ../$${arch}-user/qemu-$${arch} $${arch}/ls -l linux-test.c ; \
+ done
+
+clean:
+ rm -f *~ *.o test-i386.out test-i386.ref \
+ test-x86_64.log test-x86_64.ref qruncom $(TESTS)
diff --git a/tests/hello-arm.c b/tests/hello-arm.c
new file mode 100644
index 0000000..f84e6cb
--- /dev/null
+++ b/tests/hello-arm.c
@@ -0,0 +1,113 @@
+#define __NR_SYSCALL_BASE 0x900000
+#define __NR_exit1 (__NR_SYSCALL_BASE+ 1)
+#define __NR_write (__NR_SYSCALL_BASE+ 4)
+
+#define __sys2(x) #x
+#define __sys1(x) __sys2(x)
+
+#ifndef __syscall
+#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t"
+#endif
+
+#define __syscall_return(type, res) \
+do { \
+ return (type) (res); \
+} while (0)
+
+#define _syscall0(type,name) \
+type name(void) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ __syscall(name) \
+ "mov %0,r0" \
+ :"=r" (__res) : : "r0","lr"); \
+ __syscall_return(type,__res); \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ __syscall(name) \
+ "mov %0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)) \
+ : "r0","lr"); \
+ __syscall_return(type,__res); \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)) \
+ : "r0","r1","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \
+ : "r0","r1","r2","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ "mov\tr3,%4\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)) \
+ : "r0","r1","r2","r3","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ "mov\tr3,%4\n\t" \
+ "mov\tr4,%5\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)), \
+ "r" ((long)(arg5)) \
+ : "r0","r1","r2","r3","r4","lr"); \
+ __syscall_return(type,__res); \
+}
+
+_syscall1(int,exit1,int,status);
+_syscall3(int,write,int,fd,const char *,buf, int, len);
+
+void _start(void)
+{
+ write(1, "Hello World\n", 12);
+ exit1(0);
+}
diff --git a/tests/hello-i386.c b/tests/hello-i386.c
new file mode 100644
index 0000000..e00245d
--- /dev/null
+++ b/tests/hello-i386.c
@@ -0,0 +1,26 @@
+#include <asm/unistd.h>
+
+extern inline volatile void exit(int status)
+{
+ int __res;
+ __asm__ volatile ("movl %%ecx,%%ebx\n"\
+ "int $0x80" \
+ : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status)));
+}
+
+extern inline int write(int fd, const char * buf, int len)
+{
+ int status;
+ __asm__ volatile ("pushl %%ebx\n"\
+ "movl %%esi,%%ebx\n"\
+ "int $0x80\n" \
+ "popl %%ebx\n"\
+ : "=a" (status) \
+ : "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len)));
+}
+
+void _start(void)
+{
+ write(1, "Hello World\n", 12);
+ exit(0);
+}
diff --git a/tests/linux-test.c b/tests/linux-test.c
new file mode 100644
index 0000000..6ca9029
--- /dev/null
+++ b/tests/linux-test.c
@@ -0,0 +1,536 @@
+/*
+ * linux and CPU test
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <utime.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sched.h>
+#include <dirent.h>
+#include <setjmp.h>
+#include <sys/shm.h>
+
+#define TESTPATH "/tmp/linux-test.tmp"
+#define TESTPORT 7654
+#define STACK_SIZE 16384
+
+void error1(const char *filename, int line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", filename, line);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+int __chk_error(const char *filename, int line, int ret)
+{
+ if (ret < 0) {
+ error1(filename, line, "%m (ret=%d, errno=%d)",
+ ret, errno);
+ }
+ return ret;
+}
+
+#define error(fmt, args...) error1(__FILE__, __LINE__, fmt, ##args)
+
+#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret))
+
+/*******************************************************/
+
+#define FILE_BUF_SIZE 300
+
+void test_file(void)
+{
+ int fd, i, len, ret;
+ uint8_t buf[FILE_BUF_SIZE];
+ uint8_t buf2[FILE_BUF_SIZE];
+ uint8_t buf3[FILE_BUF_SIZE];
+ char cur_dir[1024];
+ struct stat st;
+ struct utimbuf tbuf;
+ struct iovec vecs[2];
+ DIR *dir;
+ struct dirent *de;
+
+ /* clean up, just in case */
+ unlink(TESTPATH "/file1");
+ unlink(TESTPATH "/file2");
+ unlink(TESTPATH "/file3");
+ rmdir(TESTPATH);
+
+ if (getcwd(cur_dir, sizeof(cur_dir)) == NULL)
+ error("getcwd");
+
+ chk_error(mkdir(TESTPATH, 0755));
+
+ chk_error(chdir(TESTPATH));
+
+ /* open/read/write/close/readv/writev/lseek */
+
+ fd = chk_error(open("file1", O_WRONLY | O_TRUNC | O_CREAT, 0644));
+ for(i=0;i < FILE_BUF_SIZE; i++)
+ buf[i] = i;
+ len = chk_error(write(fd, buf, FILE_BUF_SIZE / 2));
+ if (len != (FILE_BUF_SIZE / 2))
+ error("write");
+ vecs[0].iov_base = buf + (FILE_BUF_SIZE / 2);
+ vecs[0].iov_len = 16;
+ vecs[1].iov_base = buf + (FILE_BUF_SIZE / 2) + 16;
+ vecs[1].iov_len = (FILE_BUF_SIZE / 2) - 16;
+ len = chk_error(writev(fd, vecs, 2));
+ if (len != (FILE_BUF_SIZE / 2))
+ error("writev");
+ chk_error(close(fd));
+
+ chk_error(rename("file1", "file2"));
+
+ fd = chk_error(open("file2", O_RDONLY));
+
+ len = chk_error(read(fd, buf2, FILE_BUF_SIZE));
+ if (len != FILE_BUF_SIZE)
+ error("read");
+ if (memcmp(buf, buf2, FILE_BUF_SIZE) != 0)
+ error("memcmp");
+
+#define FOFFSET 16
+ ret = chk_error(lseek(fd, FOFFSET, SEEK_SET));
+ if (ret != 16)
+ error("lseek");
+ vecs[0].iov_base = buf3;
+ vecs[0].iov_len = 32;
+ vecs[1].iov_base = buf3 + 32;
+ vecs[1].iov_len = FILE_BUF_SIZE - FOFFSET - 32;
+ len = chk_error(readv(fd, vecs, 2));
+ if (len != FILE_BUF_SIZE - FOFFSET)
+ error("readv");
+ if (memcmp(buf + FOFFSET, buf3, FILE_BUF_SIZE - FOFFSET) != 0)
+ error("memcmp");
+
+ chk_error(close(fd));
+
+ /* access */
+ chk_error(access("file2", R_OK));
+
+ /* stat/chmod/utime/truncate */
+
+ chk_error(chmod("file2", 0600));
+ tbuf.actime = 1001;
+ tbuf.modtime = 1000;
+ chk_error(truncate("file2", 100));
+ chk_error(utime("file2", &tbuf));
+ chk_error(stat("file2", &st));
+ if (st.st_size != 100)
+ error("stat size");
+ if (!S_ISREG(st.st_mode))
+ error("stat mode");
+ if ((st.st_mode & 0777) != 0600)
+ error("stat mode2");
+ if (st.st_atime != 1001 ||
+ st.st_mtime != 1000)
+ error("stat time");
+
+ chk_error(stat(TESTPATH, &st));
+ if (!S_ISDIR(st.st_mode))
+ error("stat mode");
+
+ /* fstat */
+ fd = chk_error(open("file2", O_RDWR));
+ chk_error(ftruncate(fd, 50));
+ chk_error(fstat(fd, &st));
+ chk_error(close(fd));
+
+ if (st.st_size != 50)
+ error("stat size");
+ if (!S_ISREG(st.st_mode))
+ error("stat mode");
+
+ /* symlink/lstat */
+ chk_error(symlink("file2", "file3"));
+ chk_error(lstat("file3", &st));
+ if (!S_ISLNK(st.st_mode))
+ error("stat mode");
+
+ /* getdents */
+ dir = opendir(TESTPATH);
+ if (!dir)
+ error("opendir");
+ len = 0;
+ for(;;) {
+ de = readdir(dir);
+ if (!de)
+ break;
+ if (strcmp(de->d_name, ".") != 0 &&
+ strcmp(de->d_name, "..") != 0 &&
+ strcmp(de->d_name, "file2") != 0 &&
+ strcmp(de->d_name, "file3") != 0)
+ error("readdir");
+ len++;
+ }
+ closedir(dir);
+ if (len != 4)
+ error("readdir");
+
+ chk_error(unlink("file3"));
+ chk_error(unlink("file2"));
+ chk_error(chdir(cur_dir));
+ chk_error(rmdir(TESTPATH));
+}
+
+void test_fork(void)
+{
+ int pid, status;
+
+ pid = chk_error(fork());
+ if (pid == 0) {
+ /* child */
+ exit(2);
+ }
+ chk_error(waitpid(pid, &status, 0));
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 2)
+ error("waitpid status=0x%x", status);
+}
+
+void test_time(void)
+{
+ struct timeval tv, tv2;
+ struct timespec ts, rem;
+ struct rusage rusg1, rusg2;
+ int ti, i;
+
+ chk_error(gettimeofday(&tv, NULL));
+ rem.tv_sec = 1;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 20 * 1000000;
+ chk_error(nanosleep(&ts, &rem));
+ if (rem.tv_sec != 1)
+ error("nanosleep");
+ chk_error(gettimeofday(&tv2, NULL));
+ ti = tv2.tv_sec - tv.tv_sec;
+ if (ti >= 2)
+ error("gettimeofday");
+
+ chk_error(getrusage(RUSAGE_SELF, &rusg1));
+ for(i = 0;i < 10000; i++);
+ chk_error(getrusage(RUSAGE_SELF, &rusg2));
+ if ((rusg2.ru_utime.tv_sec - rusg1.ru_utime.tv_sec) < 0 ||
+ (rusg2.ru_stime.tv_sec - rusg1.ru_stime.tv_sec) < 0)
+ error("getrusage");
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int server_socket(void)
+{
+ int val, fd;
+ struct sockaddr_in sockaddr;
+
+ /* server socket */
+ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
+
+ val = 1;
+ chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)));
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(TESTPORT);
+ sockaddr.sin_addr.s_addr = 0;
+ chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
+ chk_error(listen(fd, 0));
+ return fd;
+
+}
+
+int client_socket(void)
+{
+ int fd;
+ struct sockaddr_in sockaddr;
+
+ /* server socket */
+ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(TESTPORT);
+ inet_aton("127.0.0.1", &sockaddr.sin_addr);
+ chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
+ return fd;
+}
+
+const char socket_msg[] = "hello socket\n";
+
+void test_socket(void)
+{
+ int server_fd, client_fd, fd, pid, ret, val;
+ struct sockaddr_in sockaddr;
+ socklen_t len;
+ char buf[512];
+
+ server_fd = server_socket();
+
+ /* test a few socket options */
+ len = sizeof(val);
+ chk_error(getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &val, &len));
+ if (val != SOCK_STREAM)
+ error("getsockopt");
+
+ pid = chk_error(fork());
+ if (pid == 0) {
+ client_fd = client_socket();
+ send(client_fd, socket_msg, sizeof(socket_msg), 0);
+ close(client_fd);
+ exit(0);
+ }
+ len = sizeof(sockaddr);
+ fd = chk_error(accept(server_fd, (struct sockaddr *)&sockaddr, &len));
+
+ ret = chk_error(recv(fd, buf, sizeof(buf), 0));
+ if (ret != sizeof(socket_msg))
+ error("recv");
+ if (memcmp(buf, socket_msg, sizeof(socket_msg)) != 0)
+ error("socket_msg");
+ chk_error(close(fd));
+ chk_error(close(server_fd));
+}
+
+#define WCOUNT_MAX 512
+
+void test_pipe(void)
+{
+ fd_set rfds, wfds;
+ int fds[2], fd_max, ret;
+ uint8_t ch;
+ int wcount, rcount;
+
+ chk_error(pipe(fds));
+ chk_error(fcntl(fds[0], F_SETFL, O_NONBLOCK));
+ chk_error(fcntl(fds[1], F_SETFL, O_NONBLOCK));
+ wcount = 0;
+ rcount = 0;
+ for(;;) {
+ FD_ZERO(&rfds);
+ fd_max = fds[0];
+ FD_SET(fds[0], &rfds);
+
+ FD_ZERO(&wfds);
+ FD_SET(fds[1], &wfds);
+ if (fds[1] > fd_max)
+ fd_max = fds[1];
+
+ ret = chk_error(select(fd_max + 1, &rfds, &wfds, NULL, NULL));
+ if (ret > 0) {
+ if (FD_ISSET(fds[0], &rfds)) {
+ chk_error(read(fds[0], &ch, 1));
+ rcount++;
+ if (rcount >= WCOUNT_MAX)
+ break;
+ }
+ if (FD_ISSET(fds[1], &wfds)) {
+ ch = 'a';
+ chk_error(write(fds[0], &ch, 1));
+ wcount++;
+ }
+ }
+ }
+ chk_error(close(fds[0]));
+ chk_error(close(fds[1]));
+}
+
+int thread1_res;
+int thread2_res;
+
+int thread1_func(void *arg)
+{
+ int i;
+ for(i=0;i<5;i++) {
+ thread1_res++;
+ usleep(10 * 1000);
+ }
+ return 0;
+}
+
+int thread2_func(void *arg)
+{
+ int i;
+ for(i=0;i<6;i++) {
+ thread2_res++;
+ usleep(10 * 1000);
+ }
+ return 0;
+}
+
+void test_clone(void)
+{
+ uint8_t *stack1, *stack2;
+ int pid1, pid2, status1, status2;
+
+ stack1 = malloc(STACK_SIZE);
+ pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE,
+ CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1"));
+
+ stack2 = malloc(STACK_SIZE);
+ pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE,
+ CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2"));
+
+ while (waitpid(pid1, &status1, 0) != pid1);
+ while (waitpid(pid2, &status2, 0) != pid2);
+ if (thread1_res != 5 ||
+ thread2_res != 6)
+ error("clone");
+}
+
+/***********************************/
+
+volatile int alarm_count;
+jmp_buf jmp_env;
+
+void sig_alarm(int sig)
+{
+ if (sig != SIGALRM)
+ error("signal");
+ alarm_count++;
+}
+
+void sig_segv(int sig, siginfo_t *info, void *puc)
+{
+ if (sig != SIGSEGV)
+ error("signal");
+ longjmp(jmp_env, 1);
+}
+
+void test_signal(void)
+{
+ struct sigaction act;
+ struct itimerval it, oit;
+
+ /* timer test */
+
+ alarm_count = 0;
+
+ act.sa_handler = sig_alarm;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ chk_error(sigaction(SIGALRM, &act, NULL));
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 10 * 1000;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 10 * 1000;
+ chk_error(setitimer(ITIMER_REAL, &it, NULL));
+ chk_error(getitimer(ITIMER_REAL, &oit));
+ if (oit.it_value.tv_sec != it.it_value.tv_sec ||
+ oit.it_value.tv_usec != it.it_value.tv_usec)
+ error("itimer");
+
+ while (alarm_count < 5) {
+ usleep(10 * 1000);
+ }
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 0;
+ memset(&oit, 0xff, sizeof(oit));
+ chk_error(setitimer(ITIMER_REAL, &it, &oit));
+ if (oit.it_value.tv_sec != 0 ||
+ oit.it_value.tv_usec != 10 * 1000)
+ error("setitimer");
+
+ /* SIGSEGV test */
+ act.sa_sigaction = sig_segv;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+ if (setjmp(jmp_env) == 0) {
+ *(uint8_t *)0 = 0;
+ }
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+}
+
+#define SHM_SIZE 32768
+
+void test_shm(void)
+{
+ void *ptr;
+ int shmid;
+
+ shmid = chk_error(shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0777));
+ ptr = shmat(shmid, NULL, 0);
+ if (!ptr)
+ error("shmat");
+
+ memset(ptr, 0, SHM_SIZE);
+
+ chk_error(shmctl(shmid, IPC_RMID, 0));
+ chk_error(shmdt(ptr));
+}
+
+int main(int argc, char **argv)
+{
+ test_file();
+ test_fork();
+ test_time();
+ test_socket();
+ // test_clone();
+ test_signal();
+ test_shm();
+ return 0;
+}
diff --git a/tests/pi_10.com b/tests/pi_10.com
new file mode 100644
index 0000000..8993ba1
--- /dev/null
+++ b/tests/pi_10.com
Binary files differ
diff --git a/tests/qruncom.c b/tests/qruncom.c
new file mode 100644
index 0000000..421e6a9
--- /dev/null
+++ b/tests/qruncom.c
@@ -0,0 +1,327 @@
+/*
+ * Example of use of user mode libqemu: launch a basic .com DOS
+ * executable
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <malloc.h>
+
+#include "cpu.h"
+
+//#define SIGTEST
+
+void cpu_outb(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
+}
+
+void cpu_outw(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
+}
+
+void cpu_outl(CPUState *env, int addr, int val)
+{
+ fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
+}
+
+int cpu_inb(CPUState *env, int addr)
+{
+ fprintf(stderr, "inb: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_inw(CPUState *env, int addr)
+{
+ fprintf(stderr, "inw: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_inl(CPUState *env, int addr)
+{
+ fprintf(stderr, "inl: port=0x%04x\n", addr);
+ return 0;
+}
+
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+
+uint64_t cpu_get_tsc(CPUState *env)
+{
+ return 0;
+}
+
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ unsigned long addr, unsigned int sel)
+{
+ unsigned int e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ stl((uint8_t *)ptr, e1);
+ stl((uint8_t *)ptr + 4, e2);
+}
+
+uint64_t idt_table[256];
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+
+void qemu_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *qemu_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ ptr = qemu_malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void *qemu_vmalloc(size_t size)
+{
+ return memalign(4096, size);
+}
+
+void qemu_vfree(void *ptr)
+{
+ free(ptr);
+}
+
+void qemu_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/* XXX: this is a bug in helper2.c */
+int errno;
+
+/**********************************************/
+
+#define COM_BASE_ADDR 0x10100
+
+void usage(void)
+{
+ printf("qruncom version 0.1 (c) 2003 Fabrice Bellard\n"
+ "usage: qruncom file.com\n"
+ "user mode libqemu demo: run simple .com DOS executables\n");
+ exit(1);
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(CPUState *env, int val)
+{
+ env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | ((env->regs[R_ESP] - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(env->segs[R_SS].selector, env->regs[R_ESP]) = val;
+}
+
+static void host_segv_handler(int host_signum, siginfo_t *info,
+ void *puc)
+{
+ if (cpu_signal_handler(host_signum, info, puc)) {
+ return;
+ }
+ abort();
+}
+
+int main(int argc, char **argv)
+{
+ uint8_t *vm86_mem;
+ const char *filename;
+ int fd, ret, seg;
+ CPUState *env;
+
+ if (argc != 2)
+ usage();
+ filename = argv[1];
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ /* load the MSDOS .com executable */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ exit(1);
+ }
+ ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256);
+ if (ret < 0) {
+ perror("read");
+ exit(1);
+ }
+ close(fd);
+
+ /* install exception handler for CPU emulator */
+ {
+ struct sigaction act;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ // act.sa_flags |= SA_ONSTACK;
+
+ act.sa_sigaction = host_segv_handler;
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+#if defined (TARGET_I386) && defined(USE_CODE_COPY)
+ sigaction(SIGFPE, &act, NULL);
+#endif
+ }
+
+ // cpu_set_log(CPU_LOG_TB_IN_ASM | CPU_LOG_TB_OUT_ASM | CPU_LOG_EXEC);
+
+ env = cpu_init();
+
+ /* disable code copy to simplify debugging */
+ code_copy_enabled = 0;
+
+ /* set user mode state (XXX: should be done automatically by
+ cpu_init ?) */
+ env->user_mode_only = 1;
+
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ /* NOTE: hflags duplicates some of the virtual CPU state */
+ env->hflags |= HF_PE_MASK | VM_MASK;
+
+ /* flags setup : we activate the IRQs by default as in user
+ mode. We also activate the VM86 flag to run DOS code */
+ env->eflags |= IF_MASK | VM_MASK;
+
+ /* init basic registers */
+ env->eip = 0x100;
+ env->regs[R_ESP] = 0xfffe;
+ seg = (COM_BASE_ADDR - 0x100) >> 4;
+
+ cpu_x86_load_seg_cache(env, R_CS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_SS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_DS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_ES, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_FS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_GS, seg,
+ (seg << 4), 0xffff, 0);
+
+ /* exception support */
+ env->idt.base = (unsigned long)idt_table;
+ env->idt.limit = sizeof(idt_table) - 1;
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 3);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+
+ /* put return code */
+ *seg_to_linear(env->segs[R_CS].selector, 0) = 0xb4; /* mov ah, $0 */
+ *seg_to_linear(env->segs[R_CS].selector, 1) = 0x00;
+ *seg_to_linear(env->segs[R_CS].selector, 2) = 0xcd; /* int $0x21 */
+ *seg_to_linear(env->segs[R_CS].selector, 3) = 0x21;
+ pushw(env, 0x0000);
+
+ /* the value of these registers seem to be assumed by pi_10.com */
+ env->regs[R_ESI] = 0x100;
+ env->regs[R_ECX] = 0xff;
+ env->regs[R_EBP] = 0x0900;
+ env->regs[R_EDI] = 0xfffe;
+
+ /* inform the emulator of the mmaped memory */
+ page_set_flags(0x00000000, 0x110000,
+ PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID);
+
+ for(;;) {
+ ret = cpu_x86_exec(env);
+ switch(ret) {
+ case EXCP0D_GPF:
+ {
+ int int_num, ah;
+ int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (env->regs[R_EAX] >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ exit(0);
+ case 0x02: /* write char */
+ {
+ uint8_t c = env->regs[R_EDX];
+ write(1, &c, 1);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c;
+ for(;;) {
+ c = *seg_to_linear(env->segs[R_DS].selector, env->regs[R_EAX]);
+ if (c == '$')
+ break;
+ write(1, &c, 1);
+ }
+ env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | '$';
+ }
+ break;
+ default:
+ unknown_int:
+ fprintf(stderr, "unsupported int 0x%02x\n", int_num);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ // exit(1);
+ }
+ env->eip += 2;
+ }
+ break;
+ default:
+ fprintf(stderr, "unhandled cpu_exec return code (0x%x)\n", ret);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit(1);
+ }
+ }
+}
diff --git a/tests/runcom.c b/tests/runcom.c
new file mode 100644
index 0000000..43deeca
--- /dev/null
+++ b/tests/runcom.c
@@ -0,0 +1,195 @@
+/*
+ * Simple example of use of vm86: launch a basic .com DOS executable
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#include <linux/unistd.h>
+#include <asm/vm86.h>
+
+//#define SIGTEST
+
+#undef __syscall_return
+#define __syscall_return(type, res) \
+do { \
+ return (type) (res); \
+} while (0)
+
+_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
+
+#define COM_BASE_ADDR 0x10100
+
+void usage(void)
+{
+ printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n"
+ "usage: runcom file.com\n"
+ "VM86 Run simple .com DOS executables (linux vm86 test mode)\n");
+ exit(1);
+}
+
+static inline void set_bit(uint8_t *a, unsigned int bit)
+{
+ a[bit / 8] |= (1 << (bit % 8));
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(struct vm86_regs *r, int val)
+{
+ r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
+}
+
+void dump_regs(struct vm86_regs *r)
+{
+ fprintf(stderr,
+ "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
+ "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
+ "EIP=%08lx EFL=%08lx\n"
+ "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n",
+ r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
+ r->eip, r->eflags,
+ r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
+}
+
+#ifdef SIGTEST
+void alarm_handler(int sig)
+{
+ fprintf(stderr, "alarm signal=%d\n", sig);
+ alarm(1);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ uint8_t *vm86_mem;
+ const char *filename;
+ int fd, ret, seg;
+ struct vm86plus_struct ctx;
+ struct vm86_regs *r;
+
+ if (argc != 2)
+ usage();
+ filename = argv[1];
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+#ifdef SIGTEST
+ {
+ struct sigaction act;
+
+ act.sa_handler = alarm_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(1);
+ }
+#endif
+
+ /* load the MSDOS .com executable */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ exit(1);
+ }
+ ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256);
+ if (ret < 0) {
+ perror("read");
+ exit(1);
+ }
+ close(fd);
+
+ memset(&ctx, 0, sizeof(ctx));
+ /* init basic registers */
+ r = &ctx.regs;
+ r->eip = 0x100;
+ r->esp = 0xfffe;
+ seg = (COM_BASE_ADDR - 0x100) >> 4;
+ r->cs = seg;
+ r->ss = seg;
+ r->ds = seg;
+ r->es = seg;
+ r->fs = seg;
+ r->gs = seg;
+ r->eflags = VIF_MASK;
+
+ /* put return code */
+ set_bit((uint8_t *)&ctx.int_revectored, 0x21);
+ *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */
+ *seg_to_linear(r->cs, 1) = 0x00;
+ *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */
+ *seg_to_linear(r->cs, 3) = 0x21;
+ pushw(&ctx.regs, 0x0000);
+
+ /* the value of these registers seem to be assumed by pi_10.com */
+ r->esi = 0x100;
+ r->ecx = 0xff;
+ r->ebp = 0x0900;
+ r->edi = 0xfffe;
+
+ for(;;) {
+ ret = vm86(VM86_ENTER, &ctx);
+ switch(VM86_TYPE(ret)) {
+ case VM86_INTx:
+ {
+ int int_num, ah;
+
+ int_num = VM86_ARG(ret);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (r->eax >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ exit(0);
+ case 0x02: /* write char */
+ {
+ uint8_t c = r->edx;
+ write(1, &c, 1);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c;
+ for(;;) {
+ c = *seg_to_linear(r->ds, r->edx);
+ if (c == '$')
+ break;
+ write(1, &c, 1);
+ }
+ r->eax = (r->eax & ~0xff) | '$';
+ }
+ break;
+ default:
+ unknown_int:
+ fprintf(stderr, "unsupported int 0x%02x\n", int_num);
+ dump_regs(&ctx.regs);
+ // exit(1);
+ }
+ }
+ break;
+ case VM86_SIGNAL:
+ /* a signal came, we just ignore that */
+ break;
+ case VM86_STI:
+ break;
+ default:
+ fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
+ dump_regs(&ctx.regs);
+ exit(1);
+ }
+ }
+}
diff --git a/tests/sha1.c b/tests/sha1.c
new file mode 100644
index 0000000..31b0019
--- /dev/null
+++ b/tests/sha1.c
@@ -0,0 +1,242 @@
+
+/* from valgrind tests */
+
+/* ================ sha1.c ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#define SHA1HANDSOFF
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h> /* for u_int*_t */
+
+/* ================ sha1.h ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+typedef struct {
+ u_int32_t state[5];
+ u_int32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+/* ================ end of sha1.h ================ */
+#include <endian.h>
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#elif BYTE_ORDER == BIG_ENDIAN
+#define blk0(i) block->l[i]
+#else
+#error "Endianness not defined!"
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
+{
+u_int32_t a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ u_int32_t l[16];
+} CHAR64LONG16;
+#ifdef SHA1HANDSOFF
+CHAR64LONG16 block[1]; /* use array to appear as a pointer */
+ memcpy(block, buffer, 64);
+#else
+ /* The following had better never be used because it causes the
+ * pointer-to-const buffer to be cast into a pointer to non-const.
+ * And the result is written through. I threw a "const" in, hoping
+ * this will cause a diagnostic.
+ */
+CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
+{
+u_int32_t i;
+u_int32_t j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len>>29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+unsigned i;
+unsigned char finalcount[8];
+unsigned char c;
+
+#if 0 /* untested "improvement" by DHR */
+ /* Convert context->count to a sequence of bytes
+ * in finalcount. Second element first, but
+ * big-endian order within element.
+ * But we do it all backwards.
+ */
+ unsigned char *fcp = &finalcount[8];
+
+ for (i = 0; i < 2; i++)
+ {
+ u_int32_t t = context->count[i];
+ int j;
+
+ for (j = 0; j < 4; t >>= 8, j++)
+ *--fcp = (unsigned char) t
+ }
+#else
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+#endif
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* ================ end of sha1.c ================ */
+
+#define BUFSIZE 4096
+
+int
+main(int argc, char **argv)
+{
+ SHA1_CTX ctx;
+ unsigned char hash[20], buf[BUFSIZE];
+ int i;
+
+ for(i=0;i<BUFSIZE;i++)
+ buf[i] = i;
+
+ SHA1Init(&ctx);
+ for(i=0;i<1000;i++)
+ SHA1Update(&ctx, buf, BUFSIZE);
+ SHA1Final(hash, &ctx);
+
+ printf("SHA1=");
+ for(i=0;i<20;i++)
+ printf("%02x", hash[i]);
+ printf("\n");
+ return 0;
+}
+
+
diff --git a/tests/test-i386-code16.S b/tests/test-i386-code16.S
new file mode 100644
index 0000000..e400e73
--- /dev/null
+++ b/tests/test-i386-code16.S
@@ -0,0 +1,79 @@
+ .code16
+ .globl code16_start
+ .globl code16_end
+
+CS_SEG = 0xf
+
+code16_start:
+
+ .globl code16_func1
+
+ /* basic test */
+code16_func1 = . - code16_start
+ mov $1, %eax
+ data32 lret
+
+/* test push/pop in 16 bit mode */
+ .globl code16_func2
+code16_func2 = . - code16_start
+ xor %eax, %eax
+ mov $0x12345678, %ebx
+ movl %esp, %ecx
+ push %bx
+ subl %esp, %ecx
+ pop %ax
+ data32 lret
+
+/* test various jmp opcodes */
+ .globl code16_func3
+code16_func3 = . - code16_start
+ jmp 1f
+ nop
+1:
+ mov $4, %eax
+ mov $0x12345678, %ebx
+ xor %bx, %bx
+ jz 2f
+ add $2, %ax
+2:
+
+ call myfunc
+
+ lcall $CS_SEG, $(myfunc2 - code16_start)
+
+ ljmp $CS_SEG, $(myjmp1 - code16_start)
+myjmp1_next:
+
+ cs lcall myfunc2_addr - code16_start
+
+ cs ljmp myjmp2_addr - code16_start
+myjmp2_next:
+
+ data32 lret
+
+myfunc2_addr:
+ .short myfunc2 - code16_start
+ .short CS_SEG
+
+myjmp2_addr:
+ .short myjmp2 - code16_start
+ .short CS_SEG
+
+myjmp1:
+ add $8, %ax
+ jmp myjmp1_next
+
+myjmp2:
+ add $16, %ax
+ jmp myjmp2_next
+
+myfunc:
+ add $1, %ax
+ ret
+
+myfunc2:
+ add $4, %ax
+ lret
+
+
+code16_end:
diff --git a/tests/test-i386-muldiv.h b/tests/test-i386-muldiv.h
new file mode 100644
index 0000000..fd0d991
--- /dev/null
+++ b/tests/test-i386-muldiv.h
@@ -0,0 +1,76 @@
+
+void glue(glue(test_, OP), b)(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm ("push %4\n\t"
+ "popf\n\t"
+ stringify(OP)"b %b2\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ stringify(OP) "b", s0, s1, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), w)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "w %w3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "w", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), l)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "l %k3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "l", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void glue(glue(test_, OP), q)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "q %3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "q", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+#endif
+
+#undef OP
diff --git a/tests/test-i386-shift.h b/tests/test-i386-shift.h
new file mode 100644
index 0000000..c1ed489
--- /dev/null
+++ b/tests/test-i386-shift.h
@@ -0,0 +1,187 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#ifndef OP_SHIFTD
+
+#ifdef OP_NOBYTE
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=g" (res), "=g" (flags)\
+ : "r" (s1), "0" (res), "1" (flags));
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %%cl, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "c" (s1), "0" (res), "1" (flags));
+#endif
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("q", "", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "q", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("l", "k", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "l", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("w", "w", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "w", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %%cl, %" rsize "5, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=g" (res), "=g" (flags)\
+ : "c" (s1), "0" (res), "1" (flags), "r" (s2));
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("q", "", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "q", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("l", "k", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "l", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("w", "w", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "w", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+#endif
+
+#ifndef OP_NOBYTE
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("b", "b", res, s1, 0, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "b", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_op(long s2, long s0, long s1)
+{
+ s2 = i2l(s2);
+ s0 = i2l(s0);
+#if defined(__x86_64__)
+ exec_opq(s2, s0, s1, 0);
+#endif
+ exec_opl(s2, s0, s1, 0);
+#ifdef OP_SHIFTD
+ if (s1 <= 15)
+ exec_opw(s2, s0, s1, 0);
+#else
+ exec_opw(s2, s0, s1, 0);
+#endif
+#ifndef OP_NOBYTE
+ exec_opb(s0, s1, 0);
+#endif
+#ifdef OP_CC
+#if defined(__x86_64__)
+ exec_opq(s2, s0, s1, CC_C);
+#endif
+ exec_opl(s2, s0, s1, CC_C);
+ exec_opw(s2, s0, s1, CC_C);
+ exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+ int i, n;
+#if defined(__x86_64__)
+ n = 64;
+#else
+ n = 32;
+#endif
+ for(i = 0; i < n; i++)
+ exec_op(0x21ad3d34, 0x12345678, i);
+ for(i = 0; i < n; i++)
+ exec_op(0x813f3421, 0x82345679, i);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC
+#undef OP_SHIFTD
+#undef OP_NOBYTE
+#undef EXECSHIFT
+
diff --git a/tests/test-i386-vm86.S b/tests/test-i386-vm86.S
new file mode 100644
index 0000000..a972f1b
--- /dev/null
+++ b/tests/test-i386-vm86.S
@@ -0,0 +1,104 @@
+ .code16
+ .globl vm86_code_start
+ .globl vm86_code_end
+
+#define GET_OFFSET(x) ((x) - vm86_code_start + 0x100)
+
+vm86_code_start:
+ movw $GET_OFFSET(hello_world), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ /* prepare int 0x90 vector */
+ xorw %ax, %ax
+ movw %ax, %es
+ es movw $GET_OFFSET(int90_test), 0x90 * 4
+ es movw %cs, 0x90 * 4 + 2
+
+ /* launch int 0x90 */
+
+ int $0x90
+
+ /* test IF support */
+ movw $GET_OFFSET(IF_msg), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ cli
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ sti
+ pushfl
+ popl %edx
+ movb $0xff, %ah
+ int $0x21
+
+#if 0
+ movw $GET_OFFSET(IF_msg1), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ pushf
+ movw %sp, %bx
+ andw $~0x200, (%bx)
+ popf
+#else
+ cli
+#endif
+
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ pushfl
+ movw %sp, %bx
+ orw $0x200, (%bx)
+ popfl
+
+ pushfl
+ popl %edx
+ movb $0xff, %ah
+ int $0x21
+
+ movb $0x00, %ah
+ int $0x21
+
+int90_test:
+ pushf
+ pop %dx
+ movb $0xff, %ah
+ int $0x21
+
+ movw %sp, %bx
+ movw 4(%bx), %dx
+ movb $0xff, %ah
+ int $0x21
+
+ movw $GET_OFFSET(int90_msg), %dx
+ movb $0x09, %ah
+ int $0x21
+ iret
+
+int90_msg:
+ .string "INT90 started\n$"
+
+hello_world:
+ .string "Hello VM86 world\n$"
+
+IF_msg:
+ .string "VM86 IF test\n$"
+
+IF_msg1:
+ .string "If you see a diff here, your Linux kernel is buggy, please update to 2.4.20 kernel\n$"
+
+vm86_code_end:
+ \ No newline at end of file
diff --git a/tests/test-i386.c b/tests/test-i386.c
new file mode 100644
index 0000000..4a25d03
--- /dev/null
+++ b/tests/test-i386.c
@@ -0,0 +1,2629 @@
+/*
+ * x86 CPU test
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+#include <sys/mman.h>
+
+#if !defined(__x86_64__)
+#define TEST_VM86
+#define TEST_SEGS
+#endif
+//#define LINUX_VM86_IOPL_FIX
+//#define TEST_P4_FLAGS
+#if defined(__x86_64__)
+#define TEST_SSE
+#define TEST_CMOV 1
+#define TEST_FCOMI 1
+#else
+//#define TEST_SSE
+#define TEST_CMOV 0
+#define TEST_FCOMI 0
+#endif
+
+#if defined(__x86_64__)
+#define FMT64X "%016lx"
+#define FMTLX "%016lx"
+#define X86_64_ONLY(x) x
+#else
+#define FMT64X "%016" PRIx64
+#define FMTLX "%08lx"
+#define X86_64_ONLY(x)
+#endif
+
+#ifdef TEST_VM86
+#include <asm/vm86.h>
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#define CC_C 0x0001
+#define CC_P 0x0004
+#define CC_A 0x0010
+#define CC_Z 0x0040
+#define CC_S 0x0080
+#define CC_O 0x0800
+
+#define __init_call __attribute__ ((unused,__section__ ("initcall")))
+
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+
+#if defined(__x86_64__)
+static inline long i2l(long v)
+{
+ return v | ((v ^ 0xabcd) << 32);
+}
+#else
+static inline long i2l(long v)
+{
+ return v;
+}
+#endif
+
+#define OP add
+#include "test-i386.h"
+
+#define OP sub
+#include "test-i386.h"
+
+#define OP xor
+#include "test-i386.h"
+
+#define OP and
+#include "test-i386.h"
+
+#define OP or
+#include "test-i386.h"
+
+#define OP cmp
+#include "test-i386.h"
+
+#define OP adc
+#define OP_CC
+#include "test-i386.h"
+
+#define OP sbb
+#define OP_CC
+#include "test-i386.h"
+
+#define OP inc
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP dec
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP neg
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP not
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#undef CC_MASK
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O)
+
+#define OP shl
+#include "test-i386-shift.h"
+
+#define OP shr
+#include "test-i386-shift.h"
+
+#define OP sar
+#include "test-i386-shift.h"
+
+#define OP rol
+#include "test-i386-shift.h"
+
+#define OP ror
+#include "test-i386-shift.h"
+
+#define OP rcr
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP rcl
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP shld
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP shrd
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* XXX: should be more precise ? */
+#undef CC_MASK
+#define CC_MASK (CC_C)
+
+#define OP bt
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP bts
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btr
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btc
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* lea test (modrm support) */
+#define TEST_LEAQ(STR)\
+{\
+ asm("lea " STR ", %0"\
+ : "=r" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA(STR)\
+{\
+ asm("lea " STR ", %0"\
+ : "=r" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA16(STR)\
+{\
+ asm(".code16 ; .byte 0x67 ; leal " STR ", %0 ; .code32"\
+ : "=wq" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = %08lx\n", STR, res);\
+}
+
+
+void test_lea(void)
+{
+ long eax, ebx, ecx, edx, esi, edi, res;
+ eax = i2l(0x0001);
+ ebx = i2l(0x0002);
+ ecx = i2l(0x0004);
+ edx = i2l(0x0008);
+ esi = i2l(0x0010);
+ edi = i2l(0x0020);
+
+ TEST_LEA("0x4000");
+
+ TEST_LEA("(%%eax)");
+ TEST_LEA("(%%ebx)");
+ TEST_LEA("(%%ecx)");
+ TEST_LEA("(%%edx)");
+ TEST_LEA("(%%esi)");
+ TEST_LEA("(%%edi)");
+
+ TEST_LEA("0x40(%%eax)");
+ TEST_LEA("0x40(%%ebx)");
+ TEST_LEA("0x40(%%ecx)");
+ TEST_LEA("0x40(%%edx)");
+ TEST_LEA("0x40(%%esi)");
+ TEST_LEA("0x40(%%edi)");
+
+ TEST_LEA("0x4000(%%eax)");
+ TEST_LEA("0x4000(%%ebx)");
+ TEST_LEA("0x4000(%%ecx)");
+ TEST_LEA("0x4000(%%edx)");
+ TEST_LEA("0x4000(%%esi)");
+ TEST_LEA("0x4000(%%edi)");
+
+ TEST_LEA("(%%eax, %%ecx)");
+ TEST_LEA("(%%ebx, %%edx)");
+ TEST_LEA("(%%ecx, %%ecx)");
+ TEST_LEA("(%%edx, %%ecx)");
+ TEST_LEA("(%%esi, %%ecx)");
+ TEST_LEA("(%%edi, %%ecx)");
+
+ TEST_LEA("0x40(%%eax, %%ecx)");
+ TEST_LEA("0x4000(%%ebx, %%edx)");
+
+ TEST_LEA("(%%ecx, %%ecx, 2)");
+ TEST_LEA("(%%edx, %%ecx, 4)");
+ TEST_LEA("(%%esi, %%ecx, 8)");
+
+ TEST_LEA("(,%%eax, 2)");
+ TEST_LEA("(,%%ebx, 4)");
+ TEST_LEA("(,%%ecx, 8)");
+
+ TEST_LEA("0x40(,%%eax, 2)");
+ TEST_LEA("0x40(,%%ebx, 4)");
+ TEST_LEA("0x40(,%%ecx, 8)");
+
+
+ TEST_LEA("-10(%%ecx, %%ecx, 2)");
+ TEST_LEA("-10(%%edx, %%ecx, 4)");
+ TEST_LEA("-10(%%esi, %%ecx, 8)");
+
+ TEST_LEA("0x4000(%%ecx, %%ecx, 2)");
+ TEST_LEA("0x4000(%%edx, %%ecx, 4)");
+ TEST_LEA("0x4000(%%esi, %%ecx, 8)");
+
+#if defined(__x86_64__)
+ TEST_LEAQ("0x4000");
+ TEST_LEAQ("0x4000(%%rip)");
+
+ TEST_LEAQ("(%%rax)");
+ TEST_LEAQ("(%%rbx)");
+ TEST_LEAQ("(%%rcx)");
+ TEST_LEAQ("(%%rdx)");
+ TEST_LEAQ("(%%rsi)");
+ TEST_LEAQ("(%%rdi)");
+
+ TEST_LEAQ("0x40(%%rax)");
+ TEST_LEAQ("0x40(%%rbx)");
+ TEST_LEAQ("0x40(%%rcx)");
+ TEST_LEAQ("0x40(%%rdx)");
+ TEST_LEAQ("0x40(%%rsi)");
+ TEST_LEAQ("0x40(%%rdi)");
+
+ TEST_LEAQ("0x4000(%%rax)");
+ TEST_LEAQ("0x4000(%%rbx)");
+ TEST_LEAQ("0x4000(%%rcx)");
+ TEST_LEAQ("0x4000(%%rdx)");
+ TEST_LEAQ("0x4000(%%rsi)");
+ TEST_LEAQ("0x4000(%%rdi)");
+
+ TEST_LEAQ("(%%rax, %%rcx)");
+ TEST_LEAQ("(%%rbx, %%rdx)");
+ TEST_LEAQ("(%%rcx, %%rcx)");
+ TEST_LEAQ("(%%rdx, %%rcx)");
+ TEST_LEAQ("(%%rsi, %%rcx)");
+ TEST_LEAQ("(%%rdi, %%rcx)");
+
+ TEST_LEAQ("0x40(%%rax, %%rcx)");
+ TEST_LEAQ("0x4000(%%rbx, %%rdx)");
+
+ TEST_LEAQ("(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("(%%rsi, %%rcx, 8)");
+
+ TEST_LEAQ("(,%%rax, 2)");
+ TEST_LEAQ("(,%%rbx, 4)");
+ TEST_LEAQ("(,%%rcx, 8)");
+
+ TEST_LEAQ("0x40(,%%rax, 2)");
+ TEST_LEAQ("0x40(,%%rbx, 4)");
+ TEST_LEAQ("0x40(,%%rcx, 8)");
+
+
+ TEST_LEAQ("-10(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("-10(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("-10(%%rsi, %%rcx, 8)");
+
+ TEST_LEAQ("0x4000(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("0x4000(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("0x4000(%%rsi, %%rcx, 8)");
+#else
+ /* limited 16 bit addressing test */
+ TEST_LEA16("0x4000");
+ TEST_LEA16("(%%bx)");
+ TEST_LEA16("(%%si)");
+ TEST_LEA16("(%%di)");
+ TEST_LEA16("0x40(%%bx)");
+ TEST_LEA16("0x40(%%si)");
+ TEST_LEA16("0x40(%%di)");
+ TEST_LEA16("0x4000(%%bx)");
+ TEST_LEA16("0x4000(%%si)");
+ TEST_LEA16("(%%bx,%%si)");
+ TEST_LEA16("(%%bx,%%di)");
+ TEST_LEA16("0x40(%%bx,%%si)");
+ TEST_LEA16("0x40(%%bx,%%di)");
+ TEST_LEA16("0x4000(%%bx,%%si)");
+ TEST_LEA16("0x4000(%%bx,%%di)");
+#endif
+}
+
+#define TEST_JCC(JCC, v1, v2)\
+{\
+ int res;\
+ asm("movl $1, %0\n\t"\
+ "cmpl %2, %1\n\t"\
+ "j" JCC " 1f\n\t"\
+ "movl $0, %0\n\t"\
+ "1:\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2));\
+ printf("%-10s %d\n", "j" JCC, res);\
+\
+ asm("movl $0, %0\n\t"\
+ "cmpl %2, %1\n\t"\
+ "set" JCC " %b0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2));\
+ printf("%-10s %d\n", "set" JCC, res);\
+ if (TEST_CMOV) {\
+ long val = i2l(1);\
+ long res = i2l(0x12345678);\
+X86_64_ONLY(\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "q %3, %0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "q", res);)\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "l %k3, %k0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "l", res);\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "w %w3, %w0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "r" (1), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "w", res);\
+ } \
+}
+
+/* various jump tests */
+void test_jcc(void)
+{
+ TEST_JCC("ne", 1, 1);
+ TEST_JCC("ne", 1, 0);
+
+ TEST_JCC("e", 1, 1);
+ TEST_JCC("e", 1, 0);
+
+ TEST_JCC("l", 1, 1);
+ TEST_JCC("l", 1, 0);
+ TEST_JCC("l", 1, -1);
+
+ TEST_JCC("le", 1, 1);
+ TEST_JCC("le", 1, 0);
+ TEST_JCC("le", 1, -1);
+
+ TEST_JCC("ge", 1, 1);
+ TEST_JCC("ge", 1, 0);
+ TEST_JCC("ge", -1, 1);
+
+ TEST_JCC("g", 1, 1);
+ TEST_JCC("g", 1, 0);
+ TEST_JCC("g", 1, -1);
+
+ TEST_JCC("b", 1, 1);
+ TEST_JCC("b", 1, 0);
+ TEST_JCC("b", 1, -1);
+
+ TEST_JCC("be", 1, 1);
+ TEST_JCC("be", 1, 0);
+ TEST_JCC("be", 1, -1);
+
+ TEST_JCC("ae", 1, 1);
+ TEST_JCC("ae", 1, 0);
+ TEST_JCC("ae", 1, -1);
+
+ TEST_JCC("a", 1, 1);
+ TEST_JCC("a", 1, 0);
+ TEST_JCC("a", 1, -1);
+
+
+ TEST_JCC("p", 1, 1);
+ TEST_JCC("p", 1, 0);
+
+ TEST_JCC("np", 1, 1);
+ TEST_JCC("np", 1, 0);
+
+ TEST_JCC("o", 0x7fffffff, 0);
+ TEST_JCC("o", 0x7fffffff, -1);
+
+ TEST_JCC("no", 0x7fffffff, 0);
+ TEST_JCC("no", 0x7fffffff, -1);
+
+ TEST_JCC("s", 0, 1);
+ TEST_JCC("s", 0, -1);
+ TEST_JCC("s", 0, 0);
+
+ TEST_JCC("ns", 0, 1);
+ TEST_JCC("ns", 0, -1);
+ TEST_JCC("ns", 0, 0);
+}
+
+#undef CC_MASK
+#ifdef TEST_P4_FLAGS
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+#else
+#define CC_MASK (CC_O | CC_C)
+#endif
+
+#define OP mul
+#include "test-i386-muldiv.h"
+
+#define OP imul
+#include "test-i386-muldiv.h"
+
+void test_imulw2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imulw %w2, %w0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imulw", s0, s1, res, flags & CC_MASK);
+}
+
+void test_imull2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imull %k2, %k0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imull", s0, s1, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void test_imulq2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imulq %2, %0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imulq", s0, s1, res, flags & CC_MASK);
+}
+#endif
+
+#define TEST_IMUL_IM(size, rsize, op0, op1)\
+{\
+ long res, flags, s1;\
+ flags = 0;\
+ res = 0;\
+ s1 = op1;\
+ asm volatile ("push %3\n\t"\
+ "popf\n\t"\
+ "imul" size " $" #op0 ", %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=r" (res), "=g" (flags)\
+ : "r" (s1), "1" (flags), "0" (res));\
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",\
+ "imul" size " im", (long)op0, (long)op1, res, flags & CC_MASK);\
+}
+
+
+#undef CC_MASK
+#define CC_MASK (0)
+
+#define OP div
+#include "test-i386-muldiv.h"
+
+#define OP idiv
+#include "test-i386-muldiv.h"
+
+void test_mul(void)
+{
+ test_imulb(0x1234561d, 4);
+ test_imulb(3, -4);
+ test_imulb(0x80, 0x80);
+ test_imulb(0x10, 0x10);
+
+ test_imulw(0, 0x1234001d, 45);
+ test_imulw(0, 23, -45);
+ test_imulw(0, 0x8000, 0x8000);
+ test_imulw(0, 0x100, 0x100);
+
+ test_imull(0, 0x1234001d, 45);
+ test_imull(0, 23, -45);
+ test_imull(0, 0x80000000, 0x80000000);
+ test_imull(0, 0x10000, 0x10000);
+
+ test_mulb(0x1234561d, 4);
+ test_mulb(3, -4);
+ test_mulb(0x80, 0x80);
+ test_mulb(0x10, 0x10);
+
+ test_mulw(0, 0x1234001d, 45);
+ test_mulw(0, 23, -45);
+ test_mulw(0, 0x8000, 0x8000);
+ test_mulw(0, 0x100, 0x100);
+
+ test_mull(0, 0x1234001d, 45);
+ test_mull(0, 23, -45);
+ test_mull(0, 0x80000000, 0x80000000);
+ test_mull(0, 0x10000, 0x10000);
+
+ test_imulw2(0x1234001d, 45);
+ test_imulw2(23, -45);
+ test_imulw2(0x8000, 0x8000);
+ test_imulw2(0x100, 0x100);
+
+ test_imull2(0x1234001d, 45);
+ test_imull2(23, -45);
+ test_imull2(0x80000000, 0x80000000);
+ test_imull2(0x10000, 0x10000);
+
+ TEST_IMUL_IM("w", "w", 45, 0x1234);
+ TEST_IMUL_IM("w", "w", -45, 23);
+ TEST_IMUL_IM("w", "w", 0x8000, 0x80000000);
+ TEST_IMUL_IM("w", "w", 0x7fff, 0x1000);
+
+ TEST_IMUL_IM("l", "k", 45, 0x1234);
+ TEST_IMUL_IM("l", "k", -45, 23);
+ TEST_IMUL_IM("l", "k", 0x8000, 0x80000000);
+ TEST_IMUL_IM("l", "k", 0x7fff, 0x1000);
+
+ test_idivb(0x12341678, 0x127e);
+ test_idivb(0x43210123, -5);
+ test_idivb(0x12340004, -1);
+
+ test_idivw(0, 0x12345678, 12347);
+ test_idivw(0, -23223, -45);
+ test_idivw(0, 0x12348000, -1);
+ test_idivw(0x12343, 0x12345678, 0x81238567);
+
+ test_idivl(0, 0x12345678, 12347);
+ test_idivl(0, -233223, -45);
+ test_idivl(0, 0x80000000, -1);
+ test_idivl(0x12343, 0x12345678, 0x81234567);
+
+ test_divb(0x12341678, 0x127e);
+ test_divb(0x43210123, -5);
+ test_divb(0x12340004, -1);
+
+ test_divw(0, 0x12345678, 12347);
+ test_divw(0, -23223, -45);
+ test_divw(0, 0x12348000, -1);
+ test_divw(0x12343, 0x12345678, 0x81238567);
+
+ test_divl(0, 0x12345678, 12347);
+ test_divl(0, -233223, -45);
+ test_divl(0, 0x80000000, -1);
+ test_divl(0x12343, 0x12345678, 0x81234567);
+
+#if defined(__x86_64__)
+ test_imulq(0, 0x1234001d1234001d, 45);
+ test_imulq(0, 23, -45);
+ test_imulq(0, 0x8000000000000000, 0x8000000000000000);
+ test_imulq(0, 0x100000000, 0x100000000);
+
+ test_mulq(0, 0x1234001d1234001d, 45);
+ test_mulq(0, 23, -45);
+ test_mulq(0, 0x8000000000000000, 0x8000000000000000);
+ test_mulq(0, 0x100000000, 0x100000000);
+
+ test_imulq2(0x1234001d1234001d, 45);
+ test_imulq2(23, -45);
+ test_imulq2(0x8000000000000000, 0x8000000000000000);
+ test_imulq2(0x100000000, 0x100000000);
+
+ TEST_IMUL_IM("q", "", 45, 0x12341234);
+ TEST_IMUL_IM("q", "", -45, 23);
+ TEST_IMUL_IM("q", "", 0x8000, 0x8000000000000000);
+ TEST_IMUL_IM("q", "", 0x7fff, 0x10000000);
+
+ test_idivq(0, 0x12345678abcdef, 12347);
+ test_idivq(0, -233223, -45);
+ test_idivq(0, 0x8000000000000000, -1);
+ test_idivq(0x12343, 0x12345678, 0x81234567);
+
+ test_divq(0, 0x12345678abcdef, 12347);
+ test_divq(0, -233223, -45);
+ test_divq(0, 0x8000000000000000, -1);
+ test_divq(0x12343, 0x12345678, 0x81234567);
+#endif
+}
+
+#define TEST_BSX(op, size, op0)\
+{\
+ long res, val, resz;\
+ val = op0;\
+ asm("xor %1, %1\n"\
+ "mov $0x12345678, %0\n"\
+ #op " %" size "2, %" size "0 ; setz %b1" \
+ : "=r" (res), "=q" (resz)\
+ : "g" (val));\
+ printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\
+}
+
+void test_bsx(void)
+{
+ TEST_BSX(bsrw, "w", 0);
+ TEST_BSX(bsrw, "w", 0x12340128);
+ TEST_BSX(bsfw, "w", 0);
+ TEST_BSX(bsfw, "w", 0x12340128);
+ TEST_BSX(bsrl, "k", 0);
+ TEST_BSX(bsrl, "k", 0x00340128);
+ TEST_BSX(bsfl, "k", 0);
+ TEST_BSX(bsfl, "k", 0x00340128);
+#if defined(__x86_64__)
+ TEST_BSX(bsrq, "", 0);
+ TEST_BSX(bsrq, "", 0x003401281234);
+ TEST_BSX(bsfq, "", 0);
+ TEST_BSX(bsfq, "", 0x003401281234);
+#endif
+}
+
+/**********************************************/
+
+union float64u {
+ double d;
+ uint64_t l;
+};
+
+union float64u q_nan = { .l = 0xFFF8000000000000 };
+union float64u s_nan = { .l = 0xFFF0000000000000 };
+
+void test_fops(double a, double b)
+{
+ printf("a=%f b=%f a+b=%f\n", a, b, a + b);
+ printf("a=%f b=%f a-b=%f\n", a, b, a - b);
+ printf("a=%f b=%f a*b=%f\n", a, b, a * b);
+ printf("a=%f b=%f a/b=%f\n", a, b, a / b);
+ printf("a=%f b=%f fmod(a, b)=%f\n", a, b, fmod(a, b));
+ printf("a=%f sqrt(a)=%f\n", a, sqrt(a));
+ printf("a=%f sin(a)=%f\n", a, sin(a));
+ printf("a=%f cos(a)=%f\n", a, cos(a));
+ printf("a=%f tan(a)=%f\n", a, tan(a));
+ printf("a=%f log(a)=%f\n", a, log(a));
+ printf("a=%f exp(a)=%f\n", a, exp(a));
+ printf("a=%f b=%f atan2(a, b)=%f\n", a, b, atan2(a, b));
+ /* just to test some op combining */
+ printf("a=%f asin(sin(a))=%f\n", a, asin(sin(a)));
+ printf("a=%f acos(cos(a))=%f\n", a, acos(cos(a)));
+ printf("a=%f atan(tan(a))=%f\n", a, atan(tan(a)));
+
+}
+
+void fpu_clear_exceptions(void)
+{
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+ uint32_t ignored[4];
+ long double fpregs[8];
+ } float_env32;
+
+ asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+ float_env32.fpus &= ~0x7f;
+ asm volatile ("fldenv %0\n" : : "m" (float_env32));
+}
+
+/* XXX: display exception bits when supported */
+#define FPUS_EMASK 0x0000
+//#define FPUS_EMASK 0x007f
+
+void test_fcmp(double a, double b)
+{
+ long eflags, fpus;
+
+ fpu_clear_exceptions();
+ asm("fcom %2\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fcom(%f %f)=%04lx \n",
+ a, b, fpus & (0x4500 | FPUS_EMASK));
+ fpu_clear_exceptions();
+ asm("fucom %2\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fucom(%f %f)=%04lx\n",
+ a, b, fpus & (0x4500 | FPUS_EMASK));
+ if (TEST_FCOMI) {
+ /* test f(u)comi instruction */
+ fpu_clear_exceptions();
+ asm("fcomi %3, %2\n"
+ "fstsw %%ax\n"
+ "pushf\n"
+ "pop %0\n"
+ : "=r" (eflags), "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fcomi(%f %f)=%04lx %02lx\n",
+ a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+ fpu_clear_exceptions();
+ asm("fucomi %3, %2\n"
+ "fstsw %%ax\n"
+ "pushf\n"
+ "pop %0\n"
+ : "=r" (eflags), "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fucomi(%f %f)=%04lx %02lx\n",
+ a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+ }
+ fpu_clear_exceptions();
+ asm volatile("fxam\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a));
+ printf("fxam(%f)=%04lx\n", a, fpus & 0x4700);
+ fpu_clear_exceptions();
+}
+
+void test_fcvt(double a)
+{
+ float fa;
+ long double la;
+ int16_t fpuc;
+ int i;
+ int64_t lla;
+ int ia;
+ int16_t wa;
+ double ra;
+
+ fa = a;
+ la = a;
+ printf("(float)%f = %f\n", a, fa);
+ printf("(long double)%f = %Lf\n", a, la);
+ printf("a=" FMT64X "\n", *(uint64_t *)&a);
+ printf("la=" FMT64X " %04x\n", *(uint64_t *)&la,
+ *(unsigned short *)((char *)(&la) + 8));
+
+ /* test all roundings */
+ asm volatile ("fstcw %0" : "=m" (fpuc));
+ for(i=0;i<4;i++) {
+ asm volatile ("fldcw %0" : : "m" ((fpuc & ~0x0c00) | (i << 10)));
+ asm volatile ("fist %0" : "=m" (wa) : "t" (a));
+ asm volatile ("fistl %0" : "=m" (ia) : "t" (a));
+ asm volatile ("fistpll %0" : "=m" (lla) : "t" (a) : "st");
+ asm volatile ("frndint ; fstl %0" : "=m" (ra) : "t" (a));
+ asm volatile ("fldcw %0" : : "m" (fpuc));
+ printf("(short)a = %d\n", wa);
+ printf("(int)a = %d\n", ia);
+ printf("(int64_t)a = " FMT64X "\n", lla);
+ printf("rint(a) = %f\n", ra);
+ }
+}
+
+#define TEST(N) \
+ asm("fld" #N : "=t" (a)); \
+ printf("fld" #N "= %f\n", a);
+
+void test_fconst(void)
+{
+ double a;
+ TEST(1);
+ TEST(l2t);
+ TEST(l2e);
+ TEST(pi);
+ TEST(lg2);
+ TEST(ln2);
+ TEST(z);
+}
+
+void test_fbcd(double a)
+{
+ unsigned short bcd[5];
+ double b;
+
+ asm("fbstp %0" : "=m" (bcd[0]) : "t" (a) : "st");
+ asm("fbld %1" : "=t" (b) : "m" (bcd[0]));
+ printf("a=%f bcd=%04x%04x%04x%04x%04x b=%f\n",
+ a, bcd[4], bcd[3], bcd[2], bcd[1], bcd[0], b);
+}
+
+#define TEST_ENV(env, save, restore)\
+{\
+ memset((env), 0xaa, sizeof(*(env)));\
+ for(i=0;i<5;i++)\
+ asm volatile ("fldl %0" : : "m" (dtab[i]));\
+ asm volatile (save " %0\n" : : "m" (*(env)));\
+ asm volatile (restore " %0\n": : "m" (*(env)));\
+ for(i=0;i<5;i++)\
+ asm volatile ("fstpl %0" : "=m" (rtab[i]));\
+ for(i=0;i<5;i++)\
+ printf("res[%d]=%f\n", i, rtab[i]);\
+ printf("fpuc=%04x fpus=%04x fptag=%04x\n",\
+ (env)->fpuc,\
+ (env)->fpus & 0xff00,\
+ (env)->fptag);\
+}
+
+void test_fenv(void)
+{
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+ uint32_t ignored[4];
+ long double fpregs[8];
+ } float_env32;
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t fpus;
+ uint16_t fptag;
+ uint16_t ignored[4];
+ long double fpregs[8];
+ } float_env16;
+ double dtab[8];
+ double rtab[8];
+ int i;
+
+ for(i=0;i<8;i++)
+ dtab[i] = i + 1;
+
+ TEST_ENV(&float_env16, "data16 fnstenv", "data16 fldenv");
+ TEST_ENV(&float_env16, "data16 fnsave", "data16 frstor");
+ TEST_ENV(&float_env32, "fnstenv", "fldenv");
+ TEST_ENV(&float_env32, "fnsave", "frstor");
+
+ /* test for ffree */
+ for(i=0;i<5;i++)
+ asm volatile ("fldl %0" : : "m" (dtab[i]));
+ asm volatile("ffree %st(2)");
+ asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+ asm volatile ("fninit");
+ printf("fptag=%04x\n", float_env32.fptag);
+}
+
+
+#define TEST_FCMOV(a, b, eflags, CC)\
+{\
+ double res;\
+ asm("push %3\n"\
+ "popf\n"\
+ "fcmov" CC " %2, %0\n"\
+ : "=t" (res)\
+ : "0" (a), "u" (b), "g" (eflags));\
+ printf("fcmov%s eflags=0x%04lx-> %f\n", \
+ CC, (long)eflags, res);\
+}
+
+void test_fcmov(void)
+{
+ double a, b;
+ long eflags, i;
+
+ a = 1.0;
+ b = 2.0;
+ for(i = 0; i < 4; i++) {
+ eflags = 0;
+ if (i & 1)
+ eflags |= CC_C;
+ if (i & 2)
+ eflags |= CC_Z;
+ TEST_FCMOV(a, b, eflags, "b");
+ TEST_FCMOV(a, b, eflags, "e");
+ TEST_FCMOV(a, b, eflags, "be");
+ TEST_FCMOV(a, b, eflags, "nb");
+ TEST_FCMOV(a, b, eflags, "ne");
+ TEST_FCMOV(a, b, eflags, "nbe");
+ }
+ TEST_FCMOV(a, b, 0, "u");
+ TEST_FCMOV(a, b, CC_P, "u");
+ TEST_FCMOV(a, b, 0, "nu");
+ TEST_FCMOV(a, b, CC_P, "nu");
+}
+
+void test_floats(void)
+{
+ test_fops(2, 3);
+ test_fops(1.4, -5);
+ test_fcmp(2, -1);
+ test_fcmp(2, 2);
+ test_fcmp(2, 3);
+ test_fcmp(2, q_nan.d);
+ test_fcmp(q_nan.d, -1);
+ test_fcmp(-1.0/0.0, -1);
+ test_fcmp(1.0/0.0, -1);
+ test_fcvt(0.5);
+ test_fcvt(-0.5);
+ test_fcvt(1.0/7.0);
+ test_fcvt(-1.0/9.0);
+ test_fcvt(32768);
+ test_fcvt(-1e20);
+ test_fcvt(-1.0/0.0);
+ test_fcvt(1.0/0.0);
+ test_fcvt(q_nan.d);
+ test_fconst();
+ test_fbcd(1234567890123456);
+ test_fbcd(-123451234567890);
+ test_fenv();
+ if (TEST_CMOV) {
+ test_fcmov();
+ }
+}
+
+/**********************************************/
+#if !defined(__x86_64__)
+
+#define TEST_BCD(op, op0, cc_in, cc_mask)\
+{\
+ int res, flags;\
+ res = op0;\
+ flags = cc_in;\
+ asm ("push %3\n\t"\
+ "popf\n\t"\
+ #op "\n\t"\
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=a" (res), "=g" (flags)\
+ : "0" (res), "1" (flags));\
+ printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",\
+ #op, op0, res, cc_in, flags & cc_mask);\
+}
+
+void test_bcd(void)
+{
+ TEST_BCD(daa, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+ TEST_BCD(das, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+ TEST_BCD(aaa, 0x12340205, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340306, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x1234040a, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x123405fa, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340205, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340306, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x1234040a, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x123405fa, 0, (CC_C | CC_A));
+
+ TEST_BCD(aas, 0x12340205, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340306, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x1234040a, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x123405fa, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340205, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340306, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x1234040a, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x123405fa, 0, (CC_C | CC_A));
+
+ TEST_BCD(aam, 0x12340547, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+ TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+}
+#endif
+
+#define TEST_XCHG(op, size, opconst)\
+{\
+ long op0, op1;\
+ op0 = i2l(0x12345678);\
+ op1 = i2l(0xfbca7654);\
+ asm(#op " %" size "0, %" size "1" \
+ : "=q" (op0), opconst (op1) \
+ : "0" (op0), "1" (op1));\
+ printf("%-10s A=" FMTLX " B=" FMTLX "\n",\
+ #op, op0, op1);\
+}
+
+#define TEST_CMPXCHG(op, size, opconst, eax)\
+{\
+ long op0, op1, op2;\
+ op0 = i2l(0x12345678);\
+ op1 = i2l(0xfbca7654);\
+ op2 = i2l(eax);\
+ asm(#op " %" size "0, %" size "1" \
+ : "=q" (op0), opconst (op1) \
+ : "0" (op0), "1" (op1), "a" (op2));\
+ printf("%-10s EAX=" FMTLX " A=" FMTLX " C=" FMTLX "\n",\
+ #op, op2, op0, op1);\
+}
+
+void test_xchg(void)
+{
+#if defined(__x86_64__)
+ TEST_XCHG(xchgq, "", "=q");
+#endif
+ TEST_XCHG(xchgl, "k", "=q");
+ TEST_XCHG(xchgw, "w", "=q");
+ TEST_XCHG(xchgb, "b", "=q");
+
+#if defined(__x86_64__)
+ TEST_XCHG(xchgq, "", "=m");
+#endif
+ TEST_XCHG(xchgl, "k", "=m");
+ TEST_XCHG(xchgw, "w", "=m");
+ TEST_XCHG(xchgb, "b", "=m");
+
+#if defined(__x86_64__)
+ TEST_XCHG(xaddq, "", "=q");
+#endif
+ TEST_XCHG(xaddl, "k", "=q");
+ TEST_XCHG(xaddw, "w", "=q");
+ TEST_XCHG(xaddb, "b", "=q");
+
+ {
+ int res;
+ res = 0x12345678;
+ asm("xaddl %1, %0" : "=r" (res) : "0" (res));
+ printf("xaddl same res=%08x\n", res);
+ }
+
+#if defined(__x86_64__)
+ TEST_XCHG(xaddq, "", "=m");
+#endif
+ TEST_XCHG(xaddl, "k", "=m");
+ TEST_XCHG(xaddw, "w", "=m");
+ TEST_XCHG(xaddb, "b", "=m");
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfbca7654);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfbca7654);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfffefdfc);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfffefdfc);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfbca7654);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfbca7654);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfffefdfc);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfffefdfc);
+
+ {
+ uint64_t op0, op1, op2;
+ long i, eflags;
+
+ for(i = 0; i < 2; i++) {
+ op0 = 0x123456789abcd;
+ if (i == 0)
+ op1 = 0xfbca765423456;
+ else
+ op1 = op0;
+ op2 = 0x6532432432434;
+ asm("cmpxchg8b %1\n"
+ "pushf\n"
+ "pop %2\n"
+ : "=A" (op0), "=m" (op1), "=g" (eflags)
+ : "0" (op0), "m" (op1), "b" ((int)op2), "c" ((int)(op2 >> 32)));
+ printf("cmpxchg8b: op0=" FMT64X " op1=" FMT64X " CC=%02lx\n",
+ op0, op1, eflags & CC_Z);
+ }
+ }
+}
+
+#ifdef TEST_SEGS
+/**********************************************/
+/* segmentation tests */
+
+#include <asm/ldt.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+
+_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66)
+#define modify_ldt_ldt_s user_desc
+#endif
+
+#define MK_SEL(n) (((n) << 3) | 7)
+
+uint8_t seg_data1[4096];
+uint8_t seg_data2[4096];
+
+#define TEST_LR(op, size, seg, mask)\
+{\
+ int res, res2;\
+ res = 0x12345678;\
+ asm (op " %" size "2, %" size "0\n" \
+ "movl $0, %1\n"\
+ "jnz 1f\n"\
+ "movl $1, %1\n"\
+ "1:\n"\
+ : "=r" (res), "=r" (res2) : "m" (seg), "0" (res));\
+ printf(op ": Z=%d %08x\n", res2, res & ~(mask));\
+}
+
+/* NOTE: we use Linux modify_ldt syscall */
+void test_segs(void)
+{
+ struct modify_ldt_ldt_s ldt;
+ long long ldt_table[3];
+ int res, res2;
+ char tmp;
+ struct {
+ uint32_t offset;
+ uint16_t seg;
+ } __attribute__((packed)) segoff;
+
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&seg_data1;
+ ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ ldt.entry_number = 2;
+ ldt.base_addr = (unsigned long)&seg_data2;
+ ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */
+#if 0
+ {
+ int i;
+ for(i=0;i<3;i++)
+ printf("%d: %016Lx\n", i, ldt_table[i]);
+ }
+#endif
+ /* do some tests with fs or gs */
+ asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+
+ seg_data1[1] = 0xaa;
+ seg_data2[1] = 0x55;
+
+ asm volatile ("fs movzbl 0x1, %0" : "=r" (res));
+ printf("FS[1] = %02x\n", res);
+
+ asm volatile ("pushl %%gs\n"
+ "movl %1, %%gs\n"
+ "gs movzbl 0x1, %0\n"
+ "popl %%gs\n"
+ : "=r" (res)
+ : "r" (MK_SEL(2)));
+ printf("GS[1] = %02x\n", res);
+
+ /* tests with ds/ss (implicit segment case) */
+ tmp = 0xa5;
+ asm volatile ("pushl %%ebp\n\t"
+ "pushl %%ds\n\t"
+ "movl %2, %%ds\n\t"
+ "movl %3, %%ebp\n\t"
+ "movzbl 0x1, %0\n\t"
+ "movzbl (%%ebp), %1\n\t"
+ "popl %%ds\n\t"
+ "popl %%ebp\n\t"
+ : "=r" (res), "=r" (res2)
+ : "r" (MK_SEL(1)), "r" (&tmp));
+ printf("DS[1] = %02x\n", res);
+ printf("SS[tmp] = %02x\n", res2);
+
+ segoff.seg = MK_SEL(2);
+ segoff.offset = 0xabcdef12;
+ asm volatile("lfs %2, %0\n\t"
+ "movl %%fs, %1\n\t"
+ : "=r" (res), "=g" (res2)
+ : "m" (segoff));
+ printf("FS:reg = %04x:%08x\n", res2, res);
+
+ TEST_LR("larw", "w", MK_SEL(2), 0x0100);
+ TEST_LR("larl", "", MK_SEL(2), 0x0100);
+ TEST_LR("lslw", "w", MK_SEL(2), 0);
+ TEST_LR("lsll", "", MK_SEL(2), 0);
+
+ TEST_LR("larw", "w", 0xfff8, 0);
+ TEST_LR("larl", "", 0xfff8, 0);
+ TEST_LR("lslw", "w", 0xfff8, 0);
+ TEST_LR("lsll", "", 0xfff8, 0);
+}
+
+/* 16 bit code test */
+extern char code16_start, code16_end;
+extern char code16_func1;
+extern char code16_func2;
+extern char code16_func3;
+
+void test_code16(void)
+{
+ struct modify_ldt_ldt_s ldt;
+ int res, res2;
+
+ /* build a code segment */
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&code16_start;
+ ldt.limit = &code16_end - &code16_start;
+ ldt.seg_32bit = 0;
+ ldt.contents = MODIFY_LDT_CONTENTS_CODE;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 0;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ /* call the first function */
+ asm volatile ("lcall %1, %2"
+ : "=a" (res)
+ : "i" (MK_SEL(1)), "i" (&code16_func1): "memory", "cc");
+ printf("func1() = 0x%08x\n", res);
+ asm volatile ("lcall %2, %3"
+ : "=a" (res), "=c" (res2)
+ : "i" (MK_SEL(1)), "i" (&code16_func2): "memory", "cc");
+ printf("func2() = 0x%08x spdec=%d\n", res, res2);
+ asm volatile ("lcall %1, %2"
+ : "=a" (res)
+ : "i" (MK_SEL(1)), "i" (&code16_func3): "memory", "cc");
+ printf("func3() = 0x%08x\n", res);
+}
+#endif
+
+#if defined(__x86_64__)
+asm(".globl func_lret\n"
+ "func_lret:\n"
+ "movl $0x87654641, %eax\n"
+ "lretq\n");
+#else
+asm(".globl func_lret\n"
+ "func_lret:\n"
+ "movl $0x87654321, %eax\n"
+ "lret\n"
+
+ ".globl func_iret\n"
+ "func_iret:\n"
+ "movl $0xabcd4321, %eax\n"
+ "iret\n");
+#endif
+
+extern char func_lret;
+extern char func_iret;
+
+void test_misc(void)
+{
+ char table[256];
+ long res, i;
+
+ for(i=0;i<256;i++) table[i] = 256 - i;
+ res = 0x12345678;
+ asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
+ printf("xlat: EAX=" FMTLX "\n", res);
+
+#if defined(__x86_64__)
+ {
+ static struct __attribute__((packed)) {
+ uint32_t offset;
+ uint16_t seg;
+ } desc;
+ long cs_sel;
+
+ asm volatile ("mov %%cs, %0" : "=r" (cs_sel));
+
+ asm volatile ("push %1\n"
+ "call func_lret\n"
+ : "=a" (res)
+ : "r" (cs_sel) : "memory", "cc");
+ printf("func_lret=" FMTLX "\n", res);
+
+ /* NOTE: we assume that &func_lret < 4GB */
+ desc.offset = (long)&func_lret;
+ desc.seg = cs_sel;
+
+ asm volatile ("xor %%rax, %%rax\n"
+ "rex64 lcall %1\n"
+ : "=a" (res)
+ : "m" (desc)
+ : "memory", "cc");
+ printf("func_lret2=" FMTLX "\n", res);
+
+ asm volatile ("push %2\n"
+ "mov $ 1f, %%rax\n"
+ "push %%rax\n"
+ "ljmp %1\n"
+ "1:\n"
+ : "=a" (res)
+ : "m" (desc), "b" (cs_sel)
+ : "memory", "cc");
+ printf("func_lret3=" FMTLX "\n", res);
+ }
+#else
+ asm volatile ("push %%cs ; call %1"
+ : "=a" (res)
+ : "m" (func_lret): "memory", "cc");
+ printf("func_lret=" FMTLX "\n", res);
+
+ asm volatile ("pushf ; push %%cs ; call %1"
+ : "=a" (res)
+ : "m" (func_iret): "memory", "cc");
+ printf("func_iret=" FMTLX "\n", res);
+#endif
+
+#if defined(__x86_64__)
+ /* specific popl test */
+ asm volatile ("push $12345432 ; push $0x9abcdef ; pop (%%rsp) ; pop %0"
+ : "=g" (res));
+ printf("popl esp=" FMTLX "\n", res);
+#else
+ /* specific popl test */
+ asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0"
+ : "=g" (res));
+ printf("popl esp=" FMTLX "\n", res);
+
+ /* specific popw test */
+ asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popw (%%esp) ; addl $2, %%esp ; popl %0"
+ : "=g" (res));
+ printf("popw esp=" FMTLX "\n", res);
+#endif
+}
+
+uint8_t str_buffer[4096];
+
+#define TEST_STRING1(OP, size, DF, REP)\
+{\
+ long esi, edi, eax, ecx, eflags;\
+\
+ esi = (long)(str_buffer + sizeof(str_buffer) / 2);\
+ edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\
+ eax = i2l(0x12345678);\
+ ecx = 17;\
+\
+ asm volatile ("push $0\n\t"\
+ "popf\n\t"\
+ DF "\n\t"\
+ REP #OP size "\n\t"\
+ "cld\n\t"\
+ "pushf\n\t"\
+ "pop %4\n\t"\
+ : "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\
+ : "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\
+ printf("%-10s ESI=" FMTLX " EDI=" FMTLX " EAX=" FMTLX " ECX=" FMTLX " EFL=%04x\n",\
+ REP #OP size, esi, edi, eax, ecx,\
+ (int)(eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)));\
+}
+
+#define TEST_STRING(OP, REP)\
+ TEST_STRING1(OP, "b", "", REP);\
+ TEST_STRING1(OP, "w", "", REP);\
+ TEST_STRING1(OP, "l", "", REP);\
+ X86_64_ONLY(TEST_STRING1(OP, "q", "", REP));\
+ TEST_STRING1(OP, "b", "std", REP);\
+ TEST_STRING1(OP, "w", "std", REP);\
+ TEST_STRING1(OP, "l", "std", REP);\
+ X86_64_ONLY(TEST_STRING1(OP, "q", "std", REP))
+
+void test_string(void)
+{
+ int i;
+ for(i = 0;i < sizeof(str_buffer); i++)
+ str_buffer[i] = i + 0x56;
+ TEST_STRING(stos, "");
+ TEST_STRING(stos, "rep ");
+ TEST_STRING(lods, ""); /* to verify stos */
+ TEST_STRING(lods, "rep ");
+ TEST_STRING(movs, "");
+ TEST_STRING(movs, "rep ");
+ TEST_STRING(lods, ""); /* to verify stos */
+
+ /* XXX: better tests */
+ TEST_STRING(scas, "");
+ TEST_STRING(scas, "repz ");
+ TEST_STRING(scas, "repnz ");
+ TEST_STRING(cmps, "");
+ TEST_STRING(cmps, "repz ");
+ TEST_STRING(cmps, "repnz ");
+}
+
+#ifdef TEST_VM86
+/* VM86 test */
+
+static inline void set_bit(uint8_t *a, unsigned int bit)
+{
+ a[bit / 8] |= (1 << (bit % 8));
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(struct vm86_regs *r, int val)
+{
+ r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
+}
+
+#undef __syscall_return
+#define __syscall_return(type, res) \
+do { \
+ return (type) (res); \
+} while (0)
+
+_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86)
+
+extern char vm86_code_start;
+extern char vm86_code_end;
+
+#define VM86_CODE_CS 0x100
+#define VM86_CODE_IP 0x100
+
+void test_vm86(void)
+{
+ struct vm86plus_struct ctx;
+ struct vm86_regs *r;
+ uint8_t *vm86_mem;
+ int seg, ret;
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ printf("ERROR: could not map vm86 memory");
+ return;
+ }
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* init basic registers */
+ r = &ctx.regs;
+ r->eip = VM86_CODE_IP;
+ r->esp = 0xfffe;
+ seg = VM86_CODE_CS;
+ r->cs = seg;
+ r->ss = seg;
+ r->ds = seg;
+ r->es = seg;
+ r->fs = seg;
+ r->gs = seg;
+ r->eflags = VIF_MASK;
+
+ /* move code to proper address. We use the same layout as a .com
+ dos program. */
+ memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,
+ &vm86_code_start, &vm86_code_end - &vm86_code_start);
+
+ /* mark int 0x21 as being emulated */
+ set_bit((uint8_t *)&ctx.int_revectored, 0x21);
+
+ for(;;) {
+ ret = vm86(VM86_ENTER, &ctx);
+ switch(VM86_TYPE(ret)) {
+ case VM86_INTx:
+ {
+ int int_num, ah, v;
+
+ int_num = VM86_ARG(ret);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (r->eax >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ goto the_end;
+ case 0x02: /* write char */
+ {
+ uint8_t c = r->edx;
+ putchar(c);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c, *ptr;
+ ptr = seg_to_linear(r->ds, r->edx);
+ for(;;) {
+ c = *ptr++;
+ if (c == '$')
+ break;
+ putchar(c);
+ }
+ r->eax = (r->eax & ~0xff) | '$';
+ }
+ break;
+ case 0xff: /* extension: write eflags number in edx */
+ v = (int)r->edx;
+#ifndef LINUX_VM86_IOPL_FIX
+ v &= ~0x3000;
+#endif
+ printf("%08x\n", v);
+ break;
+ default:
+ unknown_int:
+ printf("unsupported int 0x%02x\n", int_num);
+ goto the_end;
+ }
+ }
+ break;
+ case VM86_SIGNAL:
+ /* a signal came, we just ignore that */
+ break;
+ case VM86_STI:
+ break;
+ default:
+ printf("ERROR: unhandled vm86 return code (0x%x)\n", ret);
+ goto the_end;
+ }
+ }
+ the_end:
+ printf("VM86 end\n");
+ munmap(vm86_mem, 0x110000);
+}
+#endif
+
+/* exception tests */
+#if defined(__i386__) && !defined(REG_EAX)
+#define REG_EAX EAX
+#define REG_EBX EBX
+#define REG_ECX ECX
+#define REG_EDX EDX
+#define REG_ESI ESI
+#define REG_EDI EDI
+#define REG_EBP EBP
+#define REG_ESP ESP
+#define REG_EIP EIP
+#define REG_EFL EFL
+#define REG_TRAPNO TRAPNO
+#define REG_ERR ERR
+#endif
+
+#if defined(__x86_64__)
+#define REG_EIP REG_RIP
+#endif
+
+jmp_buf jmp_env;
+int v1;
+int tab[2];
+
+void sig_handler(int sig, siginfo_t *info, void *puc)
+{
+ struct ucontext *uc = puc;
+
+ printf("si_signo=%d si_errno=%d si_code=%d",
+ info->si_signo, info->si_errno, info->si_code);
+ printf(" si_addr=0x%08lx",
+ (unsigned long)info->si_addr);
+ printf("\n");
+
+ printf("trapno=" FMTLX " err=" FMTLX,
+ (long)uc->uc_mcontext.gregs[REG_TRAPNO],
+ (long)uc->uc_mcontext.gregs[REG_ERR]);
+ printf(" EIP=" FMTLX, (long)uc->uc_mcontext.gregs[REG_EIP]);
+ printf("\n");
+ longjmp(jmp_env, 1);
+}
+
+void test_exceptions(void)
+{
+ struct sigaction act;
+ volatile int val;
+
+ act.sa_sigaction = sig_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO | SA_NODEFER;
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGTRAP, &act, NULL);
+
+ /* test division by zero reporting */
+ printf("DIVZ exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now divide by zero */
+ v1 = 0;
+ v1 = 2 / v1;
+ }
+
+#if !defined(__x86_64__)
+ printf("BOUND exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* bound exception */
+ tab[0] = 1;
+ tab[1] = 10;
+ asm volatile ("bound %0, %1" : : "r" (11), "m" (tab[0]));
+ }
+#endif
+
+#ifdef TEST_SEGS
+ printf("segment exceptions:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* load an invalid segment */
+ asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 1));
+ }
+ if (setjmp(jmp_env) == 0) {
+ /* null data segment is valid */
+ asm volatile ("movl %0, %%fs" : : "r" (3));
+ /* null stack segment */
+ asm volatile ("movl %0, %%ss" : : "r" (3));
+ }
+
+ {
+ struct modify_ldt_ldt_s ldt;
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&seg_data1;
+ ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 1;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ if (setjmp(jmp_env) == 0) {
+ /* segment not present */
+ asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+ }
+ }
+#endif
+
+ /* test SEGV reporting */
+ printf("PF exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ val = 1;
+ /* we add a nop to test a weird PC retrieval case */
+ asm volatile ("nop");
+ /* now store in an invalid address */
+ *(char *)0x1234 = 1;
+ }
+
+ /* test SEGV reporting */
+ printf("PF exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ val = 1;
+ /* read from an invalid address */
+ v1 = *(char *)0x1234;
+ }
+
+ /* test illegal instruction reporting */
+ printf("UD2 exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now execute an invalid instruction */
+ asm volatile("ud2");
+ }
+ printf("lock nop exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now execute an invalid instruction */
+ asm volatile("lock nop");
+ }
+
+ printf("INT exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0xfd");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x01");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile (".byte 0xcd, 0x03");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x04");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x05");
+ }
+
+ printf("INT3 exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int3");
+ }
+
+ printf("CLI exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("cli");
+ }
+
+ printf("STI exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("cli");
+ }
+
+#if !defined(__x86_64__)
+ printf("INTO exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* overflow exception */
+ asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
+ }
+#endif
+
+ printf("OUTB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
+ }
+
+ printf("INB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
+ }
+
+ printf("REP OUTSB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
+ }
+
+ printf("REP INSB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
+ }
+
+ printf("HLT exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("hlt");
+ }
+
+ printf("single step exception:\n");
+ val = 0;
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("pushf\n"
+ "orl $0x00100, (%%esp)\n"
+ "popf\n"
+ "movl $0xabcd, %0\n"
+ "movl $0x0, %0\n" : "=m" (val) : : "cc", "memory");
+ }
+ printf("val=0x%x\n", val);
+}
+
+#if !defined(__x86_64__)
+/* specific precise single step test */
+void sig_trap_handler(int sig, siginfo_t *info, void *puc)
+{
+ struct ucontext *uc = puc;
+ printf("EIP=" FMTLX "\n", (long)uc->uc_mcontext.gregs[REG_EIP]);
+}
+
+const uint8_t sstep_buf1[4] = { 1, 2, 3, 4};
+uint8_t sstep_buf2[4];
+
+void test_single_step(void)
+{
+ struct sigaction act;
+ volatile int val;
+ int i;
+
+ val = 0;
+ act.sa_sigaction = sig_trap_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGTRAP, &act, NULL);
+ asm volatile ("pushf\n"
+ "orl $0x00100, (%%esp)\n"
+ "popf\n"
+ "movl $0xabcd, %0\n"
+
+ /* jmp test */
+ "movl $3, %%ecx\n"
+ "1:\n"
+ "addl $1, %0\n"
+ "decl %%ecx\n"
+ "jnz 1b\n"
+
+ /* movsb: the single step should stop at each movsb iteration */
+ "movl $sstep_buf1, %%esi\n"
+ "movl $sstep_buf2, %%edi\n"
+ "movl $0, %%ecx\n"
+ "rep movsb\n"
+ "movl $3, %%ecx\n"
+ "rep movsb\n"
+ "movl $1, %%ecx\n"
+ "rep movsb\n"
+
+ /* cmpsb: the single step should stop at each cmpsb iteration */
+ "movl $sstep_buf1, %%esi\n"
+ "movl $sstep_buf2, %%edi\n"
+ "movl $0, %%ecx\n"
+ "rep cmpsb\n"
+ "movl $4, %%ecx\n"
+ "rep cmpsb\n"
+
+ /* getpid() syscall: single step should skip one
+ instruction */
+ "movl $20, %%eax\n"
+ "int $0x80\n"
+ "movl $0, %%eax\n"
+
+ /* when modifying SS, trace is not done on the next
+ instruction */
+ "movl %%ss, %%ecx\n"
+ "movl %%ecx, %%ss\n"
+ "addl $1, %0\n"
+ "movl $1, %%eax\n"
+ "movl %%ecx, %%ss\n"
+ "jmp 1f\n"
+ "addl $1, %0\n"
+ "1:\n"
+ "movl $1, %%eax\n"
+ "pushl %%ecx\n"
+ "popl %%ss\n"
+ "addl $1, %0\n"
+ "movl $1, %%eax\n"
+
+ "pushf\n"
+ "andl $~0x00100, (%%esp)\n"
+ "popf\n"
+ : "=m" (val)
+ :
+ : "cc", "memory", "eax", "ecx", "esi", "edi");
+ printf("val=%d\n", val);
+ for(i = 0; i < 4; i++)
+ printf("sstep_buf2[%d] = %d\n", i, sstep_buf2[i]);
+}
+
+/* self modifying code test */
+uint8_t code[] = {
+ 0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */
+ 0xc3, /* ret */
+};
+
+asm("smc_code2:\n"
+ "movl 4(%esp), %eax\n"
+ "movl %eax, smc_patch_addr2 + 1\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "smc_patch_addr2:\n"
+ "movl $1, %eax\n"
+ "ret\n");
+
+typedef int FuncType(void);
+extern int smc_code2(int);
+void test_self_modifying_code(void)
+{
+ int i;
+
+ printf("self modifying code:\n");
+ printf("func1 = 0x%x\n", ((FuncType *)code)());
+ for(i = 2; i <= 4; i++) {
+ code[1] = i;
+ printf("func%d = 0x%x\n", i, ((FuncType *)code)());
+ }
+
+ /* more difficult test : the modified code is just after the
+ modifying instruction. It is forbidden in Intel specs, but it
+ is used by old DOS programs */
+ for(i = 2; i <= 4; i++) {
+ printf("smc_code2(%d) = %d\n", i, smc_code2(i));
+ }
+}
+#endif
+
+long enter_stack[4096];
+
+#if defined(__x86_64__)
+#define RSP "%%rsp"
+#define RBP "%%rbp"
+#else
+#define RSP "%%esp"
+#define RBP "%%ebp"
+#endif
+
+#define TEST_ENTER(size, stack_type, level)\
+{\
+ long esp_save, esp_val, ebp_val, ebp_save, i;\
+ stack_type *ptr, *stack_end, *stack_ptr;\
+ memset(enter_stack, 0, sizeof(enter_stack));\
+ stack_end = stack_ptr = (stack_type *)(enter_stack + 4096);\
+ ebp_val = (long)stack_ptr;\
+ for(i=1;i<=32;i++)\
+ *--stack_ptr = i;\
+ esp_val = (long)stack_ptr;\
+ asm("mov " RSP ", %[esp_save]\n"\
+ "mov " RBP ", %[ebp_save]\n"\
+ "mov %[esp_val], " RSP "\n"\
+ "mov %[ebp_val], " RBP "\n"\
+ "enter" size " $8, $" #level "\n"\
+ "mov " RSP ", %[esp_val]\n"\
+ "mov " RBP ", %[ebp_val]\n"\
+ "mov %[esp_save], " RSP "\n"\
+ "mov %[ebp_save], " RBP "\n"\
+ : [esp_save] "=r" (esp_save),\
+ [ebp_save] "=r" (ebp_save),\
+ [esp_val] "=r" (esp_val),\
+ [ebp_val] "=r" (ebp_val)\
+ : "[esp_val]" (esp_val),\
+ "[ebp_val]" (ebp_val));\
+ printf("level=%d:\n", level);\
+ printf("esp_val=" FMTLX "\n", esp_val - (long)stack_end);\
+ printf("ebp_val=" FMTLX "\n", ebp_val - (long)stack_end);\
+ for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\
+ printf(FMTLX "\n", (long)ptr[0]);\
+}
+
+static void test_enter(void)
+{
+#if defined(__x86_64__)
+ TEST_ENTER("q", uint64_t, 0);
+ TEST_ENTER("q", uint64_t, 1);
+ TEST_ENTER("q", uint64_t, 2);
+ TEST_ENTER("q", uint64_t, 31);
+#else
+ TEST_ENTER("l", uint32_t, 0);
+ TEST_ENTER("l", uint32_t, 1);
+ TEST_ENTER("l", uint32_t, 2);
+ TEST_ENTER("l", uint32_t, 31);
+#endif
+
+ TEST_ENTER("w", uint16_t, 0);
+ TEST_ENTER("w", uint16_t, 1);
+ TEST_ENTER("w", uint16_t, 2);
+ TEST_ENTER("w", uint16_t, 31);
+}
+
+#ifdef TEST_SSE
+
+typedef int __m64 __attribute__ ((__mode__ (__V2SI__)));
+typedef int __m128 __attribute__ ((__mode__(__V4SF__)));
+
+typedef union {
+ double d[2];
+ float s[4];
+ uint32_t l[4];
+ uint64_t q[2];
+ __m128 dq;
+} XMMReg;
+
+static uint64_t __attribute__((aligned(16))) test_values[4][2] = {
+ { 0x456723c698694873, 0xdc515cff944a58ec },
+ { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 },
+ { 0x007c62c2085427f8, 0x231be9e8cde7438d },
+ { 0x0f76255a085427f8, 0xc233e9e8c4c9439a },
+};
+
+#define SSE_OP(op)\
+{\
+ asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define SSE_OP2(op)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ b.q[0] = test_values[2*i+1][0];\
+ b.q[1] = test_values[2*i+1][1];\
+ SSE_OP(op);\
+ }\
+}
+
+#define MMX_OP2(op)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ b.q[0] = test_values[2*i+1][0];\
+ asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\
+ printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\
+ #op,\
+ a.q[0],\
+ b.q[0],\
+ r.q[0]);\
+ }\
+ SSE_OP2(op);\
+}
+
+#define SHUF_OP(op, ib)\
+{\
+ a.q[0] = test_values[0][0];\
+ a.q[1] = test_values[0][1];\
+ b.q[0] = test_values[1][0];\
+ b.q[1] = test_values[1][1];\
+ asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+}
+
+#define PSHUF_OP(op, ib)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define SHIFT_IM(op, ib)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define SHIFT_OP(op, ib)\
+{\
+ int i;\
+ SHIFT_IM(op, ib);\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ b.q[0] = ib;\
+ b.q[1] = 0;\
+ asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define MOVMSK(op)\
+{\
+ int i, reg;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ reg);\
+ }\
+}
+
+#define SSE_OPS(a) \
+SSE_OP(a ## ps);\
+SSE_OP(a ## ss);
+
+#define SSE_OPD(a) \
+SSE_OP(a ## pd);\
+SSE_OP(a ## sd);
+
+#define SSE_COMI(op, field)\
+{\
+ unsigned int eflags;\
+ XMMReg a, b;\
+ a.field[0] = a1;\
+ b.field[0] = b1;\
+ asm volatile (#op " %2, %1\n"\
+ "pushf\n"\
+ "pop %0\n"\
+ : "=m" (eflags)\
+ : "x" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=%f b=%f cc=%04x\n",\
+ #op, a1, b1,\
+ eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
+}
+
+void test_sse_comi(double a1, double b1)
+{
+ SSE_COMI(ucomiss, s);
+ SSE_COMI(ucomisd, d);
+ SSE_COMI(comiss, s);
+ SSE_COMI(comisd, d);
+}
+
+#define CVT_OP_XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+/* Force %xmm0 usage to avoid the case where both register index are 0
+ to test intruction decoding more extensively */
+#define CVT_OP_XMM2MMX(op)\
+{\
+ asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \
+ : "%xmm0");\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.q[0]);\
+}
+
+#define CVT_OP_MMX2XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\
+ printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_REG2XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\
+ printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.l[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_XMM2REG(op)\
+{\
+ asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.l[0]);\
+}
+
+struct fpxstate {
+ uint16_t fpuc;
+ uint16_t fpus;
+ uint16_t fptag;
+ uint16_t fop;
+ uint32_t fpuip;
+ uint16_t cs_sel;
+ uint16_t dummy0;
+ uint32_t fpudp;
+ uint16_t ds_sel;
+ uint16_t dummy1;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint8_t fpregs1[8 * 16];
+ uint8_t xmm_regs[8 * 16];
+ uint8_t dummy2[224];
+};
+
+static struct fpxstate fpx_state __attribute__((aligned(16)));
+static struct fpxstate fpx_state2 __attribute__((aligned(16)));
+
+void test_fxsave(void)
+{
+ struct fpxstate *fp = &fpx_state;
+ struct fpxstate *fp2 = &fpx_state2;
+ int i, nb_xmm;
+ XMMReg a, b;
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ b.q[0] = test_values[1][0];
+ b.q[1] = test_values[1][1];
+
+ asm("movdqa %2, %%xmm0\n"
+ "movdqa %3, %%xmm7\n"
+#if defined(__x86_64__)
+ "movdqa %2, %%xmm15\n"
+#endif
+ " fld1\n"
+ " fldpi\n"
+ " fldln2\n"
+ " fxsave %0\n"
+ " fxrstor %0\n"
+ " fxsave %1\n"
+ " fninit\n"
+ : "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp)
+ : "m" (a), "m" (b));
+ printf("fpuc=%04x\n", fp->fpuc);
+ printf("fpus=%04x\n", fp->fpus);
+ printf("fptag=%04x\n", fp->fptag);
+ for(i = 0; i < 3; i++) {
+ printf("ST%d: " FMT64X " %04x\n",
+ i,
+ *(uint64_t *)&fp->fpregs1[i * 16],
+ *(uint16_t *)&fp->fpregs1[i * 16 + 8]);
+ }
+ printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80);
+#if defined(__x86_64__)
+ nb_xmm = 16;
+#else
+ nb_xmm = 8;
+#endif
+ for(i = 0; i < nb_xmm; i++) {
+ printf("xmm%d: " FMT64X "" FMT64X "\n",
+ i,
+ *(uint64_t *)&fp->xmm_regs[i * 16],
+ *(uint64_t *)&fp->xmm_regs[i * 16 + 8]);
+ }
+}
+
+void test_sse(void)
+{
+ XMMReg r, a, b;
+ int i;
+
+ MMX_OP2(punpcklbw);
+ MMX_OP2(punpcklwd);
+ MMX_OP2(punpckldq);
+ MMX_OP2(packsswb);
+ MMX_OP2(pcmpgtb);
+ MMX_OP2(pcmpgtw);
+ MMX_OP2(pcmpgtd);
+ MMX_OP2(packuswb);
+ MMX_OP2(punpckhbw);
+ MMX_OP2(punpckhwd);
+ MMX_OP2(punpckhdq);
+ MMX_OP2(packssdw);
+ MMX_OP2(pcmpeqb);
+ MMX_OP2(pcmpeqw);
+ MMX_OP2(pcmpeqd);
+
+ MMX_OP2(paddq);
+ MMX_OP2(pmullw);
+ MMX_OP2(psubusb);
+ MMX_OP2(psubusw);
+ MMX_OP2(pminub);
+ MMX_OP2(pand);
+ MMX_OP2(paddusb);
+ MMX_OP2(paddusw);
+ MMX_OP2(pmaxub);
+ MMX_OP2(pandn);
+
+ MMX_OP2(pmulhuw);
+ MMX_OP2(pmulhw);
+
+ MMX_OP2(psubsb);
+ MMX_OP2(psubsw);
+ MMX_OP2(pminsw);
+ MMX_OP2(por);
+ MMX_OP2(paddsb);
+ MMX_OP2(paddsw);
+ MMX_OP2(pmaxsw);
+ MMX_OP2(pxor);
+ MMX_OP2(pmuludq);
+ MMX_OP2(pmaddwd);
+ MMX_OP2(psadbw);
+ MMX_OP2(psubb);
+ MMX_OP2(psubw);
+ MMX_OP2(psubd);
+ MMX_OP2(psubq);
+ MMX_OP2(paddb);
+ MMX_OP2(paddw);
+ MMX_OP2(paddd);
+
+ MMX_OP2(pavgb);
+ MMX_OP2(pavgw);
+
+ asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678));
+ printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]);
+
+ asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678));
+ printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]);
+
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+ printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+ asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+ printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+ asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+ printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+ asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+ printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+ {
+ r.q[0] = -1;
+ r.q[1] = -1;
+
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ b.q[0] = test_values[1][0];
+ b.q[1] = test_values[1][1];
+ asm volatile("maskmovq %1, %0" :
+ : "y" (a.q[0]), "y" (b.q[0]), "D" (&r)
+ : "memory");
+ printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n",
+ "maskmov",
+ r.q[0],
+ a.q[0],
+ b.q[0]);
+ asm volatile("maskmovdqu %1, %0" :
+ : "x" (a.dq), "x" (b.dq), "D" (&r)
+ : "memory");
+ printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n",
+ "maskmov",
+ r.q[1], r.q[0],
+ a.q[1], a.q[0],
+ b.q[1], b.q[0]);
+ }
+
+ asm volatile ("emms");
+
+ SSE_OP2(punpcklqdq);
+ SSE_OP2(punpckhqdq);
+ SSE_OP2(andps);
+ SSE_OP2(andpd);
+ SSE_OP2(andnps);
+ SSE_OP2(andnpd);
+ SSE_OP2(orps);
+ SSE_OP2(orpd);
+ SSE_OP2(xorps);
+ SSE_OP2(xorpd);
+
+ SSE_OP2(unpcklps);
+ SSE_OP2(unpcklpd);
+ SSE_OP2(unpckhps);
+ SSE_OP2(unpckhpd);
+
+ SHUF_OP(shufps, 0x78);
+ SHUF_OP(shufpd, 0x02);
+
+ PSHUF_OP(pshufd, 0x78);
+ PSHUF_OP(pshuflw, 0x78);
+ PSHUF_OP(pshufhw, 0x78);
+
+ SHIFT_OP(psrlw, 7);
+ SHIFT_OP(psrlw, 16);
+ SHIFT_OP(psraw, 7);
+ SHIFT_OP(psraw, 16);
+ SHIFT_OP(psllw, 7);
+ SHIFT_OP(psllw, 16);
+
+ SHIFT_OP(psrld, 7);
+ SHIFT_OP(psrld, 32);
+ SHIFT_OP(psrad, 7);
+ SHIFT_OP(psrad, 32);
+ SHIFT_OP(pslld, 7);
+ SHIFT_OP(pslld, 32);
+
+ SHIFT_OP(psrlq, 7);
+ SHIFT_OP(psrlq, 32);
+ SHIFT_OP(psllq, 7);
+ SHIFT_OP(psllq, 32);
+
+ SHIFT_IM(psrldq, 16);
+ SHIFT_IM(psrldq, 7);
+ SHIFT_IM(pslldq, 16);
+ SHIFT_IM(pslldq, 7);
+
+ MOVMSK(movmskps);
+ MOVMSK(movmskpd);
+
+ /* FPU specific ops */
+
+ {
+ uint32_t mxcsr;
+ asm volatile("stmxcsr %0" : "=m" (mxcsr));
+ printf("mxcsr=%08x\n", mxcsr & 0x1f80);
+ asm volatile("ldmxcsr %0" : : "m" (mxcsr));
+ }
+
+ test_sse_comi(2, -1);
+ test_sse_comi(2, 2);
+ test_sse_comi(2, 3);
+ test_sse_comi(2, q_nan.d);
+ test_sse_comi(q_nan.d, -1);
+
+ for(i = 0; i < 2; i++) {
+ a.s[0] = 2.7;
+ a.s[1] = 3.4;
+ a.s[2] = 4;
+ a.s[3] = -6.3;
+ b.s[0] = 45.7;
+ b.s[1] = 353.4;
+ b.s[2] = 4;
+ b.s[3] = 56.3;
+ if (i == 1) {
+ a.s[0] = q_nan.d;
+ b.s[3] = q_nan.d;
+ }
+
+ SSE_OPS(add);
+ SSE_OPS(mul);
+ SSE_OPS(sub);
+ SSE_OPS(min);
+ SSE_OPS(div);
+ SSE_OPS(max);
+ SSE_OPS(sqrt);
+ SSE_OPS(cmpeq);
+ SSE_OPS(cmplt);
+ SSE_OPS(cmple);
+ SSE_OPS(cmpunord);
+ SSE_OPS(cmpneq);
+ SSE_OPS(cmpnlt);
+ SSE_OPS(cmpnle);
+ SSE_OPS(cmpord);
+
+
+ a.d[0] = 2.7;
+ a.d[1] = -3.4;
+ b.d[0] = 45.7;
+ b.d[1] = -53.4;
+ if (i == 1) {
+ a.d[0] = q_nan.d;
+ b.d[1] = q_nan.d;
+ }
+ SSE_OPD(add);
+ SSE_OPD(mul);
+ SSE_OPD(sub);
+ SSE_OPD(min);
+ SSE_OPD(div);
+ SSE_OPD(max);
+ SSE_OPD(sqrt);
+ SSE_OPD(cmpeq);
+ SSE_OPD(cmplt);
+ SSE_OPD(cmple);
+ SSE_OPD(cmpunord);
+ SSE_OPD(cmpneq);
+ SSE_OPD(cmpnlt);
+ SSE_OPD(cmpnle);
+ SSE_OPD(cmpord);
+ }
+
+ /* float to float/int */
+ a.s[0] = 2.7;
+ a.s[1] = 3.4;
+ a.s[2] = 4;
+ a.s[3] = -6.3;
+ CVT_OP_XMM(cvtps2pd);
+ CVT_OP_XMM(cvtss2sd);
+ CVT_OP_XMM2MMX(cvtps2pi);
+ CVT_OP_XMM2MMX(cvttps2pi);
+ CVT_OP_XMM2REG(cvtss2si);
+ CVT_OP_XMM2REG(cvttss2si);
+ CVT_OP_XMM(cvtps2dq);
+ CVT_OP_XMM(cvttps2dq);
+
+ a.d[0] = 2.6;
+ a.d[1] = -3.4;
+ CVT_OP_XMM(cvtpd2ps);
+ CVT_OP_XMM(cvtsd2ss);
+ CVT_OP_XMM2MMX(cvtpd2pi);
+ CVT_OP_XMM2MMX(cvttpd2pi);
+ CVT_OP_XMM2REG(cvtsd2si);
+ CVT_OP_XMM2REG(cvttsd2si);
+ CVT_OP_XMM(cvtpd2dq);
+ CVT_OP_XMM(cvttpd2dq);
+
+ /* sse/mmx moves */
+ CVT_OP_XMM2MMX(movdq2q);
+ CVT_OP_MMX2XMM(movq2dq);
+
+ /* int to float */
+ a.l[0] = -6;
+ a.l[1] = 2;
+ a.l[2] = 100;
+ a.l[3] = -60000;
+ CVT_OP_MMX2XMM(cvtpi2ps);
+ CVT_OP_MMX2XMM(cvtpi2pd);
+ CVT_OP_REG2XMM(cvtsi2ss);
+ CVT_OP_REG2XMM(cvtsi2sd);
+ CVT_OP_XMM(cvtdq2ps);
+ CVT_OP_XMM(cvtdq2pd);
+
+ /* XXX: test PNI insns */
+#if 0
+ SSE_OP2(movshdup);
+#endif
+ asm volatile ("emms");
+}
+
+#endif
+
+extern void *__start_initcall;
+extern void *__stop_initcall;
+
+
+int main(int argc, char **argv)
+{
+ void **ptr;
+ void (*func)(void);
+
+ ptr = &__start_initcall;
+ while (ptr != &__stop_initcall) {
+ func = *ptr++;
+ func();
+ }
+ test_bsx();
+ test_mul();
+ test_jcc();
+ test_floats();
+#if !defined(__x86_64__)
+ test_bcd();
+#endif
+ test_xchg();
+ test_string();
+ test_misc();
+ test_lea();
+#ifdef TEST_SEGS
+ test_segs();
+ test_code16();
+#endif
+#ifdef TEST_VM86
+ test_vm86();
+#endif
+ test_exceptions();
+#if !defined(__x86_64__)
+ test_self_modifying_code();
+ test_single_step();
+#endif
+ test_enter();
+#ifdef TEST_SSE
+ test_sse();
+ test_fxsave();
+#endif
+ return 0;
+}
diff --git a/tests/test-i386.h b/tests/test-i386.h
new file mode 100644
index 0000000..75106b8
--- /dev/null
+++ b/tests/test-i386.h
@@ -0,0 +1,152 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#define EXECOP2(size, rsize, res, s1, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "q" (s1), "0" (res), "1" (flags)); \
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+ stringify(OP) size, s0, s1, res, iflags, flags & CC_MASK);
+
+#define EXECOP1(size, rsize, res, flags) \
+ asm ("push %3\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "0" (res), "1" (flags)); \
+ printf("%-10s A=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+ stringify(OP) size, s0, res, iflags, flags & CC_MASK);
+
+#ifdef OP1
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("q", "", res, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("l", "k", res, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("w", "w", res, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("b", "b", res, flags);
+}
+#else
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("q", "", res, s1, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("l", "k", res, s1, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("w", "w", res, s1, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("b", "b", res, s1, flags);
+}
+#endif
+
+void exec_op(long s0, long s1)
+{
+ s0 = i2l(s0);
+ s1 = i2l(s1);
+#if defined(__x86_64__)
+ exec_opq(s0, s1, 0);
+#endif
+ exec_opl(s0, s1, 0);
+ exec_opw(s0, s1, 0);
+ exec_opb(s0, s1, 0);
+#ifdef OP_CC
+#if defined(__x86_64__)
+ exec_opq(s0, s1, CC_C);
+#endif
+ exec_opl(s0, s1, CC_C);
+ exec_opw(s0, s1, CC_C);
+ exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+ exec_op(0x12345678, 0x812FADA);
+ exec_op(0x12341, 0x12341);
+ exec_op(0x12341, -0x12341);
+ exec_op(0xffffffff, 0);
+ exec_op(0xffffffff, -1);
+ exec_op(0xffffffff, 1);
+ exec_op(0xffffffff, 2);
+ exec_op(0x7fffffff, 0);
+ exec_op(0x7fffffff, 1);
+ exec_op(0x7fffffff, -1);
+ exec_op(0x80000000, -1);
+ exec_op(0x80000000, 1);
+ exec_op(0x80000000, -2);
+ exec_op(0x12347fff, 0);
+ exec_op(0x12347fff, 1);
+ exec_op(0x12347fff, -1);
+ exec_op(0x12348000, -1);
+ exec_op(0x12348000, 1);
+ exec_op(0x12348000, -2);
+ exec_op(0x12347f7f, 0);
+ exec_op(0x12347f7f, 1);
+ exec_op(0x12347f7f, -1);
+ exec_op(0x12348080, -1);
+ exec_op(0x12348080, 1);
+ exec_op(0x12348080, -2);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC
diff --git a/tests/test_path.c b/tests/test_path.c
new file mode 100644
index 0000000..a9b52de
--- /dev/null
+++ b/tests/test_path.c
@@ -0,0 +1,152 @@
+/* Test path override code */
+#define _GNU_SOURCE
+#include "../path.c"
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* Any log message kills the test. */
+void gemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "FATAL: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+#define NO_CHANGE(_path) \
+ do { \
+ if (strcmp(path(_path), _path) != 0) return __LINE__; \
+ } while(0)
+
+#define CHANGE_TO(_path, _newpath) \
+ do { \
+ if (strcmp(path(_path), _newpath) != 0) return __LINE__; \
+ } while(0)
+
+static void cleanup(void)
+{
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5");
+ rmdir("/tmp/qemu-test_path/DIR1/DIR2");
+ rmdir("/tmp/qemu-test_path/DIR1/DIR3");
+ rmdir("/tmp/qemu-test_path/DIR1");
+ rmdir("/tmp/qemu-test_path");
+}
+
+static unsigned int do_test(void)
+{
+ if (mkdir("/tmp/qemu-test_path", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0)
+ return __LINE__;
+
+ init_paths("/tmp/qemu-test_path");
+
+ NO_CHANGE("/tmp");
+ NO_CHANGE("/tmp/");
+ NO_CHANGE("/tmp/qemu-test_path");
+ NO_CHANGE("/tmp/qemu-test_path/");
+ NO_CHANGE("/tmp/qemu-test_path/D");
+ NO_CHANGE("/tmp/qemu-test_path/DI");
+ NO_CHANGE("/tmp/qemu-test_path/DIR");
+ NO_CHANGE("/tmp/qemu-test_path/DIR1");
+ NO_CHANGE("/tmp/qemu-test_path/DIR1/");
+
+ NO_CHANGE("/D");
+ NO_CHANGE("/DI");
+ NO_CHANGE("/DIR");
+ NO_CHANGE("/DIR2");
+ NO_CHANGE("/DIR1.");
+
+ CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1");
+
+ NO_CHANGE("/DIR1/D");
+ NO_CHANGE("/DIR1/DI");
+ NO_CHANGE("/DIR1/DIR");
+ NO_CHANGE("/DIR1/DIR1");
+
+ CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
+ CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2");
+
+ CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3");
+ CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3");
+
+ NO_CHANGE("/DIR1/DIR2/F");
+ NO_CHANGE("/DIR1/DIR2/FI");
+ NO_CHANGE("/DIR1/DIR2/FIL");
+ NO_CHANGE("/DIR1/DIR2/FIL.");
+
+ CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2");
+ CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3");
+ CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4");
+ CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5");
+
+ NO_CHANGE("/DIR1/DIR2/FILE6");
+ NO_CHANGE("/DIR1/DIR2/FILE/X");
+
+ CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
+ CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+
+ NO_CHANGE("/DIR1/DIR2/../DIR1");
+ NO_CHANGE("/DIR1/DIR2/../FILE");
+
+ CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = do_test();
+ cleanup();
+ if (ret) {
+ fprintf(stderr, "test_path: failed on line %i\n", ret);
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/tests/testthread.c b/tests/testthread.c
new file mode 100644
index 0000000..27e4825
--- /dev/null
+++ b/tests/testthread.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sched.h>
+
+void *thread1_func(void *arg)
+{
+ int i;
+ char buf[512];
+
+ for(i=0;i<10;i++) {
+ snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
+ write(1, buf, strlen(buf));
+ usleep(100 * 1000);
+ }
+ return NULL;
+}
+
+void *thread2_func(void *arg)
+{
+ int i;
+ char buf[512];
+ for(i=0;i<20;i++) {
+ snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
+ write(1, buf, strlen(buf));
+ usleep(150 * 1000);
+ }
+ return NULL;
+}
+
+void test_pthread(void)
+{
+ pthread_t tid1, tid2;
+
+ pthread_create(&tid1, NULL, thread1_func, "hello1");
+ pthread_create(&tid2, NULL, thread2_func, "hello2");
+ pthread_join(tid1, NULL);
+ pthread_join(tid2, NULL);
+ printf("End of pthread test.\n");
+}
+
+int main(int argc, char **argv)
+{
+ test_pthread();
+ return 0;
+}
diff --git a/texi2pod.pl b/texi2pod.pl
new file mode 100755
index 0000000..176627e
--- /dev/null
+++ b/texi2pod.pl
@@ -0,0 +1,428 @@
+#! /usr/bin/perl -w
+
+# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+# This file is part of GNU CC.
+
+# GNU CC 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, or (at your option)
+# any later version.
+
+# GNU CC 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 GNU CC; see the file COPYING. If not, write to
+# the Free Software Foundation, 59 Temple Place - Suite 330,
+# Boston MA 02111-1307, USA.
+
+# This does trivial (and I mean _trivial_) conversion of Texinfo
+# markup to Perl POD format. It's intended to be used to extract
+# something suitable for a manpage from a Texinfo document.
+
+$output = 0;
+$skipping = 0;
+%sects = ();
+$section = "";
+@icstack = ();
+@endwstack = ();
+@skstack = ();
+@instack = ();
+$shift = "";
+%defs = ();
+$fnno = 1;
+$inf = "";
+$ibase = "";
+
+while ($_ = shift) {
+ if (/^-D(.*)$/) {
+ if ($1 ne "") {
+ $flag = $1;
+ } else {
+ $flag = shift;
+ }
+ $value = "";
+ ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
+ die "no flag specified for -D\n"
+ unless $flag ne "";
+ die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
+ unless $flag =~ /^[a-zA-Z0-9_-]+$/;
+ $defs{$flag} = $value;
+ } elsif (/^-/) {
+ usage();
+ } else {
+ $in = $_, next unless defined $in;
+ $out = $_, next unless defined $out;
+ usage();
+ }
+}
+
+if (defined $in) {
+ $inf = gensym();
+ open($inf, "<$in") or die "opening \"$in\": $!\n";
+ $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
+} else {
+ $inf = \*STDIN;
+}
+
+if (defined $out) {
+ open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
+}
+
+while(defined $inf) {
+while(<$inf>) {
+ # Certain commands are discarded without further processing.
+ /^\@(?:
+ [a-z]+index # @*index: useful only in complete manual
+ |need # @need: useful only in printed manual
+ |(?:end\s+)?group # @group .. @end group: ditto
+ |page # @page: ditto
+ |node # @node: useful only in .info file
+ |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents
+ )\b/x and next;
+
+ chomp;
+
+ # Look for filename and title markers.
+ /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
+ /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
+
+ # Identify a man title but keep only the one we are interested in.
+ /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
+ if (exists $defs{$1}) {
+ $fn = $1;
+ $tl = postprocess($2);
+ }
+ next;
+ };
+
+ # Look for blocks surrounded by @c man begin SECTION ... @c man end.
+ # This really oughta be @ifman ... @end ifman and the like, but such
+ # would require rev'ing all other Texinfo translators.
+ /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
+ $output = 1 if exists $defs{$2};
+ $sect = $1;
+ next;
+ };
+ /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
+ /^\@c\s+man\s+end/ and do {
+ $sects{$sect} = "" unless exists $sects{$sect};
+ $sects{$sect} .= postprocess($section);
+ $section = "";
+ $output = 0;
+ next;
+ };
+
+ # handle variables
+ /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
+ $defs{$1} = $2;
+ next;
+ };
+ /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
+ delete $defs{$1};
+ next;
+ };
+
+ next unless $output;
+
+ # Discard comments. (Can't do it above, because then we'd never see
+ # @c man lines.)
+ /^\@c\b/ and next;
+
+ # End-block handler goes up here because it needs to operate even
+ # if we are skipping.
+ /^\@end\s+([a-z]+)/ and do {
+ # Ignore @end foo, where foo is not an operation which may
+ # cause us to skip, if we are presently skipping.
+ my $ended = $1;
+ next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/;
+
+ die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
+ die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
+
+ $endw = pop @endwstack;
+
+ if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
+ $skipping = pop @skstack;
+ next;
+ } elsif ($ended =~ /^(?:example|smallexample|display)$/) {
+ $shift = "";
+ $_ = ""; # need a paragraph break
+ } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
+ $_ = "\n=back\n";
+ $ic = pop @icstack;
+ } else {
+ die "unknown command \@end $ended at line $.\n";
+ }
+ };
+
+ # We must handle commands which can cause skipping even while we
+ # are skipping, otherwise we will not process nested conditionals
+ # correctly.
+ /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = "ifset";
+ $skipping = 1 unless exists $defs{$1};
+ next;
+ };
+
+ /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = "ifclear";
+ $skipping = 1 if exists $defs{$1};
+ next;
+ };
+
+ /^\@(ignore|menu|iftex)\b/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = $1;
+ $skipping = 1;
+ next;
+ };
+
+ next if $skipping;
+
+ # Character entities. First the ones that can be replaced by raw text
+ # or discarded outright:
+ s/\@copyright\{\}/(c)/g;
+ s/\@dots\{\}/.../g;
+ s/\@enddots\{\}/..../g;
+ s/\@([.!? ])/$1/g;
+ s/\@[:-]//g;
+ s/\@bullet(?:\{\})?/*/g;
+ s/\@TeX\{\}/TeX/g;
+ s/\@pounds\{\}/\#/g;
+ s/\@minus(?:\{\})?/-/g;
+ s/\\,/,/g;
+
+ # Now the ones that have to be replaced by special escapes
+ # (which will be turned back into text by unmunge())
+ s/&/&amp;/g;
+ s/\@\{/&lbrace;/g;
+ s/\@\}/&rbrace;/g;
+ s/\@\@/&at;/g;
+
+ # Inside a verbatim block, handle @var specially.
+ if ($shift ne "") {
+ s/\@var\{([^\}]*)\}/<$1>/g;
+ }
+
+ # POD doesn't interpret E<> inside a verbatim block.
+ if ($shift eq "") {
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ } else {
+ s/</&LT;/g;
+ s/>/&GT;/g;
+ }
+
+ # Single line command handlers.
+
+ /^\@include\s+(.+)$/ and do {
+ push @instack, $inf;
+ $inf = gensym();
+
+ # Try cwd and $ibase.
+ open($inf, "<" . $1)
+ or open($inf, "<" . $ibase . "/" . $1)
+ or die "cannot open $1 or $ibase/$1: $!\n";
+ next;
+ };
+
+ /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
+ and $_ = "\n=head2 $1\n";
+ /^\@subsection\s+(.+)$/
+ and $_ = "\n=head3 $1\n";
+
+ # Block command handlers:
+ /^\@itemize\s+(\@[a-z]+|\*|-)/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ $ic = $1;
+ $_ = "\n=over 4\n";
+ $endw = "itemize";
+ };
+
+ /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ if (defined $1) {
+ $ic = $1 . ".";
+ } else {
+ $ic = "1.";
+ }
+ $_ = "\n=over 4\n";
+ $endw = "enumerate";
+ };
+
+ /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ $endw = $1;
+ $ic = $2;
+ $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
+ $ic =~ s/\@(?:code|kbd)/C/;
+ $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
+ $ic =~ s/\@(?:file)/F/;
+ $_ = "\n=over 4\n";
+ };
+
+ /^\@((?:small)?example|display)/ and do {
+ push @endwstack, $endw;
+ $endw = $1;
+ $shift = "\t";
+ $_ = ""; # need a paragraph break
+ };
+
+ /^\@itemx?\s*(.+)?$/ and do {
+ if (defined $1) {
+ # Entity escapes prevent munging by the <> processing below.
+# print "$ic\n";
+ $_ = "\n=item $ic\&LT;$1\&GT;\n";
+ } else {
+ $_ = "\n=item $ic\n";
+ $ic =~ y/A-Ya-y/B-Zb-z/;
+ $ic =~ s/(\d+)/$1 + 1/eg;
+ }
+ };
+
+ $section .= $shift.$_."\n";
+}
+# End of current file.
+close($inf);
+$inf = pop @instack;
+}
+
+die "No filename or title\n" unless defined $fn && defined $tl;
+
+$sects{NAME} = "$fn \- $tl\n";
+$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
+
+for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
+ BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
+ if(exists $sects{$sect}) {
+ $head = $sect;
+ $head =~ s/SEEALSO/SEE ALSO/;
+ print "=head1 $head\n\n";
+ print scalar unmunge ($sects{$sect});
+ print "\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 [-D toggle...] [infile [outfile]]\n";
+}
+
+sub postprocess
+{
+ local $_ = $_[0];
+
+ # @value{foo} is replaced by whatever 'foo' is defined as.
+ while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
+ if (! exists $defs{$2}) {
+ print STDERR "Option $2 not defined\n";
+ s/\Q$1\E//;
+ } else {
+ $value = $defs{$2};
+ s/\Q$1\E/$value/;
+ }
+ }
+
+ # Formatting commands.
+ # Temporary escape for @r.
+ s/\@r\{([^\}]*)\}/R<$1>/g;
+ s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
+ s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
+ s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
+ s/\@sc\{([^\}]*)\}/\U$1/g;
+ s/\@file\{([^\}]*)\}/F<$1>/g;
+ s/\@w\{([^\}]*)\}/S<$1>/g;
+ s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
+
+ # Cross references are thrown away, as are @noindent and @refill.
+ # (@noindent is impossible in .pod, and @refill is unnecessary.)
+ # @* is also impossible in .pod; we discard it and any newline that
+ # follows it. Similarly, our macro @gol must be discarded.
+
+ s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
+ s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
+ s/;\s+\@pxref\{(?:[^\}]*)\}//g;
+ s/\@noindent\s*//g;
+ s/\@refill//g;
+ s/\@gol//g;
+ s/\@\*\s*\n?//g;
+
+ # @uref can take one, two, or three arguments, with different
+ # semantics each time. @url and @email are just like @uref with
+ # one argument, for our purposes.
+ s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
+ s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
+ s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
+
+ # Turn B<blah I<blah> blah> into B<blah> I<blah> B<blah> to
+ # match Texinfo semantics of @emph inside @samp. Also handle @r
+ # inside bold.
+ s/&LT;/</g;
+ s/&GT;/>/g;
+ 1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B</g;
+ 1 while (s/B<([^<>]*)I<([^>]+)>/B<$1>I<$2>B</g);
+ 1 while (s/I<([^<>]*)B<([^>]+)>/I<$1>B<$2>I</g);
+ s/[BI]<>//g;
+ s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
+ s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
+
+ # Extract footnotes. This has to be done after all other
+ # processing because otherwise the regexp will choke on formatting
+ # inside @footnote.
+ while (/\@footnote/g) {
+ s/\@footnote\{([^\}]+)\}/[$fnno]/;
+ add_footnote($1, $fnno);
+ $fnno++;
+ }
+
+ return $_;
+}
+
+sub unmunge
+{
+ # Replace escaped symbols with their equivalents.
+ local $_ = $_[0];
+
+ s/&lt;/E<lt>/g;
+ s/&gt;/E<gt>/g;
+ s/&lbrace;/\{/g;
+ s/&rbrace;/\}/g;
+ s/&at;/\@/g;
+ s/&amp;/&/g;
+ return $_;
+}
+
+sub add_footnote
+{
+ unless (exists $sects{FOOTNOTES}) {
+ $sects{FOOTNOTES} = "\n=over 4\n\n";
+ }
+
+ $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
+ $sects{FOOTNOTES} .= $_[0];
+ $sects{FOOTNOTES} .= "\n\n";
+}
+
+# stolen from Symbol.pm
+{
+ my $genseq = 0;
+ sub gensym
+ {
+ my $name = "GEN" . $genseq++;
+ my $ref = \*{$name};
+ delete $::{$name};
+ return $ref;
+ }
+}
diff --git a/thunk.c b/thunk.c
new file mode 100644
index 0000000..bc9bd28
--- /dev/null
+++ b/thunk.c
@@ -0,0 +1,243 @@
+/*
+ * Generic thunking code to convert data between host and target CPU
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "qemu.h"
+#include "thunk.h"
+
+//#define DEBUG
+
+#define MAX_STRUCTS 128
+
+/* XXX: make it dynamic */
+StructEntry struct_entries[MAX_STRUCTS];
+
+static inline const argtype *thunk_type_next(const argtype *type_ptr)
+{
+ int type;
+
+ type = *type_ptr++;
+ switch(type) {
+ case TYPE_CHAR:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ return type_ptr;
+ case TYPE_PTR:
+ return thunk_type_next(type_ptr);
+ case TYPE_ARRAY:
+ return thunk_type_next(type_ptr + 1);
+ case TYPE_STRUCT:
+ return type_ptr + 1;
+ default:
+ return NULL;
+ }
+}
+
+void thunk_register_struct(int id, const char *name, const argtype *types)
+{
+ const argtype *type_ptr;
+ StructEntry *se;
+ int nb_fields, offset, max_align, align, size, i, j;
+
+ se = struct_entries + id;
+
+ /* first we count the number of fields */
+ type_ptr = types;
+ nb_fields = 0;
+ while (*type_ptr != TYPE_NULL) {
+ type_ptr = thunk_type_next(type_ptr);
+ nb_fields++;
+ }
+ se->field_types = types;
+ se->nb_fields = nb_fields;
+ se->name = name;
+#ifdef DEBUG
+ printf("struct %s: id=%d nb_fields=%d\n",
+ se->name, id, se->nb_fields);
+#endif
+ /* now we can alloc the data */
+
+ for(i = 0;i < 2; i++) {
+ offset = 0;
+ max_align = 1;
+ se->field_offsets[i] = malloc(nb_fields * sizeof(int));
+ type_ptr = se->field_types;
+ for(j = 0;j < nb_fields; j++) {
+ size = thunk_type_size(type_ptr, i);
+ align = thunk_type_align(type_ptr, i);
+ offset = (offset + align - 1) & ~(align - 1);
+ se->field_offsets[i][j] = offset;
+ offset += size;
+ if (align > max_align)
+ max_align = align;
+ type_ptr = thunk_type_next(type_ptr);
+ }
+ offset = (offset + max_align - 1) & ~(max_align - 1);
+ se->size[i] = offset;
+ se->align[i] = max_align;
+#ifdef DEBUG
+ printf("%s: size=%d align=%d\n",
+ i == THUNK_HOST ? "host" : "target", offset, max_align);
+#endif
+ }
+}
+
+void thunk_register_struct_direct(int id, const char *name, StructEntry *se1)
+{
+ StructEntry *se;
+ se = struct_entries + id;
+ *se = *se1;
+ se->name = name;
+}
+
+
+/* now we can define the main conversion functions */
+const argtype *thunk_convert(void *dst, const void *src,
+ const argtype *type_ptr, int to_host)
+{
+ int type;
+
+ type = *type_ptr++;
+ switch(type) {
+ case TYPE_CHAR:
+ *(uint8_t *)dst = *(uint8_t *)src;
+ break;
+ case TYPE_SHORT:
+ *(uint16_t *)dst = tswap16(*(uint16_t *)src);
+ break;
+ case TYPE_INT:
+ *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+ break;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ *(uint64_t *)dst = tswap64(*(uint64_t *)src);
+ break;
+#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+ break;
+#elif HOST_LONG_BITS == 64 && TARGET_LONG_BITS == 32
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ if (to_host) {
+ *(uint64_t *)dst = tswap32(*(uint32_t *)src);
+ } else {
+ *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
+ }
+ break;
+#else
+#warning unsupported conversion
+#endif
+ case TYPE_ARRAY:
+ {
+ int array_length, i, dst_size, src_size;
+ const uint8_t *s;
+ uint8_t *d;
+
+ array_length = *type_ptr++;
+ dst_size = thunk_type_size(type_ptr, to_host);
+ src_size = thunk_type_size(type_ptr, 1 - to_host);
+ d = dst;
+ s = src;
+ for(i = 0;i < array_length; i++) {
+ thunk_convert(d, s, type_ptr, to_host);
+ d += dst_size;
+ s += src_size;
+ }
+ type_ptr = thunk_type_next(type_ptr);
+ }
+ break;
+ case TYPE_STRUCT:
+ {
+ int i;
+ const StructEntry *se;
+ const uint8_t *s;
+ uint8_t *d;
+ const argtype *field_types;
+ const int *dst_offsets, *src_offsets;
+
+ se = struct_entries + *type_ptr++;
+ if (se->convert[0] != NULL) {
+ /* specific conversion is needed */
+ (*se->convert[to_host])(dst, src);
+ } else {
+ /* standard struct conversion */
+ field_types = se->field_types;
+ dst_offsets = se->field_offsets[to_host];
+ src_offsets = se->field_offsets[1 - to_host];
+ d = dst;
+ s = src;
+ for(i = 0;i < se->nb_fields; i++) {
+ field_types = thunk_convert(d + dst_offsets[i],
+ s + src_offsets[i],
+ field_types, to_host);
+ }
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Invalid type 0x%x\n", type);
+ break;
+ }
+ return type_ptr;
+}
+
+/* from em86 */
+
+/* Utility function: Table-driven functions to translate bitmasks
+ * between X86 and Alpha formats...
+ */
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+ bitmask_transtbl * trans_tbl)
+{
+ bitmask_transtbl * btp;
+ unsigned int alpha_mask = 0;
+
+ for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+ if((x86_mask & btp->x86_mask) == btp->x86_bits) {
+ alpha_mask |= btp->alpha_bits;
+ }
+ }
+ return(alpha_mask);
+}
+
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+ bitmask_transtbl * trans_tbl)
+{
+ bitmask_transtbl * btp;
+ unsigned int x86_mask = 0;
+
+ for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+ if((alpha_mask & btp->alpha_mask) == btp->alpha_bits) {
+ x86_mask |= btp->x86_bits;
+ }
+ }
+ return(x86_mask);
+}
diff --git a/thunk.h b/thunk.h
new file mode 100644
index 0000000..42fd96f
--- /dev/null
+++ b/thunk.h
@@ -0,0 +1,158 @@
+/*
+ * Generic thunking code to convert data between host and target CPU
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef THUNK_H
+#define THUNK_H
+
+#include <inttypes.h>
+#include "cpu.h"
+
+/* types enums definitions */
+
+typedef enum argtype {
+ TYPE_NULL,
+ TYPE_CHAR,
+ TYPE_SHORT,
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_ULONG,
+ TYPE_PTRVOID, /* pointer on unknown data */
+ TYPE_LONGLONG,
+ TYPE_ULONGLONG,
+ TYPE_PTR,
+ TYPE_ARRAY,
+ TYPE_STRUCT,
+} argtype;
+
+#define MK_PTR(type) TYPE_PTR, type
+#define MK_ARRAY(type, size) TYPE_ARRAY, size, type
+#define MK_STRUCT(id) TYPE_STRUCT, id
+
+#define THUNK_TARGET 0
+#define THUNK_HOST 1
+
+typedef struct {
+ /* standard struct handling */
+ const argtype *field_types;
+ int nb_fields;
+ int *field_offsets[2];
+ /* special handling */
+ void (*convert[2])(void *dst, const void *src);
+ int size[2];
+ int align[2];
+ const char *name;
+} StructEntry;
+
+/* Translation table for bitmasks... */
+typedef struct bitmask_transtbl {
+ unsigned int x86_mask;
+ unsigned int x86_bits;
+ unsigned int alpha_mask;
+ unsigned int alpha_bits;
+} bitmask_transtbl;
+
+void thunk_register_struct(int id, const char *name, const argtype *types);
+void thunk_register_struct_direct(int id, const char *name, StructEntry *se1);
+const argtype *thunk_convert(void *dst, const void *src,
+ const argtype *type_ptr, int to_host);
+#ifndef NO_THUNK_TYPE_SIZE
+
+extern StructEntry struct_entries[];
+
+static inline int thunk_type_size(const argtype *type_ptr, int is_host)
+{
+ int type, size;
+ const StructEntry *se;
+
+ type = *type_ptr;
+ switch(type) {
+ case TYPE_CHAR:
+ return 1;
+ case TYPE_SHORT:
+ return 2;
+ case TYPE_INT:
+ return 4;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ return 8;
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ case TYPE_PTR:
+ if (is_host) {
+ return HOST_LONG_SIZE;
+ } else {
+ return TARGET_LONG_SIZE;
+ }
+ break;
+ case TYPE_ARRAY:
+ size = type_ptr[1];
+ return size * thunk_type_size(type_ptr + 2, is_host);
+ case TYPE_STRUCT:
+ se = struct_entries + type_ptr[1];
+ return se->size[is_host];
+ default:
+ return -1;
+ }
+}
+
+static inline int thunk_type_align(const argtype *type_ptr, int is_host)
+{
+ int type;
+ const StructEntry *se;
+
+ type = *type_ptr;
+ switch(type) {
+ case TYPE_CHAR:
+ return 1;
+ case TYPE_SHORT:
+ return 2;
+ case TYPE_INT:
+ return 4;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ return 8;
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ case TYPE_PTR:
+ if (is_host) {
+ return HOST_LONG_SIZE;
+ } else {
+ return TARGET_LONG_SIZE;
+ }
+ break;
+ case TYPE_ARRAY:
+ return thunk_type_align(type_ptr + 2, is_host);
+ case TYPE_STRUCT:
+ se = struct_entries + type_ptr[1];
+ return se->align[is_host];
+ default:
+ return -1;
+ }
+}
+
+#endif /* NO_THUNK_TYPE_SIZE */
+
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+ bitmask_transtbl * trans_tbl);
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+ bitmask_transtbl * trans_tbl);
+
+#endif
diff --git a/translate-all.c b/translate-all.c
new file mode 100644
index 0000000..0de429f
--- /dev/null
+++ b/translate-all.c
@@ -0,0 +1,311 @@
+/*
+ * Host code generation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+
+#define NO_CPU_IO_DEFS
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+extern int dyngen_code(uint8_t *gen_code_buf,
+ uint16_t *label_offsets, uint16_t *jmp_offsets,
+ const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels);
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+uint16_t gen_opc_buf[OPC_BUF_SIZE];
+uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
+long gen_labels[OPC_BUF_SIZE];
+int nb_gen_labels;
+
+target_ulong gen_opc_pc[OPC_BUF_SIZE];
+uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+#if defined(TARGET_I386)
+uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+#elif defined(TARGET_SPARC)
+target_ulong gen_opc_npc[OPC_BUF_SIZE];
+target_ulong gen_opc_jump_pc[2];
+#elif defined(TARGET_MIPS)
+uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+#endif
+
+int code_copy_enabled = 1;
+
+#ifdef DEBUG_DISAS
+static const char *op_str[] = {
+#define DEF(s, n, copy_size) #s,
+#include "opc.h"
+#undef DEF
+};
+
+static uint8_t op_nb_args[] = {
+#define DEF(s, n, copy_size) n,
+#include "opc.h"
+#undef DEF
+};
+
+static const unsigned short opc_copy_size[] = {
+#define DEF(s, n, copy_size) copy_size,
+#include "opc.h"
+#undef DEF
+};
+
+void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf)
+{
+ const uint16_t *opc_ptr;
+ const uint32_t *opparam_ptr;
+ int c, n, i;
+
+ opc_ptr = opc_buf;
+ opparam_ptr = opparam_buf;
+ for(;;) {
+ c = *opc_ptr++;
+ n = op_nb_args[c];
+ fprintf(logfile, "0x%04x: %s",
+ (int)(opc_ptr - opc_buf - 1), op_str[c]);
+ for(i = 0; i < n; i++) {
+ fprintf(logfile, " 0x%x", opparam_ptr[i]);
+ }
+ fprintf(logfile, "\n");
+ if (c == INDEX_op_end)
+ break;
+ opparam_ptr += n;
+ }
+}
+
+#endif
+
+/* compute label info */
+static void dyngen_labels(long *gen_labels, int nb_gen_labels,
+ uint8_t *gen_code_buf, const uint16_t *opc_buf)
+{
+ uint8_t *gen_code_ptr;
+ int c, i;
+ unsigned long gen_code_addr[OPC_BUF_SIZE];
+
+ if (nb_gen_labels == 0)
+ return;
+ /* compute the address of each op code */
+
+ gen_code_ptr = gen_code_buf;
+ i = 0;
+ for(;;) {
+ c = opc_buf[i];
+ gen_code_addr[i] =(unsigned long)gen_code_ptr;
+ if (c == INDEX_op_end)
+ break;
+ gen_code_ptr += opc_copy_size[c];
+ i++;
+ }
+
+ /* compute the address of each label */
+ for(i = 0; i < nb_gen_labels; i++) {
+ gen_labels[i] = gen_code_addr[gen_labels[i]];
+ }
+}
+
+/* return non zero if the very first instruction is invalid so that
+ the virtual CPU can trigger an exception.
+
+ '*gen_code_size_ptr' contains the size of the generated code (host
+ code).
+*/
+int cpu_gen_code(CPUState *env, TranslationBlock *tb,
+ int max_code_size, int *gen_code_size_ptr)
+{
+ uint8_t *gen_code_buf;
+ int gen_code_size;
+
+#ifdef USE_CODE_COPY
+ if (code_copy_enabled &&
+ cpu_gen_code_copy(env, tb, max_code_size, &gen_code_size) == 0) {
+ /* nothing more to do */
+ } else
+#endif
+ {
+ if (gen_intermediate_code(env, tb) < 0)
+ return -1;
+
+ /* generate machine code */
+ tb->tb_next_offset[0] = 0xffff;
+ tb->tb_next_offset[1] = 0xffff;
+ gen_code_buf = tb->tc_ptr;
+#ifdef USE_DIRECT_JUMP
+ /* the following two entries are optional (only used for string ops) */
+ tb->tb_jmp_offset[2] = 0xffff;
+ tb->tb_jmp_offset[3] = 0xffff;
+#endif
+ dyngen_labels(gen_labels, nb_gen_labels, gen_code_buf, gen_opc_buf);
+
+ gen_code_size = dyngen_code(gen_code_buf, tb->tb_next_offset,
+#ifdef USE_DIRECT_JUMP
+ tb->tb_jmp_offset,
+#else
+ NULL,
+#endif
+ gen_opc_buf, gen_opparam_buf, gen_labels);
+ }
+ *gen_code_size_ptr = gen_code_size;
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_OUT_ASM) {
+ fprintf(logfile, "OUT: [size=%d]\n", *gen_code_size_ptr);
+ disas(logfile, tb->tc_ptr, *gen_code_size_ptr);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+#endif
+ return 0;
+}
+
+/* The cpu state corresponding to 'searched_pc' is restored.
+ */
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+{
+ int j, c;
+ unsigned long tc_ptr;
+ uint16_t *opc_ptr;
+
+#ifdef USE_CODE_COPY
+ if (tb->cflags & CF_CODE_COPY) {
+ return cpu_restore_state_copy(tb, env, searched_pc, puc);
+ }
+#endif
+ if (gen_intermediate_code_pc(env, tb) < 0)
+ return -1;
+
+ /* find opc index corresponding to search_pc */
+ tc_ptr = (unsigned long)tb->tc_ptr;
+ if (searched_pc < tc_ptr)
+ return -1;
+ j = 0;
+ opc_ptr = gen_opc_buf;
+ for(;;) {
+ c = *opc_ptr;
+ if (c == INDEX_op_end)
+ return -1;
+ tc_ptr += opc_copy_size[c];
+ if (searched_pc < tc_ptr)
+ break;
+ opc_ptr++;
+ }
+ j = opc_ptr - gen_opc_buf;
+ /* now find start of instruction before */
+ while (gen_opc_instr_start[j] == 0)
+ j--;
+#if defined(TARGET_I386)
+ {
+ int cc_op;
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_OP) {
+ int i;
+ fprintf(logfile, "RESTORE:\n");
+ for(i=0;i<=j; i++) {
+ if (gen_opc_instr_start[i]) {
+ fprintf(logfile, "0x%04x: " TARGET_FMT_lx "\n", i, gen_opc_pc[i]);
+ }
+ }
+ fprintf(logfile, "spc=0x%08lx j=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n",
+ searched_pc, j, gen_opc_pc[j] - tb->cs_base,
+ (uint32_t)tb->cs_base);
+ }
+#endif
+ env->eip = gen_opc_pc[j] - tb->cs_base;
+ cc_op = gen_opc_cc_op[j];
+ if (cc_op != CC_OP_DYNAMIC)
+ env->cc_op = cc_op;
+ }
+#elif defined(TARGET_ARM)
+ env->regs[15] = gen_opc_pc[j];
+#elif defined(TARGET_SPARC)
+ {
+ target_ulong npc;
+ env->pc = gen_opc_pc[j];
+ npc = gen_opc_npc[j];
+ if (npc == 1) {
+ /* dynamic NPC: already stored */
+ } else if (npc == 2) {
+ target_ulong t2 = (target_ulong)puc;
+ /* jump PC: use T2 and the jump targets of the translation */
+ if (t2)
+ env->npc = gen_opc_jump_pc[0];
+ else
+ env->npc = gen_opc_jump_pc[1];
+ } else {
+ env->npc = npc;
+ }
+ }
+#elif defined(TARGET_PPC)
+ {
+ int type;
+ /* for PPC, we need to look at the micro operation to get the
+ access type */
+ env->nip = gen_opc_pc[j];
+ switch(c) {
+#if defined(CONFIG_USER_ONLY)
+#define CASE3(op)\
+ case INDEX_op_ ## op ## _raw
+#else
+#define CASE3(op)\
+ case INDEX_op_ ## op ## _user:\
+ case INDEX_op_ ## op ## _kernel
+#endif
+
+ CASE3(stfd):
+ CASE3(stfs):
+ CASE3(lfd):
+ CASE3(lfs):
+ type = ACCESS_FLOAT;
+ break;
+ CASE3(lwarx):
+ type = ACCESS_RES;
+ break;
+ CASE3(stwcx):
+ type = ACCESS_RES;
+ break;
+ CASE3(eciwx):
+ CASE3(ecowx):
+ type = ACCESS_EXT;
+ break;
+ default:
+ type = ACCESS_INT;
+ break;
+ }
+ env->access_type = type;
+ }
+#elif defined(TARGET_MIPS)
+ env->PC = gen_opc_pc[j];
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ env->hflags |= gen_opc_hflags[j];
+#endif
+ return 0;
+}
diff --git a/translate-op.c b/translate-op.c
new file mode 100644
index 0000000..fddac70
--- /dev/null
+++ b/translate-op.c
@@ -0,0 +1,37 @@
+/*
+ * Host code generation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+#include "dyngen.h"
+#include "op.h"
+
diff --git a/usb-linux.c b/usb-linux.c
new file mode 100644
index 0000000..0a13753
--- /dev/null
+++ b/usb-linux.c
@@ -0,0 +1,522 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#if defined(__linux__)
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/compiler.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+
+/* We redefine it to avoid version problems */
+struct usb_ctrltransfer {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint32_t timeout;
+ void *data;
+};
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ char *product_name, int product_name_size,
+ const char *devname);
+
+//#define DEBUG
+
+#define USBDEVFS_PATH "/proc/bus/usb"
+#define PRODUCT_NAME_SZ 32
+
+typedef struct USBHostDevice {
+ USBDevice dev;
+ int fd;
+} USBHostDevice;
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+#if 0
+ USBHostDevice *s = (USBHostDevice *)dev;
+ /* USBDEVFS_RESET, but not the first time as it has already be
+ done by the host OS */
+ ioctl(s->fd, USBDEVFS_RESET);
+#endif
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+
+ if (s->fd >= 0)
+ close(s->fd);
+ qemu_free(s);
+}
+
+static int usb_host_handle_control(USBDevice *dev,
+ int request,
+ int value,
+ int index,
+ int length,
+ uint8_t *data)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+ struct usb_ctrltransfer ct;
+ int ret;
+
+ if (request == (DeviceOutRequest | USB_REQ_SET_ADDRESS)) {
+ /* specific SET_ADDRESS support */
+ dev->addr = value;
+ return 0;
+ } else {
+ ct.bRequestType = request >> 8;
+ ct.bRequest = request;
+ ct.wValue = value;
+ ct.wIndex = index;
+ ct.wLength = length;
+ ct.timeout = 50;
+ ct.data = data;
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ default:
+ return USB_RET_STALL;
+ }
+ } else {
+ return ret;
+ }
+ }
+}
+
+static int usb_host_handle_data(USBDevice *dev, int pid,
+ uint8_t devep,
+ uint8_t *data, int len)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+ struct usbdevfs_bulktransfer bt;
+ int ret;
+
+ /* XXX: optimize and handle all data types by looking at the
+ config descriptor */
+ if (pid == USB_TOKEN_IN)
+ devep |= 0x80;
+ bt.ep = devep;
+ bt.len = len;
+ bt.timeout = 50;
+ bt.data = data;
+ ret = ioctl(s->fd, USBDEVFS_BULK, &bt);
+ if (ret < 0) {
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ case EPIPE:
+ default:
+#ifdef DEBUG
+ printf("handle_data: errno=%d\n", errno);
+#endif
+ return USB_RET_STALL;
+ }
+ } else {
+ return ret;
+ }
+}
+
+/* XXX: exclude high speed devices or implement EHCI */
+USBDevice *usb_host_device_open(const char *devname)
+{
+ int fd, interface, ret, i;
+ USBHostDevice *dev;
+ struct usbdevfs_connectinfo ci;
+ uint8_t descr[1024];
+ char buf[1024];
+ int descr_len, dev_descr_len, config_descr_len, nb_interfaces;
+ int bus_num, addr;
+ char product_name[PRODUCT_NAME_SZ];
+
+ if (usb_host_find_device(&bus_num, &addr,
+ product_name, sizeof(product_name),
+ devname) < 0)
+ return NULL;
+
+ snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
+ bus_num, addr);
+ fd = open(buf, O_RDWR);
+ if (fd < 0) {
+ perror(buf);
+ return NULL;
+ }
+
+ /* read the config description */
+ descr_len = read(fd, descr, sizeof(descr));
+ if (descr_len <= 0) {
+ perror("read descr");
+ goto fail;
+ }
+
+ i = 0;
+ dev_descr_len = descr[0];
+ if (dev_descr_len > descr_len)
+ goto fail;
+ i += dev_descr_len;
+ config_descr_len = descr[i];
+ if (i + config_descr_len > descr_len)
+ goto fail;
+ nb_interfaces = descr[i + 4];
+ if (nb_interfaces != 1) {
+ /* NOTE: currently we grab only one interface */
+ fprintf(stderr, "usb_host: only one interface supported\n");
+ goto fail;
+ }
+
+#ifdef USBDEVFS_DISCONNECT
+ /* earlier Linux 2.4 do not support that */
+ {
+ struct usbdevfs_ioctl ctrl;
+ ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+ ctrl.ifno = 0;
+ ret = ioctl(fd, USBDEVFS_IOCTL, &ctrl);
+ if (ret < 0 && errno != ENODATA) {
+ perror("USBDEVFS_DISCONNECT");
+ goto fail;
+ }
+ }
+#endif
+
+ /* XXX: only grab if all interfaces are free */
+ interface = 0;
+ ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &interface);
+ if (ret < 0) {
+ if (errno == EBUSY) {
+ fprintf(stderr, "usb_host: device already grabbed\n");
+ } else {
+ perror("USBDEVFS_CLAIMINTERFACE");
+ }
+ fail:
+ close(fd);
+ return NULL;
+ }
+
+ ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+ if (ret < 0) {
+ perror("USBDEVFS_CONNECTINFO");
+ goto fail;
+ }
+
+#ifdef DEBUG
+ printf("host USB device %d.%d grabbed\n", bus_num, addr);
+#endif
+
+ dev = qemu_mallocz(sizeof(USBHostDevice));
+ if (!dev)
+ goto fail;
+ dev->fd = fd;
+ if (ci.slow)
+ dev->dev.speed = USB_SPEED_LOW;
+ else
+ dev->dev.speed = USB_SPEED_HIGH;
+ dev->dev.handle_packet = usb_generic_handle_packet;
+
+ dev->dev.handle_reset = usb_host_handle_reset;
+ dev->dev.handle_control = usb_host_handle_control;
+ dev->dev.handle_data = usb_host_handle_data;
+ dev->dev.handle_destroy = usb_host_handle_destroy;
+
+ if (product_name[0] == '\0')
+ snprintf(dev->dev.devname, sizeof(dev->dev.devname),
+ "host:%s", devname);
+ else
+ pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
+ product_name);
+
+ return (USBDevice *)dev;
+}
+
+static int get_tag_value(char *buf, int buf_size,
+ const char *str, const char *tag,
+ const char *stopchars)
+{
+ const char *p;
+ char *q;
+ p = strstr(str, tag);
+ if (!p)
+ return -1;
+ p += strlen(tag);
+ while (isspace(*p))
+ p++;
+ q = buf;
+ while (*p != '\0' && !strchr(stopchars, *p)) {
+ if ((q - buf) < (buf_size - 1))
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ return q - buf;
+}
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+ FILE *f;
+ char line[1024];
+ char buf[1024];
+ int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
+ int ret;
+ char product_name[512];
+
+ f = fopen(USBDEVFS_PATH "/devices", "r");
+ if (!f) {
+ term_printf("Could not open %s\n", USBDEVFS_PATH "/devices");
+ return 0;
+ }
+ device_count = 0;
+ bus_num = addr = speed = class_id = product_id = vendor_id = 0;
+ ret = 0;
+ for(;;) {
+ if (fgets(line, sizeof(line), f) == NULL)
+ break;
+ if (strlen(line) > 0)
+ line[strlen(line) - 1] = '\0';
+ if (line[0] == 'T' && line[1] == ':') {
+ if (device_count && (vendor_id || product_id)) {
+ /* New device. Add the previously discovered device. */
+ ret = func(opaque, bus_num, addr, class_id, vendor_id,
+ product_id, product_name, speed);
+ if (ret)
+ goto the_end;
+ }
+ if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0)
+ goto fail;
+ bus_num = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0)
+ goto fail;
+ addr = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0)
+ goto fail;
+ if (!strcmp(buf, "480"))
+ speed = USB_SPEED_HIGH;
+ else if (!strcmp(buf, "1.5"))
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ product_name[0] = '\0';
+ class_id = 0xff;
+ device_count++;
+ product_id = 0;
+ vendor_id = 0;
+ } else if (line[0] == 'P' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0)
+ goto fail;
+ vendor_id = strtoul(buf, NULL, 16);
+ if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0)
+ goto fail;
+ product_id = strtoul(buf, NULL, 16);
+ } else if (line[0] == 'S' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0)
+ goto fail;
+ pstrcpy(product_name, sizeof(product_name), buf);
+ } else if (line[0] == 'D' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0)
+ goto fail;
+ class_id = strtoul(buf, NULL, 16);
+ }
+ fail: ;
+ }
+ if (device_count && (vendor_id || product_id)) {
+ /* Add the last device. */
+ ret = func(opaque, bus_num, addr, class_id, vendor_id,
+ product_id, product_name, speed);
+ }
+ the_end:
+ fclose(f);
+ return ret;
+}
+
+typedef struct FindDeviceState {
+ int vendor_id;
+ int product_id;
+ int bus_num;
+ int addr;
+ char product_name[PRODUCT_NAME_SZ];
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
+ int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed)
+{
+ FindDeviceState *s = opaque;
+ if ((vendor_id == s->vendor_id &&
+ product_id == s->product_id) ||
+ (bus_num == s->bus_num &&
+ addr == s->addr)) {
+ pstrcpy(s->product_name, PRODUCT_NAME_SZ, product_name);
+ s->bus_num = bus_num;
+ s->addr = addr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* the syntax is :
+ 'bus.addr' (decimal numbers) or
+ 'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ char *product_name, int product_name_size,
+ const char *devname)
+{
+ const char *p;
+ int ret;
+ FindDeviceState fs;
+
+ p = strchr(devname, '.');
+ if (p) {
+ *pbus_num = strtoul(devname, NULL, 0);
+ *paddr = strtoul(p + 1, NULL, 0);
+ fs.bus_num = *pbus_num;
+ fs.addr = *paddr;
+ ret = usb_host_scan(&fs, usb_host_find_device_scan);
+ if (ret)
+ pstrcpy(product_name, product_name_size, fs.product_name);
+ return 0;
+ }
+ p = strchr(devname, ':');
+ if (p) {
+ fs.vendor_id = strtoul(devname, NULL, 16);
+ fs.product_id = strtoul(p + 1, NULL, 16);
+ ret = usb_host_scan(&fs, usb_host_find_device_scan);
+ if (ret) {
+ *pbus_num = fs.bus_num;
+ *paddr = fs.addr;
+ pstrcpy(product_name, product_name_size, fs.product_name);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+ int class;
+ const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+ { USB_CLASS_AUDIO, "Audio"},
+ { USB_CLASS_COMM, "Communication"},
+ { USB_CLASS_HID, "HID"},
+ { USB_CLASS_HUB, "Hub" },
+ { USB_CLASS_PHYSICAL, "Physical" },
+ { USB_CLASS_PRINTER, "Printer" },
+ { USB_CLASS_MASS_STORAGE, "Storage" },
+ { USB_CLASS_CDC_DATA, "Data" },
+ { USB_CLASS_APP_SPEC, "Application Specific" },
+ { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+ { USB_CLASS_STILL_IMAGE, "Still Image" },
+ { USB_CLASS_CSCID, "Smart Card" },
+ { USB_CLASS_CONTENT_SEC, "Content Security" },
+ { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+ const struct usb_class_info *p;
+ for(p = usb_class_info; p->class != -1; p++) {
+ if (p->class == class)
+ break;
+ }
+ return p->class_name;
+}
+
+void usb_info_device(int bus_num, int addr, int class_id,
+ int vendor_id, int product_id,
+ const char *product_name,
+ int speed)
+{
+ const char *class_str, *speed_str;
+
+ switch(speed) {
+ case USB_SPEED_LOW:
+ speed_str = "1.5";
+ break;
+ case USB_SPEED_FULL:
+ speed_str = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed_str = "480";
+ break;
+ default:
+ speed_str = "?";
+ break;
+ }
+
+ term_printf(" Device %d.%d, speed %s Mb/s\n",
+ bus_num, addr, speed_str);
+ class_str = usb_class_str(class_id);
+ if (class_str)
+ term_printf(" %s:", class_str);
+ else
+ term_printf(" Class %02x:", class_id);
+ term_printf(" USB device %04x:%04x", vendor_id, product_id);
+ if (product_name[0] != '\0')
+ term_printf(", %s", product_name);
+ term_printf("\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+ int class_id,
+ int vendor_id, int product_id,
+ const char *product_name,
+ int speed)
+{
+ usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
+ product_name, speed);
+ return 0;
+}
+
+void usb_host_info(void)
+{
+ usb_host_scan(NULL, usb_host_info_device);
+}
+
+#else
+
+void usb_host_info(void)
+{
+ term_printf("USB host devices not supported\n");
+}
+
+/* XXX: modify configure to compile the right host driver */
+USBDevice *usb_host_device_open(const char *devname)
+{
+ return NULL;
+}
+
+#endif
diff --git a/vgafont.h b/vgafont.h
new file mode 100644
index 0000000..bb75796
--- /dev/null
+++ b/vgafont.h
@@ -0,0 +1,4611 @@
+static uint8_t vgafont16[256 * 16] = {
+
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x1a, /* 00011010 */
+ 0x32, /* 00110010 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe7, /* 11100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^P' */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0xf0, /* 11110000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0e, /* 00001110 */
+ 0x1e, /* 00011110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00101000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x28, /* 00101000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x86, /* 10000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xdc, /* 11011100 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xde, /* 11011110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0x70, /* 01110000 */
+ 0x38, /* 00111000 */
+ 0x1c, /* 00011100 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x36, /* 00110110 */
+ 0x32, /* 00110010 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '€' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 'Â…' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d 'Â' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'ÂŽ' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f 'Â' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 'Â' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x6e, /* 01101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 'Â’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '™' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c 'œ' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xe6, /* 11100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e 'ž' */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xf8, /* 11111000 */
+ 0xc4, /* 11000100 */
+ 0xcc, /* 11001100 */
+ 0xde, /* 11011110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 'Â¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xdc, /* 11011100 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '¬' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xce, /* 11001110 */
+ 0x9a, /* 10011010 */
+ 0x3f, /* 00111111 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '­' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Ã…' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'ÃŽ' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ã' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ã’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ã' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'Ã¥' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 'é' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xf3, /* 11110011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee 'î' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x00, /* 00000000 */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc 'ü' */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
diff --git a/vl.c b/vl.c
new file mode 100644
index 0000000..657116b
--- /dev/null
+++ b/vl.c
@@ -0,0 +1,6224 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#ifndef _WIN32
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <dirent.h>
+#include <netdb.h>
+#ifdef _BSD
+#include <sys/stat.h>
+#ifndef __APPLE__
+#include <libutil.h>
+#endif
+#else
+#ifndef __sun__
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <pty.h>
+#include <malloc.h>
+#include <linux/rtc.h>
+#include <linux/ppdev.h>
+#endif
+#endif
+#endif
+
+#if defined(CONFIG_SLIRP)
+#include "libslirp.h"
+#endif
+
+#ifdef _WIN32
+#include <malloc.h>
+#include <sys/timeb.h>
+#include <windows.h>
+#define getopt_long_only getopt_long
+#define memalign(align, size) malloc(size)
+#endif
+
+#include "qemu_socket.h"
+
+#ifdef CONFIG_SDL
+#ifdef __APPLE__
+#include <SDL/SDL.h>
+#endif
+#endif /* CONFIG_SDL */
+
+#ifdef CONFIG_COCOA
+#undef main
+#define main qemu_main
+#endif /* CONFIG_COCOA */
+
+#include "disas.h"
+
+#include "exec-all.h"
+
+#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+
+//#define DEBUG_UNUSED_IOPORT
+//#define DEBUG_IOPORT
+
+#define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024)
+
+#ifdef TARGET_PPC
+#define DEFAULT_RAM_SIZE 144
+#else
+#define DEFAULT_RAM_SIZE 128
+#endif
+/* in ms */
+#define GUI_REFRESH_INTERVAL 30
+
+/* Max number of USB devices that can be specified on the commandline. */
+#define MAX_USB_CMDLINE 8
+
+/* XXX: use a two level table to limit memory usage */
+#define MAX_IOPORTS 65536
+
+const char *bios_dir = CONFIG_QEMU_SHAREDIR;
+char phys_ram_file[1024];
+void *ioport_opaque[MAX_IOPORTS];
+IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
+IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
+BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD];
+int vga_ram_size;
+int bios_size;
+static DisplayState display_state;
+int nographic;
+const char* keyboard_layout = NULL;
+int64_t ticks_per_sec;
+int boot_device = 'c';
+int ram_size;
+int pit_min_timer_count = 0;
+int nb_nics;
+NICInfo nd_table[MAX_NICS];
+QEMUTimer *gui_timer;
+int vm_running;
+int rtc_utc = 1;
+int cirrus_vga_enabled = 1;
+#ifdef TARGET_SPARC
+int graphic_width = 1024;
+int graphic_height = 768;
+#else
+int graphic_width = 800;
+int graphic_height = 600;
+#endif
+int graphic_depth = 15;
+int full_screen = 0;
+CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+#ifdef TARGET_I386
+int win2k_install_hack = 0;
+#endif
+int usb_enabled = 0;
+static VLANState *first_vlan;
+int smp_cpus = 1;
+int vnc_display = -1;
+#if defined(TARGET_SPARC)
+#define MAX_CPUS 16
+#elif defined(TARGET_I386)
+#define MAX_CPUS 255
+#else
+#define MAX_CPUS 1
+#endif
+int acpi_enabled = 1;
+int fd_bootchk = 1;
+
+/***********************************************************/
+/* x86 ISA bus support */
+
+target_phys_addr_t isa_mem_base = 0;
+PicState2 *isa_pic;
+
+uint32_t default_ioport_readb(void *opaque, uint32_t address)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+ fprintf(stderr, "inb: port=0x%04x\n", address);
+#endif
+ return 0xff;
+}
+
+void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+ fprintf(stderr, "outb: port=0x%04x data=0x%02x\n", address, data);
+#endif
+}
+
+/* default is to make two byte accesses */
+uint32_t default_ioport_readw(void *opaque, uint32_t address)
+{
+ uint32_t data;
+ data = ioport_read_table[0][address](ioport_opaque[address], address);
+ address = (address + 1) & (MAX_IOPORTS - 1);
+ data |= ioport_read_table[0][address](ioport_opaque[address], address) << 8;
+ return data;
+}
+
+void default_ioport_writew(void *opaque, uint32_t address, uint32_t data)
+{
+ ioport_write_table[0][address](ioport_opaque[address], address, data & 0xff);
+ address = (address + 1) & (MAX_IOPORTS - 1);
+ ioport_write_table[0][address](ioport_opaque[address], address, (data >> 8) & 0xff);
+}
+
+uint32_t default_ioport_readl(void *opaque, uint32_t address)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+ fprintf(stderr, "inl: port=0x%04x\n", address);
+#endif
+ return 0xffffffff;
+}
+
+void default_ioport_writel(void *opaque, uint32_t address, uint32_t data)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+ fprintf(stderr, "outl: port=0x%04x data=0x%02x\n", address, data);
+#endif
+}
+
+void init_ioports(void)
+{
+ int i;
+
+ for(i = 0; i < MAX_IOPORTS; i++) {
+ ioport_read_table[0][i] = default_ioport_readb;
+ ioport_write_table[0][i] = default_ioport_writeb;
+ ioport_read_table[1][i] = default_ioport_readw;
+ ioport_write_table[1][i] = default_ioport_writew;
+ ioport_read_table[2][i] = default_ioport_readl;
+ ioport_write_table[2][i] = default_ioport_writel;
+ }
+}
+
+/* size is the word size in byte */
+int register_ioport_read(int start, int length, int size,
+ IOPortReadFunc *func, void *opaque)
+{
+ int i, bsize;
+
+ if (size == 1) {
+ bsize = 0;
+ } else if (size == 2) {
+ bsize = 1;
+ } else if (size == 4) {
+ bsize = 2;
+ } else {
+ hw_error("register_ioport_read: invalid size");
+ return -1;
+ }
+ for(i = start; i < start + length; i += size) {
+ ioport_read_table[bsize][i] = func;
+ if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+ hw_error("register_ioport_read: invalid opaque");
+ ioport_opaque[i] = opaque;
+ }
+ return 0;
+}
+
+/* size is the word size in byte */
+int register_ioport_write(int start, int length, int size,
+ IOPortWriteFunc *func, void *opaque)
+{
+ int i, bsize;
+
+ if (size == 1) {
+ bsize = 0;
+ } else if (size == 2) {
+ bsize = 1;
+ } else if (size == 4) {
+ bsize = 2;
+ } else {
+ hw_error("register_ioport_write: invalid size");
+ return -1;
+ }
+ for(i = start; i < start + length; i += size) {
+ ioport_write_table[bsize][i] = func;
+ if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+ hw_error("register_ioport_read: invalid opaque");
+ ioport_opaque[i] = opaque;
+ }
+ return 0;
+}
+
+void isa_unassign_ioport(int start, int length)
+{
+ int i;
+
+ for(i = start; i < start + length; i++) {
+ ioport_read_table[0][i] = default_ioport_readb;
+ ioport_read_table[1][i] = default_ioport_readw;
+ ioport_read_table[2][i] = default_ioport_readl;
+
+ ioport_write_table[0][i] = default_ioport_writeb;
+ ioport_write_table[1][i] = default_ioport_writew;
+ ioport_write_table[2][i] = default_ioport_writel;
+ }
+}
+
+/***********************************************************/
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+void cpu_outb(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "outb: %04x %02x\n", addr, val);
+#endif
+ ioport_write_table[0][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void cpu_outw(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "outw: %04x %04x\n", addr, val);
+#endif
+ ioport_write_table[1][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void cpu_outl(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "outl: %04x %08x\n", addr, val);
+#endif
+ ioport_write_table[2][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+int cpu_inb(CPUState *env, int addr)
+{
+ int val;
+ val = ioport_read_table[0][addr](ioport_opaque[addr], addr);
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "inb : %04x %02x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+ return val;
+}
+
+int cpu_inw(CPUState *env, int addr)
+{
+ int val;
+ val = ioport_read_table[1][addr](ioport_opaque[addr], addr);
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "inw : %04x %04x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+ return val;
+}
+
+int cpu_inl(CPUState *env, int addr)
+{
+ int val;
+ val = ioport_read_table[2][addr](ioport_opaque[addr], addr);
+#ifdef DEBUG_IOPORT
+ if (loglevel & CPU_LOG_IOPORT)
+ fprintf(logfile, "inl : %04x %08x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+ if (env)
+ env->last_io_time = cpu_get_time_fast();
+#endif
+ return val;
+}
+
+/***********************************************************/
+void hw_error(const char *fmt, ...)
+{
+ va_list ap;
+ CPUState *env;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu: hardware error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ fprintf(stderr, "CPU #%d:\n", env->cpu_index);
+#ifdef TARGET_I386
+ cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU);
+#else
+ cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+ }
+ va_end(ap);
+ abort();
+}
+
+/***********************************************************/
+/* keyboard/mouse */
+
+static QEMUPutKBDEvent *qemu_put_kbd_event;
+static void *qemu_put_kbd_event_opaque;
+static QEMUPutMouseEvent *qemu_put_mouse_event;
+static void *qemu_put_mouse_event_opaque;
+static int qemu_put_mouse_event_absolute;
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+{
+ qemu_put_kbd_event_opaque = opaque;
+ qemu_put_kbd_event = func;
+}
+
+void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute)
+{
+ qemu_put_mouse_event_opaque = opaque;
+ qemu_put_mouse_event = func;
+ qemu_put_mouse_event_absolute = absolute;
+}
+
+void kbd_put_keycode(int keycode)
+{
+ if (qemu_put_kbd_event) {
+ qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode);
+ }
+}
+
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
+{
+ if (qemu_put_mouse_event) {
+ qemu_put_mouse_event(qemu_put_mouse_event_opaque,
+ dx, dy, dz, buttons_state);
+ }
+}
+
+int kbd_mouse_is_absolute(void)
+{
+ return qemu_put_mouse_event_absolute;
+}
+
+/* compute with 96 bit intermediate result: (a*b)/c */
+uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+ union {
+ uint64_t ll;
+ struct {
+#ifdef WORDS_BIGENDIAN
+ uint32_t high, low;
+#else
+ uint32_t low, high;
+#endif
+ } l;
+ } u, res;
+ uint64_t rl, rh;
+
+ u.ll = a;
+ rl = (uint64_t)u.l.low * (uint64_t)b;
+ rh = (uint64_t)u.l.high * (uint64_t)b;
+ rh += (rl >> 32);
+ res.l.high = rh / c;
+ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+ return res.ll;
+}
+
+/***********************************************************/
+/* real time host monotonic timer */
+
+#define QEMU_TIMER_BASE 1000000000LL
+
+#ifdef WIN32
+
+static int64_t clock_freq;
+
+static void init_get_clock(void)
+{
+ LARGE_INTEGER freq;
+ int ret;
+ ret = QueryPerformanceFrequency(&freq);
+ if (ret == 0) {
+ fprintf(stderr, "Could not calibrate ticks\n");
+ exit(1);
+ }
+ clock_freq = freq.QuadPart;
+}
+
+static int64_t get_clock(void)
+{
+ LARGE_INTEGER ti;
+ QueryPerformanceCounter(&ti);
+ return muldiv64(ti.QuadPart, QEMU_TIMER_BASE, clock_freq);
+}
+
+#else
+
+static int use_rt_clock;
+
+static void init_get_clock(void)
+{
+ use_rt_clock = 0;
+#if defined(__linux__)
+ {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ use_rt_clock = 1;
+ }
+ }
+#endif
+}
+
+static int64_t get_clock(void)
+{
+#if defined(__linux__)
+ if (use_rt_clock) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+ } else
+#endif
+ {
+ /* XXX: using gettimeofday leads to problems if the date
+ changes, so it should be avoided. */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
+ }
+}
+
+#endif
+
+/***********************************************************/
+/* guest cycle counter */
+
+static int64_t cpu_ticks_prev;
+static int64_t cpu_ticks_offset;
+static int64_t cpu_clock_offset;
+static int cpu_ticks_enabled;
+
+/* return the host CPU cycle counter and handle stop/restart */
+int64_t cpu_get_ticks(void)
+{
+ if (!cpu_ticks_enabled) {
+ return cpu_ticks_offset;
+ } else {
+ int64_t ticks;
+ ticks = cpu_get_real_ticks();
+ if (cpu_ticks_prev > ticks) {
+ /* Note: non increasing ticks may happen if the host uses
+ software suspend */
+ cpu_ticks_offset += cpu_ticks_prev - ticks;
+ }
+ cpu_ticks_prev = ticks;
+ return ticks + cpu_ticks_offset;
+ }
+}
+
+/* return the host CPU monotonic timer and handle stop/restart */
+static int64_t cpu_get_clock(void)
+{
+ int64_t ti;
+ if (!cpu_ticks_enabled) {
+ return cpu_clock_offset;
+ } else {
+ ti = get_clock();
+ return ti + cpu_clock_offset;
+ }
+}
+
+/* enable cpu_get_ticks() */
+void cpu_enable_ticks(void)
+{
+ if (!cpu_ticks_enabled) {
+ cpu_ticks_offset -= cpu_get_real_ticks();
+ cpu_clock_offset -= get_clock();
+ cpu_ticks_enabled = 1;
+ }
+}
+
+/* disable cpu_get_ticks() : the clock is stopped. You must not call
+ cpu_get_ticks() after that. */
+void cpu_disable_ticks(void)
+{
+ if (cpu_ticks_enabled) {
+ cpu_ticks_offset = cpu_get_ticks();
+ cpu_clock_offset = cpu_get_clock();
+ cpu_ticks_enabled = 0;
+ }
+}
+
+/***********************************************************/
+/* timers */
+
+#define QEMU_TIMER_REALTIME 0
+#define QEMU_TIMER_VIRTUAL 1
+
+struct QEMUClock {
+ int type;
+ /* XXX: add frequency */
+};
+
+struct QEMUTimer {
+ QEMUClock *clock;
+ int64_t expire_time;
+ QEMUTimerCB *cb;
+ void *opaque;
+ struct QEMUTimer *next;
+};
+
+QEMUClock *rt_clock;
+QEMUClock *vm_clock;
+
+static QEMUTimer *active_timers[2];
+#ifdef _WIN32
+static MMRESULT timerID;
+static HANDLE host_alarm = NULL;
+static unsigned int period = 1;
+#else
+/* frequency of the times() clock tick */
+static int timer_freq;
+#endif
+
+QEMUClock *qemu_new_clock(int type)
+{
+ QEMUClock *clock;
+ clock = qemu_mallocz(sizeof(QEMUClock));
+ if (!clock)
+ return NULL;
+ clock->type = type;
+ return clock;
+}
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque)
+{
+ QEMUTimer *ts;
+
+ ts = qemu_mallocz(sizeof(QEMUTimer));
+ ts->clock = clock;
+ ts->cb = cb;
+ ts->opaque = opaque;
+ return ts;
+}
+
+void qemu_free_timer(QEMUTimer *ts)
+{
+ qemu_free(ts);
+}
+
+/* stop a timer, but do not dealloc it */
+void qemu_del_timer(QEMUTimer *ts)
+{
+ QEMUTimer **pt, *t;
+
+ /* NOTE: this code must be signal safe because
+ qemu_timer_expired() can be called from a signal. */
+ pt = &active_timers[ts->clock->type];
+ for(;;) {
+ t = *pt;
+ if (!t)
+ break;
+ if (t == ts) {
+ *pt = t->next;
+ break;
+ }
+ pt = &t->next;
+ }
+}
+
+/* modify the current timer so that it will be fired when current_time
+ >= expire_time. The corresponding callback will be called. */
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
+{
+ QEMUTimer **pt, *t;
+
+ qemu_del_timer(ts);
+
+ /* add the timer in the sorted list */
+ /* NOTE: this code must be signal safe because
+ qemu_timer_expired() can be called from a signal. */
+ pt = &active_timers[ts->clock->type];
+ for(;;) {
+ t = *pt;
+ if (!t)
+ break;
+ if (t->expire_time > expire_time)
+ break;
+ pt = &t->next;
+ }
+ ts->expire_time = expire_time;
+ ts->next = *pt;
+ *pt = ts;
+}
+
+int qemu_timer_pending(QEMUTimer *ts)
+{
+ QEMUTimer *t;
+ for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) {
+ if (t == ts)
+ return 1;
+ }
+ return 0;
+}
+
+static inline int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time)
+{
+ if (!timer_head)
+ return 0;
+ return (timer_head->expire_time <= current_time);
+}
+
+static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time)
+{
+ QEMUTimer *ts;
+
+ for(;;) {
+ ts = *ptimer_head;
+ if (!ts || ts->expire_time > current_time)
+ break;
+ /* remove timer from the list before calling the callback */
+ *ptimer_head = ts->next;
+ ts->next = NULL;
+
+ /* run the callback (the timer list can be modified) */
+ ts->cb(ts->opaque);
+ }
+}
+
+int64_t qemu_get_clock(QEMUClock *clock)
+{
+ switch(clock->type) {
+ case QEMU_TIMER_REALTIME:
+ return get_clock() / 1000000;
+ default:
+ case QEMU_TIMER_VIRTUAL:
+ return cpu_get_clock();
+ }
+}
+
+static void init_timers(void)
+{
+ init_get_clock();
+ ticks_per_sec = QEMU_TIMER_BASE;
+ rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
+ vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
+}
+
+/* save a timer */
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ if (qemu_timer_pending(ts)) {
+ expire_time = ts->expire_time;
+ } else {
+ expire_time = -1;
+ }
+ qemu_put_be64(f, expire_time);
+}
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ expire_time = qemu_get_be64(f);
+ if (expire_time != -1) {
+ qemu_mod_timer(ts, expire_time);
+ } else {
+ qemu_del_timer(ts);
+ }
+}
+
+static void timer_save(QEMUFile *f, void *opaque)
+{
+ if (cpu_ticks_enabled) {
+ hw_error("cannot save state if virtual timers are running");
+ }
+ qemu_put_be64s(f, &cpu_ticks_offset);
+ qemu_put_be64s(f, &ticks_per_sec);
+}
+
+static int timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ if (version_id != 1)
+ return -EINVAL;
+ if (cpu_ticks_enabled) {
+ return -EINVAL;
+ }
+ qemu_get_be64s(f, &cpu_ticks_offset);
+ qemu_get_be64s(f, &ticks_per_sec);
+ return 0;
+}
+
+#ifdef _WIN32
+void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg,
+ DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
+#else
+static void host_alarm_handler(int host_signum)
+#endif
+{
+#if 0
+#define DISP_FREQ 1000
+ {
+ static int64_t delta_min = INT64_MAX;
+ static int64_t delta_max, delta_cum, last_clock, delta, ti;
+ static int count;
+ ti = qemu_get_clock(vm_clock);
+ if (last_clock != 0) {
+ delta = ti - last_clock;
+ if (delta < delta_min)
+ delta_min = delta;
+ if (delta > delta_max)
+ delta_max = delta;
+ delta_cum += delta;
+ if (++count == DISP_FREQ) {
+ printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n",
+ muldiv64(delta_min, 1000000, ticks_per_sec),
+ muldiv64(delta_max, 1000000, ticks_per_sec),
+ muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec),
+ (double)ticks_per_sec / ((double)delta_cum / DISP_FREQ));
+ count = 0;
+ delta_min = INT64_MAX;
+ delta_max = 0;
+ delta_cum = 0;
+ }
+ }
+ last_clock = ti;
+ }
+#endif
+ if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+ qemu_get_clock(vm_clock)) ||
+ qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
+ qemu_get_clock(rt_clock))) {
+#ifdef _WIN32
+ SetEvent(host_alarm);
+#endif
+ CPUState *env = cpu_single_env;
+ if (env) {
+ /* stop the currently executing cpu because a timer occured */
+ cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled) {
+ kqemu_cpu_interrupt(env);
+ }
+#endif
+ }
+ }
+}
+
+#ifndef _WIN32
+
+#if defined(__linux__)
+
+#define RTC_FREQ 1024
+
+static int rtc_fd;
+
+static int start_rtc_timer(void)
+{
+ rtc_fd = open("/dev/rtc", O_RDONLY);
+ if (rtc_fd < 0)
+ return -1;
+ if (ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) {
+ fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n"
+ "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n"
+ "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n");
+ goto fail;
+ }
+ if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) {
+ fail:
+ close(rtc_fd);
+ return -1;
+ }
+ pit_min_timer_count = PIT_FREQ / RTC_FREQ;
+ return 0;
+}
+
+#else
+
+static int start_rtc_timer(void)
+{
+ return -1;
+}
+
+#endif /* !defined(__linux__) */
+
+#endif /* !defined(_WIN32) */
+
+static void init_timer_alarm(void)
+{
+#ifdef _WIN32
+ {
+ int count=0;
+ TIMECAPS tc;
+
+ ZeroMemory(&tc, sizeof(TIMECAPS));
+ timeGetDevCaps(&tc, sizeof(TIMECAPS));
+ if (period < tc.wPeriodMin)
+ period = tc.wPeriodMin;
+ timeBeginPeriod(period);
+ timerID = timeSetEvent(1, // interval (ms)
+ period, // resolution
+ host_alarm_handler, // function
+ (DWORD)&count, // user parameter
+ TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
+ if( !timerID ) {
+ perror("failed timer alarm");
+ exit(1);
+ }
+ host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!host_alarm) {
+ perror("failed CreateEvent");
+ exit(1);
+ }
+ qemu_add_wait_object(host_alarm, NULL, NULL);
+ }
+ pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000;
+#else
+ {
+ struct sigaction act;
+ struct itimerval itv;
+
+ /* get times() syscall frequency */
+ timer_freq = sysconf(_SC_CLK_TCK);
+
+ /* timer signal */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+#if defined (TARGET_I386) && defined(USE_CODE_COPY)
+ act.sa_flags |= SA_ONSTACK;
+#endif
+ act.sa_handler = host_alarm_handler;
+ sigaction(SIGALRM, &act, NULL);
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 10 * 1000;
+ setitimer(ITIMER_REAL, &itv, NULL);
+ /* we probe the tick duration of the kernel to inform the user if
+ the emulated kernel requested a too high timer frequency */
+ getitimer(ITIMER_REAL, &itv);
+
+#if defined(__linux__)
+ /* XXX: force /dev/rtc usage because even 2.6 kernels may not
+ have timers with 1 ms resolution. The correct solution will
+ be to use the POSIX real time timers available in recent
+ 2.6 kernels */
+ if (itv.it_interval.tv_usec > 1000 || 1) {
+ /* try to use /dev/rtc to have a faster timer */
+ if (start_rtc_timer() < 0)
+ goto use_itimer;
+ /* disable itimer */
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itv, NULL);
+
+ /* use the RTC */
+ sigaction(SIGIO, &act, NULL);
+ fcntl(rtc_fd, F_SETFL, O_ASYNC);
+ fcntl(rtc_fd, F_SETOWN, getpid());
+ } else
+#endif /* defined(__linux__) */
+ {
+ use_itimer:
+ pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec *
+ PIT_FREQ) / 1000000;
+ }
+ }
+#endif
+}
+
+void quit_timers(void)
+{
+#ifdef _WIN32
+ timeKillEvent(timerID);
+ timeEndPeriod(period);
+ if (host_alarm) {
+ CloseHandle(host_alarm);
+ host_alarm = NULL;
+ }
+#endif
+}
+
+/***********************************************************/
+/* character device */
+
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
+{
+ return s->chr_write(s, buf, len);
+}
+
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
+{
+ if (!s->chr_ioctl)
+ return -ENOTSUP;
+ return s->chr_ioctl(s, cmd, arg);
+}
+
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ qemu_chr_write(s, buf, strlen(buf));
+ va_end(ap);
+}
+
+void qemu_chr_send_event(CharDriverState *s, int event)
+{
+ if (s->chr_send_event)
+ s->chr_send_event(s, event);
+}
+
+void qemu_chr_add_read_handler(CharDriverState *s,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ s->chr_add_read_handler(s, fd_can_read, fd_read, opaque);
+}
+
+void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event)
+{
+ s->chr_event = chr_event;
+}
+
+static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ return len;
+}
+
+static void null_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+}
+
+CharDriverState *qemu_chr_open_null(void)
+{
+ CharDriverState *chr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ chr->chr_write = null_chr_write;
+ chr->chr_add_read_handler = null_chr_add_read_handler;
+ return chr;
+}
+
+#ifdef _WIN32
+
+static void socket_cleanup(void)
+{
+ WSACleanup();
+}
+
+static int socket_init(void)
+{
+ WSADATA Data;
+ int ret, err;
+
+ ret = WSAStartup(MAKEWORD(2,2), &Data);
+ if (ret != 0) {
+ err = WSAGetLastError();
+ fprintf(stderr, "WSAStartup: %d\n", err);
+ return -1;
+ }
+ atexit(socket_cleanup);
+ return 0;
+}
+
+static int send_all(int fd, const uint8_t *buf, int len1)
+{
+ int ret, len;
+
+ len = len1;
+ while (len > 0) {
+ ret = send(fd, buf, len, 0);
+ if (ret < 0) {
+ int errno;
+ errno = WSAGetLastError();
+ if (errno != WSAEWOULDBLOCK) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
+void socket_set_nonblock(int fd)
+{
+ unsigned long opt = 1;
+ ioctlsocket(fd, FIONBIO, &opt);
+}
+
+#else
+
+static int unix_write(int fd, const uint8_t *buf, int len1)
+{
+ int ret, len;
+
+ len = len1;
+ while (len > 0) {
+ ret = write(fd, buf, len);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
+static inline int send_all(int fd, const uint8_t *buf, int len1)
+{
+ return unix_write(fd, buf, len1);
+}
+
+void socket_set_nonblock(int fd)
+{
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+}
+#endif /* !_WIN32 */
+
+#ifndef _WIN32
+
+typedef struct {
+ int fd_in, fd_out;
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ int max_size;
+} FDCharDriver;
+
+#define STDIO_MAX_CLIENTS 2
+
+static int stdio_nb_clients;
+static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
+
+static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ FDCharDriver *s = chr->opaque;
+ return unix_write(s->fd_out, buf, len);
+}
+
+static int fd_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ return s->max_size;
+}
+
+static void fd_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[1024];
+
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ if (len == 0)
+ return;
+ size = read(s->fd_in, buf, len);
+ if (size > 0) {
+ s->fd_read(s->fd_opaque, buf, size);
+ }
+}
+
+static void fd_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ FDCharDriver *s = chr->opaque;
+
+ if (s->fd_in >= 0) {
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+ if (nographic && s->fd_in == 0) {
+ } else {
+ qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll,
+ fd_chr_read, NULL, chr);
+ }
+ }
+}
+
+/* open a character device to a unix fd */
+CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
+{
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(FDCharDriver));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ s->fd_in = fd_in;
+ s->fd_out = fd_out;
+ chr->opaque = s;
+ chr->chr_write = fd_chr_write;
+ chr->chr_add_read_handler = fd_chr_add_read_handler;
+ return chr;
+}
+
+CharDriverState *qemu_chr_open_file_out(const char *file_out)
+{
+ int fd_out;
+
+ fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666);
+ if (fd_out < 0)
+ return NULL;
+ return qemu_chr_open_fd(-1, fd_out);
+}
+
+CharDriverState *qemu_chr_open_pipe(const char *filename)
+{
+ int fd;
+
+ fd = open(filename, O_RDWR | O_BINARY);
+ if (fd < 0)
+ return NULL;
+ return qemu_chr_open_fd(fd, fd);
+}
+
+
+/* for STDIO, we handle the case where several clients use it
+ (nographic mode) */
+
+#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
+
+#define TERM_FIFO_MAX_SIZE 1
+
+static int term_got_escape, client_index;
+static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
+static int term_fifo_size;
+static int term_timestamps;
+static int64_t term_timestamps_start;
+
+void term_print_help(void)
+{
+ printf("\n"
+ "C-a h print this help\n"
+ "C-a x exit emulator\n"
+ "C-a s save disk data back to file (if -snapshot)\n"
+ "C-a b send break (magic sysrq)\n"
+ "C-a t toggle console timestamps\n"
+ "C-a c switch between console and monitor\n"
+ "C-a C-a send C-a\n"
+ );
+}
+
+/* called when a char is received */
+static void stdio_received_byte(int ch)
+{
+ if (term_got_escape) {
+ term_got_escape = 0;
+ switch(ch) {
+ case 'h':
+ term_print_help();
+ break;
+ case 'x':
+ exit(0);
+ break;
+ case 's':
+ {
+ int i;
+ for (i = 0; i < MAX_DISKS; i++) {
+ if (bs_table[i])
+ bdrv_commit(bs_table[i]);
+ }
+ }
+ break;
+ case 'b':
+ if (client_index < stdio_nb_clients) {
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ chr = stdio_clients[client_index];
+ s = chr->opaque;
+ chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK);
+ }
+ break;
+ case 'c':
+ client_index++;
+ if (client_index >= stdio_nb_clients)
+ client_index = 0;
+ if (client_index == 0) {
+ /* send a new line in the monitor to get the prompt */
+ ch = '\r';
+ goto send_char;
+ }
+ break;
+ case 't':
+ term_timestamps = !term_timestamps;
+ term_timestamps_start = -1;
+ break;
+ case TERM_ESCAPE:
+ goto send_char;
+ }
+ } else if (ch == TERM_ESCAPE) {
+ term_got_escape = 1;
+ } else {
+ send_char:
+ if (client_index < stdio_nb_clients) {
+ uint8_t buf[1];
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ chr = stdio_clients[client_index];
+ s = chr->opaque;
+ if (s->fd_can_read(s->fd_opaque) > 0) {
+ buf[0] = ch;
+ s->fd_read(s->fd_opaque, buf, 1);
+ } else if (term_fifo_size == 0) {
+ term_fifo[term_fifo_size++] = ch;
+ }
+ }
+ }
+}
+
+static int stdio_read_poll(void *opaque)
+{
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ if (client_index < stdio_nb_clients) {
+ chr = stdio_clients[client_index];
+ s = chr->opaque;
+ /* try to flush the queue if needed */
+ if (term_fifo_size != 0 && s->fd_can_read(s->fd_opaque) > 0) {
+ s->fd_read(s->fd_opaque, term_fifo, 1);
+ term_fifo_size = 0;
+ }
+ /* see if we can absorb more chars */
+ if (term_fifo_size == 0)
+ return 1;
+ else
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static void stdio_read(void *opaque)
+{
+ int size;
+ uint8_t buf[1];
+
+ size = read(0, buf, 1);
+ if (size > 0)
+ stdio_received_byte(buf[0]);
+}
+
+static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ FDCharDriver *s = chr->opaque;
+ if (!term_timestamps) {
+ return unix_write(s->fd_out, buf, len);
+ } else {
+ int i;
+ char buf1[64];
+
+ for(i = 0; i < len; i++) {
+ unix_write(s->fd_out, buf + i, 1);
+ if (buf[i] == '\n') {
+ int64_t ti;
+ int secs;
+
+ ti = get_clock();
+ if (term_timestamps_start == -1)
+ term_timestamps_start = ti;
+ ti -= term_timestamps_start;
+ secs = ti / 1000000000;
+ snprintf(buf1, sizeof(buf1),
+ "[%02d:%02d:%02d.%03d] ",
+ secs / 3600,
+ (secs / 60) % 60,
+ secs % 60,
+ (int)((ti / 1000000) % 1000));
+ unix_write(s->fd_out, buf1, strlen(buf1));
+ }
+ }
+ return len;
+ }
+}
+
+/* init terminal so that we can grab keys */
+static struct termios oldtty;
+static int old_fd0_flags;
+
+static void term_exit(void)
+{
+ tcsetattr (0, TCSANOW, &oldtty);
+ fcntl(0, F_SETFL, old_fd0_flags);
+}
+
+static void term_init(void)
+{
+ struct termios tty;
+
+ tcgetattr (0, &tty);
+ oldtty = tty;
+ old_fd0_flags = fcntl(0, F_GETFL);
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ /* if graphical mode, we allow Ctrl-C handling */
+ if (nographic)
+ tty.c_lflag &= ~ISIG;
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+
+ tcsetattr (0, TCSANOW, &tty);
+
+ atexit(term_exit);
+
+ fcntl(0, F_SETFL, O_NONBLOCK);
+}
+
+CharDriverState *qemu_chr_open_stdio(void)
+{
+ CharDriverState *chr;
+
+ if (nographic) {
+ if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
+ return NULL;
+ chr = qemu_chr_open_fd(0, 1);
+ chr->chr_write = stdio_write;
+ if (stdio_nb_clients == 0)
+ qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL);
+ client_index = stdio_nb_clients;
+ } else {
+ if (stdio_nb_clients != 0)
+ return NULL;
+ chr = qemu_chr_open_fd(0, 1);
+ }
+ stdio_clients[stdio_nb_clients++] = chr;
+ if (stdio_nb_clients == 1) {
+ /* set the terminal in raw mode */
+ term_init();
+ }
+ return chr;
+}
+
+#if defined(__linux__)
+CharDriverState *qemu_chr_open_pty(void)
+{
+ struct termios tty;
+ char slave_name[1024];
+ int master_fd, slave_fd;
+
+ /* Not satisfying */
+ if (openpty(&master_fd, &slave_fd, slave_name, NULL, NULL) < 0) {
+ return NULL;
+ }
+
+ /* Disabling local echo and line-buffered output */
+ tcgetattr (master_fd, &tty);
+ tty.c_lflag &= ~(ECHO|ICANON|ISIG);
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+ tcsetattr (master_fd, TCSAFLUSH, &tty);
+
+ fprintf(stderr, "char device redirected to %s\n", slave_name);
+ return qemu_chr_open_fd(master_fd, master_fd);
+}
+
+static void tty_serial_init(int fd, int speed,
+ int parity, int data_bits, int stop_bits)
+{
+ struct termios tty;
+ speed_t spd;
+
+#if 0
+ printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+#endif
+ tcgetattr (fd, &tty);
+
+ switch(speed) {
+ case 50:
+ spd = B50;
+ break;
+ case 75:
+ spd = B75;
+ break;
+ case 300:
+ spd = B300;
+ break;
+ case 600:
+ spd = B600;
+ break;
+ case 1200:
+ spd = B1200;
+ break;
+ case 2400:
+ spd = B2400;
+ break;
+ case 4800:
+ spd = B4800;
+ break;
+ case 9600:
+ spd = B9600;
+ break;
+ case 19200:
+ spd = B19200;
+ break;
+ case 38400:
+ spd = B38400;
+ break;
+ case 57600:
+ spd = B57600;
+ break;
+ default:
+ case 115200:
+ spd = B115200;
+ break;
+ }
+
+ cfsetispeed(&tty, spd);
+ cfsetospeed(&tty, spd);
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
+ tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS);
+ switch(data_bits) {
+ default:
+ case 8:
+ tty.c_cflag |= CS8;
+ break;
+ case 7:
+ tty.c_cflag |= CS7;
+ break;
+ case 6:
+ tty.c_cflag |= CS6;
+ break;
+ case 5:
+ tty.c_cflag |= CS5;
+ break;
+ }
+ switch(parity) {
+ default:
+ case 'N':
+ break;
+ case 'E':
+ tty.c_cflag |= PARENB;
+ break;
+ case 'O':
+ tty.c_cflag |= PARENB | PARODD;
+ break;
+ }
+
+ tcsetattr (fd, TCSANOW, &tty);
+}
+
+static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+ FDCharDriver *s = chr->opaque;
+
+ switch(cmd) {
+ case CHR_IOCTL_SERIAL_SET_PARAMS:
+ {
+ QEMUSerialSetParams *ssp = arg;
+ tty_serial_init(s->fd_in, ssp->speed, ssp->parity,
+ ssp->data_bits, ssp->stop_bits);
+ }
+ break;
+ case CHR_IOCTL_SERIAL_SET_BREAK:
+ {
+ int enable = *(int *)arg;
+ if (enable)
+ tcsendbreak(s->fd_in, 1);
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+CharDriverState *qemu_chr_open_tty(const char *filename)
+{
+ CharDriverState *chr;
+ int fd;
+
+ fd = open(filename, O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ return NULL;
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ tty_serial_init(fd, 115200, 'N', 8, 1);
+ chr = qemu_chr_open_fd(fd, fd);
+ if (!chr)
+ return NULL;
+ chr->chr_ioctl = tty_serial_ioctl;
+ return chr;
+}
+
+static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+ int fd = (int)chr->opaque;
+ uint8_t b;
+
+ switch(cmd) {
+ case CHR_IOCTL_PP_READ_DATA:
+ if (ioctl(fd, PPRDATA, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_DATA:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWDATA, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_CONTROL:
+ if (ioctl(fd, PPRCONTROL, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_CONTROL:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWCONTROL, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_STATUS:
+ if (ioctl(fd, PPRSTATUS, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+CharDriverState *qemu_chr_open_pp(const char *filename)
+{
+ CharDriverState *chr;
+ int fd;
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ return NULL;
+
+ if (ioctl(fd, PPCLAIM) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr) {
+ close(fd);
+ return NULL;
+ }
+ chr->opaque = (void *)fd;
+ chr->chr_write = null_chr_write;
+ chr->chr_add_read_handler = null_chr_add_read_handler;
+ chr->chr_ioctl = pp_ioctl;
+ return chr;
+}
+
+#else
+CharDriverState *qemu_chr_open_pty(void)
+{
+ return NULL;
+}
+#endif
+
+#endif /* !defined(_WIN32) */
+
+#ifdef _WIN32
+typedef struct {
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *win_opaque;
+ int max_size;
+ HANDLE hcom, hrecv, hsend;
+ OVERLAPPED orecv, osend;
+ BOOL fpipe;
+ DWORD len;
+} WinCharState;
+
+#define NSENDBUF 2048
+#define NRECVBUF 2048
+#define MAXCONNECT 1
+#define NTIMEOUT 5000
+
+static int win_chr_poll(void *opaque);
+static int win_chr_pipe_poll(void *opaque);
+
+static void win_chr_close2(WinCharState *s)
+{
+ if (s->hsend) {
+ CloseHandle(s->hsend);
+ s->hsend = NULL;
+ }
+ if (s->hrecv) {
+ CloseHandle(s->hrecv);
+ s->hrecv = NULL;
+ }
+ if (s->hcom) {
+ CloseHandle(s->hcom);
+ s->hcom = NULL;
+ }
+ if (s->fpipe)
+ qemu_del_polling_cb(win_chr_pipe_poll, s);
+ else
+ qemu_del_polling_cb(win_chr_poll, s);
+}
+
+static void win_chr_close(CharDriverState *chr)
+{
+ WinCharState *s = chr->opaque;
+ win_chr_close2(s);
+}
+
+static int win_chr_init(WinCharState *s, const char *filename)
+{
+ COMMCONFIG comcfg;
+ COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+ COMSTAT comstat;
+ DWORD size;
+ DWORD err;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+ fprintf(stderr, "Failed SetupComm\n");
+ goto fail;
+ }
+
+ ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+ size = sizeof(COMMCONFIG);
+ GetDefaultCommConfig(filename, &comcfg, &size);
+ comcfg.dcb.DCBlength = sizeof(DCB);
+ CommConfigDialog(filename, NULL, &comcfg);
+
+ if (!SetCommState(s->hcom, &comcfg.dcb)) {
+ fprintf(stderr, "Failed SetCommState\n");
+ goto fail;
+ }
+
+ if (!SetCommMask(s->hcom, EV_ERR)) {
+ fprintf(stderr, "Failed SetCommMask\n");
+ goto fail;
+ }
+
+ cto.ReadIntervalTimeout = MAXDWORD;
+ if (!SetCommTimeouts(s->hcom, &cto)) {
+ fprintf(stderr, "Failed SetCommTimeouts\n");
+ goto fail;
+ }
+
+ if (!ClearCommError(s->hcom, &err, &comstat)) {
+ fprintf(stderr, "Failed ClearCommError\n");
+ goto fail;
+ }
+ qemu_add_polling_cb(win_chr_poll, s);
+ return 0;
+
+ fail:
+ win_chr_close2(s);
+ return -1;
+}
+
+static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+{
+ WinCharState *s = chr->opaque;
+ DWORD len, ret, size, err;
+
+ len = len1;
+ ZeroMemory(&s->osend, sizeof(s->osend));
+ s->osend.hEvent = s->hsend;
+ while (len > 0) {
+ if (s->hsend)
+ ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+ else
+ ret = WriteFile(s->hcom, buf, len, &size, NULL);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+ if (ret) {
+ buf += size;
+ len -= size;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else {
+ buf += size;
+ len -= size;
+ }
+ }
+ return len1 - len;
+}
+
+static int win_chr_read_poll(WinCharState *s)
+{
+ s->max_size = s->fd_can_read(s->win_opaque);
+ return s->max_size;
+}
+
+static void win_chr_readfile(WinCharState *s)
+{
+ int ret, err;
+ uint8_t buf[1024];
+ DWORD size;
+
+ ZeroMemory(&s->orecv, sizeof(s->orecv));
+ s->orecv.hEvent = s->hrecv;
+ ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
+ }
+ }
+
+ if (size > 0) {
+ s->fd_read(s->win_opaque, buf, size);
+ }
+}
+
+static void win_chr_read(WinCharState *s)
+{
+ if (s->len > s->max_size)
+ s->len = s->max_size;
+ if (s->len == 0)
+ return;
+
+ win_chr_readfile(s);
+}
+
+static int win_chr_poll(void *opaque)
+{
+ WinCharState *s = opaque;
+ COMSTAT status;
+ DWORD comerr;
+
+ ClearCommError(s->hcom, &comerr, &status);
+ if (status.cbInQue > 0) {
+ s->len = status.cbInQue;
+ win_chr_read_poll(s);
+ win_chr_read(s);
+ return 1;
+ }
+ return 0;
+}
+
+static void win_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ WinCharState *s = chr->opaque;
+
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->win_opaque = opaque;
+}
+
+CharDriverState *qemu_chr_open_win(const char *filename)
+{
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_add_read_handler = win_chr_add_read_handler;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_init(s, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ return chr;
+}
+
+static int win_chr_pipe_poll(void *opaque)
+{
+ WinCharState *s = opaque;
+ DWORD size;
+
+ PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+ if (size > 0) {
+ s->len = size;
+ win_chr_read_poll(s);
+ win_chr_read(s);
+ return 1;
+ }
+ return 0;
+}
+
+static int win_chr_pipe_init(WinCharState *s, const char *filename)
+{
+ OVERLAPPED ov;
+ int ret;
+ DWORD size;
+ char openname[256];
+
+ s->fpipe = TRUE;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+ s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ret = ConnectNamedPipe(s->hcom, &ov);
+ if (ret) {
+ fprintf(stderr, "Failed ConnectNamedPipe\n");
+ goto fail;
+ }
+
+ ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+ if (!ret) {
+ fprintf(stderr, "Failed GetOverlappedResult\n");
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ goto fail;
+ }
+
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ qemu_add_polling_cb(win_chr_pipe_poll, s);
+ return 0;
+
+ fail:
+ win_chr_close2(s);
+ return -1;
+}
+
+
+CharDriverState *qemu_chr_open_win_pipe(const char *filename)
+{
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_add_read_handler = win_chr_add_read_handler;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_pipe_init(s, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ return chr;
+}
+
+CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+{
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(WinCharState));
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+ s->hcom = fd_out;
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_add_read_handler = win_chr_add_read_handler;
+ return chr;
+}
+
+CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
+{
+ HANDLE fd_out;
+
+ fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd_out == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ return qemu_chr_open_win_file(fd_out);
+}
+#endif
+
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ int fd;
+ struct sockaddr_in daddr;
+ char buf[1024];
+ int bufcnt;
+ int bufptr;
+ int max_size;
+} NetCharDriver;
+
+static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ NetCharDriver *s = chr->opaque;
+
+ return sendto(s->fd, buf, len, 0,
+ (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ s->max_size = s->fd_can_read(s->fd_opaque);
+
+ /* If there were any stray characters in the queue process them
+ * first
+ */
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ }
+ return s->max_size;
+}
+
+static void udp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ if (s->max_size == 0)
+ return;
+ s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
+ s->bufptr = s->bufcnt;
+ if (s->bufcnt <= 0)
+ return;
+
+ s->bufptr = 0;
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ }
+}
+
+static void udp_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ NetCharDriver *s = chr->opaque;
+
+ if (s->fd >= 0) {
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+ qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+ udp_chr_read, NULL, chr);
+ }
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+int parse_host_src_port(struct sockaddr_in *haddr,
+ struct sockaddr_in *saddr,
+ const char *str);
+
+CharDriverState *qemu_chr_open_udp(const char *def)
+{
+ CharDriverState *chr = NULL;
+ NetCharDriver *s = NULL;
+ int fd = -1;
+ struct sockaddr_in saddr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto return_err;
+ s = qemu_mallocz(sizeof(NetCharDriver));
+ if (!s)
+ goto return_err;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ goto return_err;
+ }
+
+ if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
+ printf("Could not parse: %s\n", def);
+ goto return_err;
+ }
+
+ if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+ {
+ perror("bind");
+ goto return_err;
+ }
+
+ s->fd = fd;
+ s->bufcnt = 0;
+ s->bufptr = 0;
+ chr->opaque = s;
+ chr->chr_write = udp_chr_write;
+ chr->chr_add_read_handler = udp_chr_add_read_handler;
+ return chr;
+
+return_err:
+ if (chr)
+ free(chr);
+ if (s)
+ free(s);
+ if (fd >= 0)
+ closesocket(fd);
+ return NULL;
+}
+
+/***********************************************************/
+/* TCP Net console */
+
+typedef struct {
+ IOCanRWHandler *fd_can_read;
+ IOReadHandler *fd_read;
+ void *fd_opaque;
+ int fd, listen_fd;
+ int connected;
+ int max_size;
+ int do_telnetopt;
+} TCPCharDriver;
+
+static void tcp_chr_accept(void *opaque);
+
+static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->connected) {
+ return send_all(s->fd, buf, len);
+ } else {
+ /* XXX: indicate an error ? */
+ return len;
+ }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ if (!s->connected)
+ return 0;
+ s->max_size = s->fd_can_read(s->fd_opaque);
+ return s->max_size;
+}
+
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+ TCPCharDriver *s,
+ char *buf, int *size)
+{
+ /* Handle any telnet client's basic IAC options to satisfy char by
+ * char mode with no echo. All IAC options will be removed from
+ * the buf and the do_telnetopt variable will be used to track the
+ * state of the width of the IAC information.
+ *
+ * IAC commands come in sets of 3 bytes with the exception of the
+ * "IAC BREAK" command and the double IAC.
+ */
+
+ int i;
+ int j = 0;
+
+ for (i = 0; i < *size; i++) {
+ if (s->do_telnetopt > 1) {
+ if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+ /* Double IAC means send an IAC */
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ s->do_telnetopt = 1;
+ } else {
+ if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
+ /* Handle IAC break commands by sending a serial break */
+ chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK);
+ s->do_telnetopt++;
+ }
+ s->do_telnetopt++;
+ }
+ if (s->do_telnetopt >= 4) {
+ s->do_telnetopt = 1;
+ }
+ } else {
+ if ((unsigned char)buf[i] == IAC) {
+ s->do_telnetopt = 2;
+ } else {
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ }
+ }
+ }
+ *size = j;
+}
+
+static void tcp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ uint8_t buf[1024];
+ int len, size;
+
+ if (!s->connected || s->max_size <= 0)
+ return;
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ size = recv(s->fd, buf, len, 0);
+ if (size == 0) {
+ /* connection closed */
+ s->connected = 0;
+ if (s->listen_fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ }
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ s->fd = -1;
+ } else if (size > 0) {
+ if (s->do_telnetopt)
+ tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+ if (size > 0)
+ s->fd_read(s->fd_opaque, buf, size);
+ }
+}
+
+static void tcp_chr_add_read_handler(CharDriverState *chr,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque)
+{
+ TCPCharDriver *s = chr->opaque;
+
+ s->fd_can_read = fd_can_read;
+ s->fd_read = fd_read;
+ s->fd_opaque = opaque;
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ s->connected = 1;
+ qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+ tcp_chr_read, NULL, chr);
+}
+
+#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+static void tcp_chr_telnet_init(int fd)
+{
+ char buf[3];
+ /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
+ IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ send(fd, (char *)buf, 3, 0);
+}
+
+static void tcp_chr_accept(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = accept(s->listen_fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ if (s->do_telnetopt)
+ tcp_chr_telnet_init(fd);
+ break;
+ }
+ }
+ socket_set_nonblock(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+}
+
+static void tcp_chr_close(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd >= 0)
+ closesocket(s->fd);
+ if (s->listen_fd >= 0)
+ closesocket(s->listen_fd);
+ qemu_free(s);
+}
+
+static CharDriverState *qemu_chr_open_tcp(const char *host_str,
+ int is_telnet)
+{
+ CharDriverState *chr = NULL;
+ TCPCharDriver *s = NULL;
+ int fd = -1, ret, err, val;
+ int is_listen = 0;
+ int is_waitconnect = 1;
+ const char *ptr;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ goto fail;
+
+ ptr = host_str;
+ while((ptr = strchr(ptr,','))) {
+ ptr++;
+ if (!strncmp(ptr,"server",6)) {
+ is_listen = 1;
+ } else if (!strncmp(ptr,"nowait",6)) {
+ is_waitconnect = 0;
+ } else {
+ printf("Unknown option: %s\n", ptr);
+ goto fail;
+ }
+ }
+ if (!is_listen)
+ is_waitconnect = 0;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ goto fail;
+ s = qemu_mallocz(sizeof(TCPCharDriver));
+ if (!s)
+ goto fail;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ goto fail;
+
+ if (!is_waitconnect)
+ socket_set_nonblock(fd);
+
+ s->connected = 0;
+ s->fd = -1;
+ s->listen_fd = -1;
+ if (is_listen) {
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0)
+ goto fail;
+ ret = listen(fd, 0);
+ if (ret < 0)
+ goto fail;
+ s->listen_fd = fd;
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ if (is_telnet)
+ s->do_telnetopt = 1;
+ } else {
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+ } else {
+ goto fail;
+ }
+ } else {
+ s->connected = 1;
+ break;
+ }
+ }
+ s->fd = fd;
+ if (s->connected)
+ tcp_chr_connect(chr);
+ else
+ qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+ }
+
+ chr->opaque = s;
+ chr->chr_write = tcp_chr_write;
+ chr->chr_add_read_handler = tcp_chr_add_read_handler;
+ chr->chr_close = tcp_chr_close;
+ if (is_listen && is_waitconnect) {
+ printf("QEMU waiting for connection on: %s\n", host_str);
+ tcp_chr_accept(chr);
+ socket_set_nonblock(s->listen_fd);
+ }
+
+ return chr;
+ fail:
+ if (fd >= 0)
+ closesocket(fd);
+ qemu_free(s);
+ qemu_free(chr);
+ return NULL;
+}
+
+CharDriverState *qemu_chr_open(const char *filename)
+{
+ const char *p;
+
+ if (!strcmp(filename, "vc")) {
+ return text_console_init(&display_state);
+ } else if (!strcmp(filename, "null")) {
+ return qemu_chr_open_null();
+ } else
+ if (strstart(filename, "tcp:", &p)) {
+ return qemu_chr_open_tcp(p, 0);
+ } else
+ if (strstart(filename, "telnet:", &p)) {
+ return qemu_chr_open_tcp(p, 1);
+ } else
+ if (strstart(filename, "udp:", &p)) {
+ return qemu_chr_open_udp(p);
+ } else
+#ifndef _WIN32
+ if (strstart(filename, "file:", &p)) {
+ return qemu_chr_open_file_out(p);
+ } else if (strstart(filename, "pipe:", &p)) {
+ return qemu_chr_open_pipe(p);
+ } else if (!strcmp(filename, "pty")) {
+ return qemu_chr_open_pty();
+ } else if (!strcmp(filename, "stdio")) {
+ return qemu_chr_open_stdio();
+ } else
+#endif
+#if defined(__linux__)
+ if (strstart(filename, "/dev/parport", NULL)) {
+ return qemu_chr_open_pp(filename);
+ } else
+ if (strstart(filename, "/dev/", NULL)) {
+ return qemu_chr_open_tty(filename);
+ } else
+#endif
+#ifdef _WIN32
+ if (strstart(filename, "COM", NULL)) {
+ return qemu_chr_open_win(filename);
+ } else
+ if (strstart(filename, "pipe:", &p)) {
+ return qemu_chr_open_win_pipe(p);
+ } else
+ if (strstart(filename, "file:", &p)) {
+ return qemu_chr_open_win_file_out(p);
+ }
+#endif
+ {
+ return NULL;
+ }
+}
+
+void qemu_chr_close(CharDriverState *chr)
+{
+ if (chr->chr_close)
+ chr->chr_close(chr);
+}
+
+/***********************************************************/
+/* network device redirectors */
+
+void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+ int len, i, j, c;
+
+ for(i=0;i<size;i+=16) {
+ len = size - i;
+ if (len > 16)
+ len = 16;
+ fprintf(f, "%08x ", i);
+ for(j=0;j<16;j++) {
+ if (j < len)
+ fprintf(f, " %02x", buf[i+j]);
+ else
+ fprintf(f, " ");
+ }
+ fprintf(f, " ");
+ for(j=0;j<len;j++) {
+ c = buf[i+j];
+ if (c < ' ' || c > '~')
+ c = '.';
+ fprintf(f, "%c", c);
+ }
+ fprintf(f, "\n");
+ }
+}
+
+static int parse_macaddr(uint8_t *macaddr, const char *p)
+{
+ int i;
+ for(i = 0; i < 6; i++) {
+ macaddr[i] = strtol(p, (char **)&p, 16);
+ if (i == 5) {
+ if (*p != '\0')
+ return -1;
+ } else {
+ if (*p != ':')
+ return -1;
+ p++;
+ }
+ }
+ return 0;
+}
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+ const char *p, *p1;
+ int len;
+ p = *pp;
+ p1 = strchr(p, sep);
+ if (!p1)
+ return -1;
+ len = p1 - p;
+ p1++;
+ if (buf_size > 0) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p, len);
+ buf[len] = '\0';
+ }
+ *pp = p1;
+ return 0;
+}
+
+int parse_host_src_port(struct sockaddr_in *haddr,
+ struct sockaddr_in *saddr,
+ const char *input_str)
+{
+ char *str = strdup(input_str);
+ char *host_str = str;
+ char *src_str;
+ char *ptr;
+
+ /*
+ * Chop off any extra arguments at the end of the string which
+ * would start with a comma, then fill in the src port information
+ * if it was provided else use the "any address" and "any port".
+ */
+ if ((ptr = strchr(str,',')))
+ *ptr = '\0';
+
+ if ((src_str = strchr(input_str,'@'))) {
+ *src_str = '\0';
+ src_str++;
+ }
+
+ if (parse_host_port(haddr, host_str) < 0)
+ goto fail;
+
+ if (!src_str || *src_str == '\0')
+ src_str = ":0";
+
+ if (parse_host_port(saddr, src_str) < 0)
+ goto fail;
+
+ free(str);
+ return(0);
+
+fail:
+ free(str);
+ return -1;
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str)
+{
+ char buf[512];
+ struct hostent *he;
+ const char *p, *r;
+ int port;
+
+ p = str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ return -1;
+ saddr->sin_family = AF_INET;
+ if (buf[0] == '\0') {
+ saddr->sin_addr.s_addr = 0;
+ } else {
+ if (isdigit(buf[0])) {
+ if (!inet_aton(buf, &saddr->sin_addr))
+ return -1;
+ } else {
+ if ((he = gethostbyname(buf)) == NULL)
+ return - 1;
+ saddr->sin_addr = *(struct in_addr *)he->h_addr;
+ }
+ }
+ port = strtol(p, (char **)&r, 0);
+ if (r == p)
+ return -1;
+ saddr->sin_port = htons(port);
+ return 0;
+}
+
+/* find or alloc a new VLAN */
+VLANState *qemu_find_vlan(int id)
+{
+ VLANState **pvlan, *vlan;
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ if (vlan->id == id)
+ return vlan;
+ }
+ vlan = qemu_mallocz(sizeof(VLANState));
+ if (!vlan)
+ return NULL;
+ vlan->id = id;
+ vlan->next = NULL;
+ pvlan = &first_vlan;
+ while (*pvlan != NULL)
+ pvlan = &(*pvlan)->next;
+ *pvlan = vlan;
+ return vlan;
+}
+
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+ IOReadHandler *fd_read,
+ IOCanRWHandler *fd_can_read,
+ void *opaque)
+{
+ VLANClientState *vc, **pvc;
+ vc = qemu_mallocz(sizeof(VLANClientState));
+ if (!vc)
+ return NULL;
+ vc->fd_read = fd_read;
+ vc->fd_can_read = fd_can_read;
+ vc->opaque = opaque;
+ vc->vlan = vlan;
+
+ vc->next = NULL;
+ pvc = &vlan->first_client;
+ while (*pvc != NULL)
+ pvc = &(*pvc)->next;
+ *pvc = vc;
+ return vc;
+}
+
+int qemu_can_send_packet(VLANClientState *vc1)
+{
+ VLANState *vlan = vc1->vlan;
+ VLANClientState *vc;
+
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc != vc1) {
+ if (vc->fd_can_read && !vc->fd_can_read(vc->opaque))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+{
+ VLANState *vlan = vc1->vlan;
+ VLANClientState *vc;
+
+#if 0
+ printf("vlan %d send:\n", vlan->id);
+ hex_dump(stdout, buf, size);
+#endif
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc != vc1) {
+ vc->fd_read(vc->opaque, buf, size);
+ }
+ }
+}
+
+#if defined(CONFIG_SLIRP)
+
+/* slirp network adapter */
+
+static int slirp_inited;
+static VLANClientState *slirp_vc;
+
+int slirp_can_output(void)
+{
+ return !slirp_vc || qemu_can_send_packet(slirp_vc);
+}
+
+void slirp_output(const uint8_t *pkt, int pkt_len)
+{
+#if 0
+ printf("slirp output:\n");
+ hex_dump(stdout, pkt, pkt_len);
+#endif
+ if (!slirp_vc)
+ return;
+ qemu_send_packet(slirp_vc, pkt, pkt_len);
+}
+
+static void slirp_receive(void *opaque, const uint8_t *buf, int size)
+{
+#if 0
+ printf("slirp input:\n");
+ hex_dump(stdout, buf, size);
+#endif
+ slirp_input(buf, size);
+}
+
+static int net_slirp_init(VLANState *vlan)
+{
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init();
+ }
+ slirp_vc = qemu_new_vlan_client(vlan,
+ slirp_receive, NULL, NULL);
+ snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector");
+ return 0;
+}
+
+static void net_slirp_redir(const char *redir_str)
+{
+ int is_udp;
+ char buf[256], *r;
+ const char *p;
+ struct in_addr guest_addr;
+ int host_port, guest_port;
+
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init();
+ }
+
+ p = redir_str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ if (!strcmp(buf, "tcp")) {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ host_port = strtol(buf, &r, 0);
+ if (r == buf)
+ goto fail;
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ goto fail;
+ if (buf[0] == '\0') {
+ pstrcpy(buf, sizeof(buf), "10.0.2.15");
+ }
+ if (!inet_aton(buf, &guest_addr))
+ goto fail;
+
+ guest_port = strtol(p, &r, 0);
+ if (r == p)
+ goto fail;
+
+ if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) {
+ fprintf(stderr, "qemu: could not set up redirection\n");
+ exit(1);
+ }
+ return;
+ fail:
+ fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n");
+ exit(1);
+}
+
+#ifndef _WIN32
+
+char smb_dir[1024];
+
+static void smb_exit(void)
+{
+ DIR *d;
+ struct dirent *de;
+ char filename[1024];
+
+ /* erase all the files in the directory */
+ d = opendir(smb_dir);
+ for(;;) {
+ de = readdir(d);
+ if (!de)
+ break;
+ if (strcmp(de->d_name, ".") != 0 &&
+ strcmp(de->d_name, "..") != 0) {
+ snprintf(filename, sizeof(filename), "%s/%s",
+ smb_dir, de->d_name);
+ unlink(filename);
+ }
+ }
+ closedir(d);
+ rmdir(smb_dir);
+}
+
+/* automatic user mode samba server configuration */
+void net_slirp_smb(const char *exported_dir)
+{
+ char smb_conf[1024];
+ char smb_cmdline[1024];
+ FILE *f;
+
+ if (!slirp_inited) {
+ slirp_inited = 1;
+ slirp_init();
+ }
+
+ /* XXX: better tmp dir construction */
+ snprintf(smb_dir, sizeof(smb_dir), "/tmp/qemu-smb.%d", getpid());
+ if (mkdir(smb_dir, 0700) < 0) {
+ fprintf(stderr, "qemu: could not create samba server dir '%s'\n", smb_dir);
+ exit(1);
+ }
+ snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_dir, "smb.conf");
+
+ f = fopen(smb_conf, "w");
+ if (!f) {
+ fprintf(stderr, "qemu: could not create samba server configuration file '%s'\n", smb_conf);
+ exit(1);
+ }
+ fprintf(f,
+ "[global]\n"
+ "private dir=%s\n"
+ "smb ports=0\n"
+ "socket address=127.0.0.1\n"
+ "pid directory=%s\n"
+ "lock directory=%s\n"
+ "log file=%s/log.smbd\n"
+ "smb passwd file=%s/smbpasswd\n"
+ "security = share\n"
+ "[qemu]\n"
+ "path=%s\n"
+ "read only=no\n"
+ "guest ok=yes\n",
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ smb_dir,
+ exported_dir
+ );
+ fclose(f);
+ atexit(smb_exit);
+
+ snprintf(smb_cmdline, sizeof(smb_cmdline), "/usr/sbin/smbd -s %s",
+ smb_conf);
+
+ slirp_add_exec(0, smb_cmdline, 4, 139);
+}
+
+#endif /* !defined(_WIN32) */
+
+#endif /* CONFIG_SLIRP */
+
+#if !defined(_WIN32)
+
+typedef struct TAPState {
+ VLANClientState *vc;
+ int fd;
+} TAPState;
+
+static void tap_receive(void *opaque, const uint8_t *buf, int size)
+{
+ TAPState *s = opaque;
+ int ret;
+ for(;;) {
+ ret = write(s->fd, buf, size);
+ if (ret < 0 && (errno == EINTR || errno == EAGAIN)) {
+ } else {
+ break;
+ }
+ }
+}
+
+static void tap_send(void *opaque)
+{
+ TAPState *s = opaque;
+ uint8_t buf[4096];
+ int size;
+
+ size = read(s->fd, buf, sizeof(buf));
+ if (size > 0) {
+ qemu_send_packet(s->vc, buf, size);
+ }
+}
+
+/* fd support */
+
+static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
+{
+ TAPState *s;
+
+ s = qemu_mallocz(sizeof(TAPState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+ s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
+ qemu_set_fd_handler(s->fd, tap_send, NULL, s);
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
+ return s;
+}
+
+#ifdef _BSD
+static int tap_open(char *ifname, int ifname_size)
+{
+ int fd;
+ char *dev;
+ struct stat s;
+
+ fd = open("/dev/tap", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n");
+ return -1;
+ }
+
+ fstat(fd, &s);
+ dev = devname(s.st_rdev, S_IFCHR);
+ pstrcpy(ifname, ifname_size, dev);
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+#elif defined(__sun__)
+static int tap_open(char *ifname, int ifname_size)
+{
+ fprintf(stderr, "warning: tap_open not yet implemented\n");
+ return -1;
+}
+#else
+static int tap_open(char *ifname, int ifname_size)
+{
+ struct ifreq ifr;
+ int fd, ret;
+
+ fd = open("/dev/net/tun", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n");
+ return -1;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (ifname[0] != '\0')
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+ else
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n");
+ close(fd);
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, ifr.ifr_name);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+#endif
+
+static int net_tap_init(VLANState *vlan, const char *ifname1,
+ const char *setup_script)
+{
+ TAPState *s;
+ int pid, status, fd;
+ char *args[3];
+ char **parg;
+ char ifname[128];
+
+ if (ifname1 != NULL)
+ pstrcpy(ifname, sizeof(ifname), ifname1);
+ else
+ ifname[0] = '\0';
+ fd = tap_open(ifname, sizeof(ifname));
+ if (fd < 0)
+ return -1;
+
+ if (!setup_script)
+ setup_script = "";
+ if (setup_script[0] != '\0') {
+ /* try to launch network init script */
+ pid = fork();
+ if (pid >= 0) {
+ if (pid == 0) {
+ parg = args;
+ *parg++ = (char *)setup_script;
+ *parg++ = ifname;
+ *parg++ = NULL;
+ execv(setup_script, args);
+ _exit(1);
+ }
+ while (waitpid(pid, &status, 0) != pid);
+ if (!WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ fprintf(stderr, "%s: could not launch network script\n",
+ setup_script);
+ return -1;
+ }
+ }
+ }
+ s = net_tap_fd_init(vlan, fd);
+ if (!s)
+ return -1;
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "tap: ifname=%s setup_script=%s", ifname, setup_script);
+ return 0;
+}
+
+#endif /* !_WIN32 */
+
+/* network connection */
+typedef struct NetSocketState {
+ VLANClientState *vc;
+ int fd;
+ int state; /* 0 = getting length, 1 = getting data */
+ int index;
+ int packet_len;
+ uint8_t buf[4096];
+ struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+} NetSocketState;
+
+typedef struct NetSocketListenState {
+ VLANState *vlan;
+ int fd;
+} NetSocketListenState;
+
+/* XXX: we consider we can send the whole packet without blocking */
+static void net_socket_receive(void *opaque, const uint8_t *buf, int size)
+{
+ NetSocketState *s = opaque;
+ uint32_t len;
+ len = htonl(size);
+
+ send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+ send_all(s->fd, buf, size);
+}
+
+static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size)
+{
+ NetSocketState *s = opaque;
+ sendto(s->fd, buf, size, 0,
+ (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+}
+
+static void net_socket_send(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int l, size, err;
+ uint8_t buf1[4096];
+ const uint8_t *buf;
+
+ size = recv(s->fd, buf1, sizeof(buf1), 0);
+ if (size < 0) {
+ err = socket_error();
+ if (err != EWOULDBLOCK)
+ goto eoc;
+ } else if (size == 0) {
+ /* end of connection */
+ eoc:
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ return;
+ }
+ buf = buf1;
+ while (size > 0) {
+ /* reassemble a packet from the network */
+ switch(s->state) {
+ case 0:
+ l = 4 - s->index;
+ if (l > size)
+ l = size;
+ memcpy(s->buf + s->index, buf, l);
+ buf += l;
+ size -= l;
+ s->index += l;
+ if (s->index == 4) {
+ /* got length */
+ s->packet_len = ntohl(*(uint32_t *)s->buf);
+ s->index = 0;
+ s->state = 1;
+ }
+ break;
+ case 1:
+ l = s->packet_len - s->index;
+ if (l > size)
+ l = size;
+ memcpy(s->buf + s->index, buf, l);
+ s->index += l;
+ buf += l;
+ size -= l;
+ if (s->index >= s->packet_len) {
+ qemu_send_packet(s->vc, s->buf, s->packet_len);
+ s->index = 0;
+ s->state = 0;
+ }
+ break;
+ }
+ }
+}
+
+static void net_socket_send_dgram(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int size;
+
+ size = recv(s->fd, s->buf, sizeof(s->buf), 0);
+ if (size < 0)
+ return;
+ if (size == 0) {
+ /* end of connection */
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ return;
+ }
+ qemu_send_packet(s->vc, s->buf, size);
+}
+
+static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
+{
+ struct ip_mreq imr;
+ int fd;
+ int val, ret;
+ if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
+ fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
+ inet_ntoa(mcastaddr->sin_addr),
+ (int)ntohl(mcastaddr->sin_addr.s_addr));
+ return -1;
+
+ }
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ return -1;
+ }
+
+ val = 1;
+ ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+ goto fail;
+ }
+
+ ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
+ if (ret < 0) {
+ perror("bind");
+ goto fail;
+ }
+
+ /* Add host to multicast group */
+ imr.imr_multiaddr = mcastaddr->sin_addr;
+ imr.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (const char *)&imr, sizeof(struct ip_mreq));
+ if (ret < 0) {
+ perror("setsockopt(IP_ADD_MEMBERSHIP)");
+ goto fail;
+ }
+
+ /* Force mcast msgs to loopback (eg. several QEMUs in same host */
+ val = 1;
+ ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
+ goto fail;
+ }
+
+ socket_set_nonblock(fd);
+ return fd;
+fail:
+ if (fd >= 0)
+ closesocket(fd);
+ return -1;
+}
+
+static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd,
+ int is_connected)
+{
+ struct sockaddr_in saddr;
+ int newfd;
+ socklen_t saddr_len;
+ NetSocketState *s;
+
+ /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
+ * Because this may be "shared" socket from a "master" process, datagrams would be recv()
+ * by ONLY ONE process: we must "clone" this dgram socket --jjo
+ */
+
+ if (is_connected) {
+ if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
+ /* must be bound */
+ if (saddr.sin_addr.s_addr==0) {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
+ fd);
+ return NULL;
+ }
+ /* clone dgram socket */
+ newfd = net_socket_mcast_create(&saddr);
+ if (newfd < 0) {
+ /* error already reported by net_socket_mcast_create() */
+ close(fd);
+ return NULL;
+ }
+ /* clone newfd to fd, close newfd */
+ dup2(newfd, fd);
+ close(newfd);
+
+ } else {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
+ fd, strerror(errno));
+ return NULL;
+ }
+ }
+
+ s = qemu_mallocz(sizeof(NetSocketState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+
+ s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, NULL, s);
+ qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
+
+ /* mcast: save bound address as dst */
+ if (is_connected) s->dgram_dst=saddr;
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: fd=%d (%s mcast=%s:%d)",
+ fd, is_connected? "cloned" : "",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return s;
+}
+
+static void net_socket_connect(void *opaque)
+{
+ NetSocketState *s = opaque;
+ qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
+}
+
+static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd,
+ int is_connected)
+{
+ NetSocketState *s;
+ s = qemu_mallocz(sizeof(NetSocketState));
+ if (!s)
+ return NULL;
+ s->fd = fd;
+ s->vc = qemu_new_vlan_client(vlan,
+ net_socket_receive, NULL, s);
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: fd=%d", fd);
+ if (is_connected) {
+ net_socket_connect(s);
+ } else {
+ qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
+ }
+ return s;
+}
+
+static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
+ int is_connected)
+{
+ int so_type=-1, optlen=sizeof(so_type);
+
+ if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, &optlen)< 0) {
+ fprintf(stderr, "qemu: error: setsockopt(SO_TYPE) for fd=%d failed\n", fd);
+ return NULL;
+ }
+ switch(so_type) {
+ case SOCK_DGRAM:
+ return net_socket_fd_init_dgram(vlan, fd, is_connected);
+ case SOCK_STREAM:
+ return net_socket_fd_init_stream(vlan, fd, is_connected);
+ default:
+ /* who knows ... this could be a eg. a pty, do warn and continue as stream */
+ fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
+ return net_socket_fd_init_stream(vlan, fd, is_connected);
+ }
+ return NULL;
+}
+
+static void net_socket_accept(void *opaque)
+{
+ NetSocketListenState *s = opaque;
+ NetSocketState *s1;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = accept(s->fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+ s1 = net_socket_fd_init(s->vlan, fd, 1);
+ if (!s1) {
+ closesocket(fd);
+ } else {
+ snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
+ "socket: connection from %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ }
+}
+
+static int net_socket_listen_init(VLANState *vlan, const char *host_str)
+{
+ NetSocketListenState *s;
+ int fd, val, ret;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ s = qemu_mallocz(sizeof(NetSocketListenState));
+ if (!s)
+ return -1;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ return -1;
+ }
+ s->vlan = vlan;
+ s->fd = fd;
+ qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
+ return 0;
+}
+
+static int net_socket_connect_init(VLANState *vlan, const char *host_str)
+{
+ NetSocketState *s;
+ int fd, connected, ret, err;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ connected = 0;
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+ } else {
+ perror("connect");
+ closesocket(fd);
+ return -1;
+ }
+ } else {
+ connected = 1;
+ break;
+ }
+ }
+ s = net_socket_fd_init(vlan, fd, connected);
+ if (!s)
+ return -1;
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: connect to %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+}
+
+static int net_socket_mcast_init(VLANState *vlan, const char *host_str)
+{
+ NetSocketState *s;
+ int fd;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+
+ fd = net_socket_mcast_create(&saddr);
+ if (fd < 0)
+ return -1;
+
+ s = net_socket_fd_init(vlan, fd, 0);
+ if (!s)
+ return -1;
+
+ s->dgram_dst = saddr;
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "socket: mcast=%s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+
+}
+
+static int get_param_value(char *buf, int buf_size,
+ const char *tag, const char *str)
+{
+ const char *p;
+ char *q;
+ char option[128];
+
+ p = str;
+ for(;;) {
+ q = option;
+ while (*p != '\0' && *p != '=') {
+ if ((q - option) < sizeof(option) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p != '=')
+ break;
+ p++;
+ if (!strcmp(tag, option)) {
+ q = buf;
+ while (*p != '\0' && *p != ',') {
+ if ((q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ return q - buf;
+ } else {
+ while (*p != '\0' && *p != ',') {
+ p++;
+ }
+ }
+ if (*p != ',')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+int net_client_init(const char *str)
+{
+ const char *p;
+ char *q;
+ char device[64];
+ char buf[1024];
+ int vlan_id, ret;
+ VLANState *vlan;
+
+ p = str;
+ q = device;
+ while (*p != '\0' && *p != ',') {
+ if ((q - device) < sizeof(device) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p == ',')
+ p++;
+ vlan_id = 0;
+ if (get_param_value(buf, sizeof(buf), "vlan", p)) {
+ vlan_id = strtol(buf, NULL, 0);
+ }
+ vlan = qemu_find_vlan(vlan_id);
+ if (!vlan) {
+ fprintf(stderr, "Could not create vlan %d\n", vlan_id);
+ return -1;
+ }
+ if (!strcmp(device, "nic")) {
+ NICInfo *nd;
+ uint8_t *macaddr;
+
+ if (nb_nics >= MAX_NICS) {
+ fprintf(stderr, "Too Many NICs\n");
+ return -1;
+ }
+ nd = &nd_table[nb_nics];
+ macaddr = nd->macaddr;
+ macaddr[0] = 0x52;
+ macaddr[1] = 0x54;
+ macaddr[2] = 0x00;
+ macaddr[3] = 0x12;
+ macaddr[4] = 0x34;
+ macaddr[5] = 0x56 + nb_nics;
+
+ if (get_param_value(buf, sizeof(buf), "macaddr", p)) {
+ if (parse_macaddr(macaddr, buf) < 0) {
+ fprintf(stderr, "invalid syntax for ethernet address\n");
+ return -1;
+ }
+ }
+ if (get_param_value(buf, sizeof(buf), "model", p)) {
+ nd->model = strdup(buf);
+ }
+ nd->vlan = vlan;
+ nb_nics++;
+ ret = 0;
+ } else
+ if (!strcmp(device, "none")) {
+ /* does nothing. It is needed to signal that no network cards
+ are wanted */
+ ret = 0;
+ } else
+#ifdef CONFIG_SLIRP
+ if (!strcmp(device, "user")) {
+ if (get_param_value(buf, sizeof(buf), "hostname", p)) {
+ pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf);
+ }
+ ret = net_slirp_init(vlan);
+ } else
+#endif
+#ifdef _WIN32
+ if (!strcmp(device, "tap")) {
+ char ifname[64];
+ if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+ fprintf(stderr, "tap: no interface name\n");
+ return -1;
+ }
+ ret = tap_win32_init(vlan, ifname);
+ } else
+#else
+ if (!strcmp(device, "tap")) {
+ char ifname[64];
+ char setup_script[1024];
+ int fd;
+ if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+ fd = strtol(buf, NULL, 0);
+ ret = -1;
+ if (net_tap_fd_init(vlan, fd))
+ ret = 0;
+ } else {
+ get_param_value(ifname, sizeof(ifname), "ifname", p);
+ if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) {
+ pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT);
+ }
+ ret = net_tap_init(vlan, ifname, setup_script);
+ }
+ } else
+#endif
+ if (!strcmp(device, "socket")) {
+ if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+ int fd;
+ fd = strtol(buf, NULL, 0);
+ ret = -1;
+ if (net_socket_fd_init(vlan, fd, 1))
+ ret = 0;
+ } else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) {
+ ret = net_socket_listen_init(vlan, buf);
+ } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
+ ret = net_socket_connect_init(vlan, buf);
+ } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) {
+ ret = net_socket_mcast_init(vlan, buf);
+ } else {
+ fprintf(stderr, "Unknown socket options: %s\n", p);
+ return -1;
+ }
+ } else
+ {
+ fprintf(stderr, "Unknown network device: %s\n", device);
+ return -1;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Could not initialize device '%s'\n", device);
+ }
+
+ return ret;
+}
+
+void do_info_network(void)
+{
+ VLANState *vlan;
+ VLANClientState *vc;
+
+ for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+ term_printf("VLAN %d devices:\n", vlan->id);
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next)
+ term_printf(" %s\n", vc->info_str);
+ }
+}
+
+/***********************************************************/
+/* USB devices */
+
+static USBPort *used_usb_ports;
+static USBPort *free_usb_ports;
+
+/* ??? Maybe change this to register a hub to keep track of the topology. */
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+ usb_attachfn attach)
+{
+ port->opaque = opaque;
+ port->index = index;
+ port->attach = attach;
+ port->next = free_usb_ports;
+ free_usb_ports = port;
+}
+
+static int usb_device_add(const char *devname)
+{
+ const char *p;
+ USBDevice *dev;
+ USBPort *port;
+
+ if (!free_usb_ports)
+ return -1;
+
+ if (strstart(devname, "host:", &p)) {
+ dev = usb_host_device_open(p);
+ } else if (!strcmp(devname, "mouse")) {
+ dev = usb_mouse_init();
+ } else if (!strcmp(devname, "tablet")) {
+ dev = usb_tablet_init();
+ } else if (strstart(devname, "disk:", &p)) {
+ dev = usb_msd_init(p);
+ } else {
+ return -1;
+ }
+ if (!dev)
+ return -1;
+
+ /* Find a USB port to add the device to. */
+ port = free_usb_ports;
+ if (!port->next) {
+ USBDevice *hub;
+
+ /* Create a new hub and chain it on. */
+ free_usb_ports = NULL;
+ port->next = used_usb_ports;
+ used_usb_ports = port;
+
+ hub = usb_hub_init(VM_USB_HUB_SIZE);
+ usb_attach(port, hub);
+ port = free_usb_ports;
+ }
+
+ free_usb_ports = port->next;
+ port->next = used_usb_ports;
+ used_usb_ports = port;
+ usb_attach(port, dev);
+ return 0;
+}
+
+static int usb_device_del(const char *devname)
+{
+ USBPort *port;
+ USBPort **lastp;
+ USBDevice *dev;
+ int bus_num, addr;
+ const char *p;
+
+ if (!used_usb_ports)
+ return -1;
+
+ p = strchr(devname, '.');
+ if (!p)
+ return -1;
+ bus_num = strtoul(devname, NULL, 0);
+ addr = strtoul(p + 1, NULL, 0);
+ if (bus_num != 0)
+ return -1;
+
+ lastp = &used_usb_ports;
+ port = used_usb_ports;
+ while (port && port->dev->addr != addr) {
+ lastp = &port->next;
+ port = port->next;
+ }
+
+ if (!port)
+ return -1;
+
+ dev = port->dev;
+ *lastp = port->next;
+ usb_attach(port, NULL);
+ dev->handle_destroy(dev);
+ port->next = free_usb_ports;
+ free_usb_ports = port;
+ return 0;
+}
+
+void do_usb_add(const char *devname)
+{
+ int ret;
+ ret = usb_device_add(devname);
+ if (ret < 0)
+ term_printf("Could not add USB device '%s'\n", devname);
+}
+
+void do_usb_del(const char *devname)
+{
+ int ret;
+ ret = usb_device_del(devname);
+ if (ret < 0)
+ term_printf("Could not remove USB device '%s'\n", devname);
+}
+
+void usb_info(void)
+{
+ USBDevice *dev;
+ USBPort *port;
+ const char *speed_str;
+
+ if (!usb_enabled) {
+ term_printf("USB support not enabled\n");
+ return;
+ }
+
+ for (port = used_usb_ports; port; port = port->next) {
+ dev = port->dev;
+ if (!dev)
+ continue;
+ switch(dev->speed) {
+ case USB_SPEED_LOW:
+ speed_str = "1.5";
+ break;
+ case USB_SPEED_FULL:
+ speed_str = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed_str = "480";
+ break;
+ default:
+ speed_str = "?";
+ break;
+ }
+ term_printf(" Device %d.%d, Speed %s Mb/s, Product %s\n",
+ 0, dev->addr, speed_str, dev->devname);
+ }
+}
+
+/***********************************************************/
+/* pid file */
+
+static char *pid_filename;
+
+/* Remove PID file. Called on normal exit */
+
+static void remove_pidfile(void)
+{
+ unlink (pid_filename);
+}
+
+static void create_pidfile(const char *filename)
+{
+ struct stat pidstat;
+ FILE *f;
+
+ /* Try to write our PID to the named file */
+ if (stat(filename, &pidstat) < 0) {
+ if (errno == ENOENT) {
+ if ((f = fopen (filename, "w")) == NULL) {
+ perror("Opening pidfile");
+ exit(1);
+ }
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ pid_filename = qemu_strdup(filename);
+ if (!pid_filename) {
+ fprintf(stderr, "Could not save PID filename");
+ exit(1);
+ }
+ atexit(remove_pidfile);
+ }
+ } else {
+ fprintf(stderr, "%s already exists. Remove it and try again.\n",
+ filename);
+ exit(1);
+ }
+}
+
+/***********************************************************/
+/* dumb display */
+
+static void dumb_update(DisplayState *ds, int x, int y, int w, int h)
+{
+}
+
+static void dumb_resize(DisplayState *ds, int w, int h)
+{
+}
+
+static void dumb_refresh(DisplayState *ds)
+{
+ vga_hw_update();
+}
+
+void dumb_display_init(DisplayState *ds)
+{
+ ds->data = NULL;
+ ds->linesize = 0;
+ ds->depth = 0;
+ ds->dpy_update = dumb_update;
+ ds->dpy_resize = dumb_resize;
+ ds->dpy_refresh = dumb_refresh;
+}
+
+/***********************************************************/
+/* I/O handling */
+
+#define MAX_IO_HANDLERS 64
+
+typedef struct IOHandlerRecord {
+ int fd;
+ IOCanRWHandler *fd_read_poll;
+ IOHandler *fd_read;
+ IOHandler *fd_write;
+ void *opaque;
+ /* temporary data */
+ struct pollfd *ufd;
+ struct IOHandlerRecord *next;
+} IOHandlerRecord;
+
+static IOHandlerRecord *first_io_handler;
+
+/* XXX: fd_read_poll should be suppressed, but an API change is
+ necessary in the character devices to suppress fd_can_read(). */
+int qemu_set_fd_handler2(int fd,
+ IOCanRWHandler *fd_read_poll,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ IOHandlerRecord **pioh, *ioh;
+
+ if (!fd_read && !fd_write) {
+ pioh = &first_io_handler;
+ for(;;) {
+ ioh = *pioh;
+ if (ioh == NULL)
+ break;
+ if (ioh->fd == fd) {
+ *pioh = ioh->next;
+ qemu_free(ioh);
+ break;
+ }
+ pioh = &ioh->next;
+ }
+ } else {
+ for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
+ if (ioh->fd == fd)
+ goto found;
+ }
+ ioh = qemu_mallocz(sizeof(IOHandlerRecord));
+ if (!ioh)
+ return -1;
+ ioh->next = first_io_handler;
+ first_io_handler = ioh;
+ found:
+ ioh->fd = fd;
+ ioh->fd_read_poll = fd_read_poll;
+ ioh->fd_read = fd_read;
+ ioh->fd_write = fd_write;
+ ioh->opaque = opaque;
+ }
+ return 0;
+}
+
+int qemu_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
+}
+
+/***********************************************************/
+/* Polling handling */
+
+typedef struct PollingEntry {
+ PollingFunc *func;
+ void *opaque;
+ struct PollingEntry *next;
+} PollingEntry;
+
+static PollingEntry *first_polling_entry;
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque)
+{
+ PollingEntry **ppe, *pe;
+ pe = qemu_mallocz(sizeof(PollingEntry));
+ if (!pe)
+ return -1;
+ pe->func = func;
+ pe->opaque = opaque;
+ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next);
+ *ppe = pe;
+ return 0;
+}
+
+void qemu_del_polling_cb(PollingFunc *func, void *opaque)
+{
+ PollingEntry **ppe, *pe;
+ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) {
+ pe = *ppe;
+ if (pe->func == func && pe->opaque == opaque) {
+ *ppe = pe->next;
+ qemu_free(pe);
+ break;
+ }
+ }
+}
+
+#ifdef _WIN32
+/***********************************************************/
+/* Wait objects support */
+typedef struct WaitObjects {
+ int num;
+ HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
+ WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1];
+ void *opaque[MAXIMUM_WAIT_OBJECTS + 1];
+} WaitObjects;
+
+static WaitObjects wait_objects = {0};
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+ WaitObjects *w = &wait_objects;
+
+ if (w->num >= MAXIMUM_WAIT_OBJECTS)
+ return -1;
+ w->events[w->num] = handle;
+ w->func[w->num] = func;
+ w->opaque[w->num] = opaque;
+ w->num++;
+ return 0;
+}
+
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+ int i, found;
+ WaitObjects *w = &wait_objects;
+
+ found = 0;
+ for (i = 0; i < w->num; i++) {
+ if (w->events[i] == handle)
+ found = 1;
+ if (found) {
+ w->events[i] = w->events[i + 1];
+ w->func[i] = w->func[i + 1];
+ w->opaque[i] = w->opaque[i + 1];
+ }
+ }
+ if (found)
+ w->num--;
+}
+#endif
+
+/***********************************************************/
+/* savevm/loadvm support */
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ fwrite(buf, 1, size, f);
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+ fputc(v, f);
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 24);
+ qemu_put_byte(f, v >> 16);
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+ qemu_put_be32(f, v >> 32);
+ qemu_put_be32(f, v);
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ return fread(buf, 1, size, f);
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+ int v;
+ v = fgetc(f);
+ if (v == EOF)
+ return 0;
+ else
+ return v;
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 24;
+ v |= qemu_get_byte(f) << 16;
+ v |= qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+ uint64_t v;
+ v = (uint64_t)qemu_get_be32(f) << 32;
+ v |= qemu_get_be32(f);
+ return v;
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+ return ftell(f);
+}
+
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+{
+ if (fseek(f, pos, whence) < 0)
+ return -1;
+ return ftell(f);
+}
+
+typedef struct SaveStateEntry {
+ char idstr[256];
+ int instance_id;
+ int version_id;
+ SaveStateHandler *save_state;
+ LoadStateHandler *load_state;
+ void *opaque;
+ struct SaveStateEntry *next;
+} SaveStateEntry;
+
+static SaveStateEntry *first_se;
+
+int register_savevm(const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque)
+{
+ SaveStateEntry *se, **pse;
+
+ se = qemu_malloc(sizeof(SaveStateEntry));
+ if (!se)
+ return -1;
+ pstrcpy(se->idstr, sizeof(se->idstr), idstr);
+ se->instance_id = instance_id;
+ se->version_id = version_id;
+ se->save_state = save_state;
+ se->load_state = load_state;
+ se->opaque = opaque;
+ se->next = NULL;
+
+ /* add at the end of list */
+ pse = &first_se;
+ while (*pse != NULL)
+ pse = &(*pse)->next;
+ *pse = se;
+ return 0;
+}
+
+#define QEMU_VM_FILE_MAGIC 0x5145564d
+#define QEMU_VM_FILE_VERSION 0x00000001
+
+int qemu_savevm(const char *filename)
+{
+ SaveStateEntry *se;
+ QEMUFile *f;
+ int len, len_pos, cur_pos, saved_vm_running, ret;
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ ret = -1;
+ goto the_end;
+ }
+
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+ for(se = first_se; se != NULL; se = se->next) {
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ /* record size: filled later */
+ len_pos = ftell(f);
+ qemu_put_be32(f, 0);
+
+ se->save_state(f, se->opaque);
+
+ /* fill record size */
+ cur_pos = ftell(f);
+ len = ftell(f) - len_pos - 4;
+ fseek(f, len_pos, SEEK_SET);
+ qemu_put_be32(f, len);
+ fseek(f, cur_pos, SEEK_SET);
+ }
+
+ fclose(f);
+ ret = 0;
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+ return ret;
+}
+
+static SaveStateEntry *find_se(const char *idstr, int instance_id)
+{
+ SaveStateEntry *se;
+
+ for(se = first_se; se != NULL; se = se->next) {
+ if (!strcmp(se->idstr, idstr) &&
+ instance_id == se->instance_id)
+ return se;
+ }
+ return NULL;
+}
+
+int qemu_loadvm(const char *filename)
+{
+ SaveStateEntry *se;
+ QEMUFile *f;
+ int len, cur_pos, ret, instance_id, record_len, version_id;
+ int saved_vm_running;
+ unsigned int v;
+ char idstr[256];
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ ret = -1;
+ goto the_end;
+ }
+
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_MAGIC)
+ goto fail;
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_VERSION) {
+ fail:
+ fclose(f);
+ ret = -1;
+ goto the_end;
+ }
+ for(;;) {
+ len = qemu_get_byte(f);
+ if (feof(f))
+ break;
+ qemu_get_buffer(f, idstr, len);
+ idstr[len] = '\0';
+ instance_id = qemu_get_be32(f);
+ version_id = qemu_get_be32(f);
+ record_len = qemu_get_be32(f);
+#if 0
+ printf("idstr=%s instance=0x%x version=%d len=%d\n",
+ idstr, instance_id, version_id, record_len);
+#endif
+ cur_pos = ftell(f);
+ se = find_se(idstr, instance_id);
+ if (!se) {
+ fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n",
+ instance_id, idstr);
+ } else {
+ ret = se->load_state(f, se->opaque, version_id);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
+ instance_id, idstr);
+ }
+ }
+ /* always seek to exact end of record */
+ qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+ }
+ fclose(f);
+ ret = 0;
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+ return ret;
+}
+
+/***********************************************************/
+/* cpu save/restore */
+
+#if defined(TARGET_I386)
+
+static void cpu_put_seg(QEMUFile *f, SegmentCache *dt)
+{
+ qemu_put_be32(f, dt->selector);
+ qemu_put_betl(f, dt->base);
+ qemu_put_be32(f, dt->limit);
+ qemu_put_be32(f, dt->flags);
+}
+
+static void cpu_get_seg(QEMUFile *f, SegmentCache *dt)
+{
+ dt->selector = qemu_get_be32(f);
+ dt->base = qemu_get_betl(f);
+ dt->limit = qemu_get_be32(f);
+ dt->flags = qemu_get_be32(f);
+}
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+ uint16_t fptag, fpus, fpuc, fpregs_format;
+ uint32_t hflags;
+ int i;
+
+ for(i = 0; i < CPU_NB_REGS; i++)
+ qemu_put_betls(f, &env->regs[i]);
+ qemu_put_betls(f, &env->eip);
+ qemu_put_betls(f, &env->eflags);
+ hflags = env->hflags; /* XXX: suppress most of the redundant hflags */
+ qemu_put_be32s(f, &hflags);
+
+ /* FPU */
+ fpuc = env->fpuc;
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= ((!env->fptags[i]) << i);
+ }
+
+ qemu_put_be16s(f, &fpuc);
+ qemu_put_be16s(f, &fpus);
+ qemu_put_be16s(f, &fptag);
+
+#ifdef USE_X86LDOUBLE
+ fpregs_format = 0;
+#else
+ fpregs_format = 1;
+#endif
+ qemu_put_be16s(f, &fpregs_format);
+
+ for(i = 0; i < 8; i++) {
+#ifdef USE_X86LDOUBLE
+ {
+ uint64_t mant;
+ uint16_t exp;
+ /* we save the real CPU data (in case of MMX usage only 'mant'
+ contains the MMX register */
+ cpu_get_fp80(&mant, &exp, env->fpregs[i].d);
+ qemu_put_be64(f, mant);
+ qemu_put_be16(f, exp);
+ }
+#else
+ /* if we use doubles for float emulation, we save the doubles to
+ avoid losing information in case of MMX usage. It can give
+ problems if the image is restored on a CPU where long
+ doubles are used instead. */
+ qemu_put_be64(f, env->fpregs[i].mmx.MMX_Q(0));
+#endif
+ }
+
+ for(i = 0; i < 6; i++)
+ cpu_put_seg(f, &env->segs[i]);
+ cpu_put_seg(f, &env->ldt);
+ cpu_put_seg(f, &env->tr);
+ cpu_put_seg(f, &env->gdt);
+ cpu_put_seg(f, &env->idt);
+
+ qemu_put_be32s(f, &env->sysenter_cs);
+ qemu_put_be32s(f, &env->sysenter_esp);
+ qemu_put_be32s(f, &env->sysenter_eip);
+
+ qemu_put_betls(f, &env->cr[0]);
+ qemu_put_betls(f, &env->cr[2]);
+ qemu_put_betls(f, &env->cr[3]);
+ qemu_put_betls(f, &env->cr[4]);
+
+ for(i = 0; i < 8; i++)
+ qemu_put_betls(f, &env->dr[i]);
+
+ /* MMU */
+ qemu_put_be32s(f, &env->a20_mask);
+
+ /* XMM */
+ qemu_put_be32s(f, &env->mxcsr);
+ for(i = 0; i < CPU_NB_REGS; i++) {
+ qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(0));
+ qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(1));
+ }
+
+#ifdef TARGET_X86_64
+ qemu_put_be64s(f, &env->efer);
+ qemu_put_be64s(f, &env->star);
+ qemu_put_be64s(f, &env->lstar);
+ qemu_put_be64s(f, &env->cstar);
+ qemu_put_be64s(f, &env->fmask);
+ qemu_put_be64s(f, &env->kernelgsbase);
+#endif
+}
+
+#ifdef USE_X86LDOUBLE
+/* XXX: add that in a FPU generic layer */
+union x86_longdouble {
+ uint64_t mant;
+ uint16_t exp;
+};
+
+#define MANTD1(fp) (fp & ((1LL << 52) - 1))
+#define EXPBIAS1 1023
+#define EXPD1(fp) ((fp >> 52) & 0x7FF)
+#define SIGND1(fp) ((fp >> 32) & 0x80000000)
+
+static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp)
+{
+ int e;
+ /* mantissa */
+ p->mant = (MANTD1(temp) << 11) | (1LL << 63);
+ /* exponent + sign */
+ e = EXPD1(temp) - EXPBIAS1 + 16383;
+ e |= SIGND1(temp) >> 16;
+ p->exp = e;
+}
+#endif
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+ int i, guess_mmx;
+ uint32_t hflags;
+ uint16_t fpus, fpuc, fptag, fpregs_format;
+
+ if (version_id != 3)
+ return -EINVAL;
+ for(i = 0; i < CPU_NB_REGS; i++)
+ qemu_get_betls(f, &env->regs[i]);
+ qemu_get_betls(f, &env->eip);
+ qemu_get_betls(f, &env->eflags);
+ qemu_get_be32s(f, &hflags);
+
+ qemu_get_be16s(f, &fpuc);
+ qemu_get_be16s(f, &fpus);
+ qemu_get_be16s(f, &fptag);
+ qemu_get_be16s(f, &fpregs_format);
+
+ /* NOTE: we cannot always restore the FPU state if the image come
+ from a host with a different 'USE_X86LDOUBLE' define. We guess
+ if we are in an MMX state to restore correctly in that case. */
+ guess_mmx = ((fptag == 0xff) && (fpus & 0x3800) == 0);
+ for(i = 0; i < 8; i++) {
+ uint64_t mant;
+ uint16_t exp;
+
+ switch(fpregs_format) {
+ case 0:
+ mant = qemu_get_be64(f);
+ exp = qemu_get_be16(f);
+#ifdef USE_X86LDOUBLE
+ env->fpregs[i].d = cpu_set_fp80(mant, exp);
+#else
+ /* difficult case */
+ if (guess_mmx)
+ env->fpregs[i].mmx.MMX_Q(0) = mant;
+ else
+ env->fpregs[i].d = cpu_set_fp80(mant, exp);
+#endif
+ break;
+ case 1:
+ mant = qemu_get_be64(f);
+#ifdef USE_X86LDOUBLE
+ {
+ union x86_longdouble *p;
+ /* difficult case */
+ p = (void *)&env->fpregs[i];
+ if (guess_mmx) {
+ p->mant = mant;
+ p->exp = 0xffff;
+ } else {
+ fp64_to_fp80(p, mant);
+ }
+ }
+#else
+ env->fpregs[i].mmx.MMX_Q(0) = mant;
+#endif
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ env->fpuc = fpuc;
+ /* XXX: restore FPU round state */
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ fptag ^= 0xff;
+ for(i = 0; i < 8; i++) {
+ env->fptags[i] = (fptag >> i) & 1;
+ }
+
+ for(i = 0; i < 6; i++)
+ cpu_get_seg(f, &env->segs[i]);
+ cpu_get_seg(f, &env->ldt);
+ cpu_get_seg(f, &env->tr);
+ cpu_get_seg(f, &env->gdt);
+ cpu_get_seg(f, &env->idt);
+
+ qemu_get_be32s(f, &env->sysenter_cs);
+ qemu_get_be32s(f, &env->sysenter_esp);
+ qemu_get_be32s(f, &env->sysenter_eip);
+
+ qemu_get_betls(f, &env->cr[0]);
+ qemu_get_betls(f, &env->cr[2]);
+ qemu_get_betls(f, &env->cr[3]);
+ qemu_get_betls(f, &env->cr[4]);
+
+ for(i = 0; i < 8; i++)
+ qemu_get_betls(f, &env->dr[i]);
+
+ /* MMU */
+ qemu_get_be32s(f, &env->a20_mask);
+
+ qemu_get_be32s(f, &env->mxcsr);
+ for(i = 0; i < CPU_NB_REGS; i++) {
+ qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(0));
+ qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(1));
+ }
+
+#ifdef TARGET_X86_64
+ qemu_get_be64s(f, &env->efer);
+ qemu_get_be64s(f, &env->star);
+ qemu_get_be64s(f, &env->lstar);
+ qemu_get_be64s(f, &env->cstar);
+ qemu_get_be64s(f, &env->fmask);
+ qemu_get_be64s(f, &env->kernelgsbase);
+#endif
+
+ /* XXX: compute hflags from scratch, except for CPL and IIF */
+ env->hflags = hflags;
+ tlb_flush(env, 1);
+ return 0;
+}
+
+#elif defined(TARGET_PPC)
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
+
+#elif defined(TARGET_MIPS)
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
+
+#elif defined(TARGET_SPARC)
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+ int i;
+ uint32_t tmp;
+
+ for(i = 0; i < 8; i++)
+ qemu_put_betls(f, &env->gregs[i]);
+ for(i = 0; i < NWINDOWS * 16; i++)
+ qemu_put_betls(f, &env->regbase[i]);
+
+ /* FPU */
+ for(i = 0; i < TARGET_FPREGS; i++) {
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = env->fpr[i];
+ qemu_put_be32(f, u.i);
+ }
+
+ qemu_put_betls(f, &env->pc);
+ qemu_put_betls(f, &env->npc);
+ qemu_put_betls(f, &env->y);
+ tmp = GET_PSR(env);
+ qemu_put_be32(f, tmp);
+ qemu_put_betls(f, &env->fsr);
+ qemu_put_betls(f, &env->tbr);
+#ifndef TARGET_SPARC64
+ qemu_put_be32s(f, &env->wim);
+ /* MMU */
+ for(i = 0; i < 16; i++)
+ qemu_put_be32s(f, &env->mmuregs[i]);
+#endif
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+ int i;
+ uint32_t tmp;
+
+ for(i = 0; i < 8; i++)
+ qemu_get_betls(f, &env->gregs[i]);
+ for(i = 0; i < NWINDOWS * 16; i++)
+ qemu_get_betls(f, &env->regbase[i]);
+
+ /* FPU */
+ for(i = 0; i < TARGET_FPREGS; i++) {
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = qemu_get_be32(f);
+ env->fpr[i] = u.f;
+ }
+
+ qemu_get_betls(f, &env->pc);
+ qemu_get_betls(f, &env->npc);
+ qemu_get_betls(f, &env->y);
+ tmp = qemu_get_be32(f);
+ env->cwp = 0; /* needed to ensure that the wrapping registers are
+ correctly updated */
+ PUT_PSR(env, tmp);
+ qemu_get_betls(f, &env->fsr);
+ qemu_get_betls(f, &env->tbr);
+#ifndef TARGET_SPARC64
+ qemu_get_be32s(f, &env->wim);
+ /* MMU */
+ for(i = 0; i < 16; i++)
+ qemu_get_be32s(f, &env->mmuregs[i]);
+#endif
+ tlb_flush(env, 1);
+ return 0;
+}
+
+#elif defined(TARGET_ARM)
+
+/* ??? Need to implement these. */
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
+
+#else
+
+#warning No CPU save/restore functions
+
+#endif
+
+/***********************************************************/
+/* ram save/restore */
+
+/* we just avoid storing empty pages */
+static void ram_put_page(QEMUFile *f, const uint8_t *buf, int len)
+{
+ int i, v;
+
+ v = buf[0];
+ for(i = 1; i < len; i++) {
+ if (buf[i] != v)
+ goto normal_save;
+ }
+ qemu_put_byte(f, 1);
+ qemu_put_byte(f, v);
+ return;
+ normal_save:
+ qemu_put_byte(f, 0);
+ qemu_put_buffer(f, buf, len);
+}
+
+static int ram_get_page(QEMUFile *f, uint8_t *buf, int len)
+{
+ int v;
+
+ v = qemu_get_byte(f);
+ switch(v) {
+ case 0:
+ if (qemu_get_buffer(f, buf, len) != len)
+ return -EIO;
+ break;
+ case 1:
+ v = qemu_get_byte(f);
+ memset(buf, v, len);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void ram_save(QEMUFile *f, void *opaque)
+{
+ int i;
+ qemu_put_be32(f, phys_ram_size);
+ for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
+ ram_put_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
+ }
+}
+
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ int i, ret;
+
+ if (version_id != 1)
+ return -EINVAL;
+ if (qemu_get_be32(f) != phys_ram_size)
+ return -EINVAL;
+ for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
+ ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/***********************************************************/
+/* machine registration */
+
+QEMUMachine *first_machine = NULL;
+
+int qemu_register_machine(QEMUMachine *m)
+{
+ QEMUMachine **pm;
+ pm = &first_machine;
+ while (*pm != NULL)
+ pm = &(*pm)->next;
+ m->next = NULL;
+ *pm = m;
+ return 0;
+}
+
+QEMUMachine *find_machine(const char *name)
+{
+ QEMUMachine *m;
+
+ for(m = first_machine; m != NULL; m = m->next) {
+ if (!strcmp(m->name, name))
+ return m;
+ }
+ return NULL;
+}
+
+/***********************************************************/
+/* main execution loop */
+
+void gui_update(void *opaque)
+{
+ display_state.dpy_refresh(&display_state);
+ qemu_mod_timer(gui_timer, GUI_REFRESH_INTERVAL + qemu_get_clock(rt_clock));
+}
+
+struct vm_change_state_entry {
+ VMChangeStateHandler *cb;
+ void *opaque;
+ LIST_ENTRY (vm_change_state_entry) entries;
+};
+
+static LIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head;
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+ void *opaque)
+{
+ VMChangeStateEntry *e;
+
+ e = qemu_mallocz(sizeof (*e));
+ if (!e)
+ return NULL;
+
+ e->cb = cb;
+ e->opaque = opaque;
+ LIST_INSERT_HEAD(&vm_change_state_head, e, entries);
+ return e;
+}
+
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e)
+{
+ LIST_REMOVE (e, entries);
+ qemu_free (e);
+}
+
+static void vm_state_notify(int running)
+{
+ VMChangeStateEntry *e;
+
+ for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
+ e->cb(e->opaque, running);
+ }
+}
+
+/* XXX: support several handlers */
+static VMStopHandler *vm_stop_cb;
+static void *vm_stop_opaque;
+
+int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque)
+{
+ vm_stop_cb = cb;
+ vm_stop_opaque = opaque;
+ return 0;
+}
+
+void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque)
+{
+ vm_stop_cb = NULL;
+}
+
+void vm_start(void)
+{
+ if (!vm_running) {
+ cpu_enable_ticks();
+ vm_running = 1;
+ vm_state_notify(1);
+ }
+}
+
+void vm_stop(int reason)
+{
+ if (vm_running) {
+ cpu_disable_ticks();
+ vm_running = 0;
+ if (reason != 0) {
+ if (vm_stop_cb) {
+ vm_stop_cb(vm_stop_opaque, reason);
+ }
+ }
+ vm_state_notify(0);
+ }
+}
+
+/* reset/shutdown handler */
+
+typedef struct QEMUResetEntry {
+ QEMUResetHandler *func;
+ void *opaque;
+ struct QEMUResetEntry *next;
+} QEMUResetEntry;
+
+static QEMUResetEntry *first_reset_entry;
+static int reset_requested;
+static int shutdown_requested;
+static int powerdown_requested;
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque)
+{
+ QEMUResetEntry **pre, *re;
+
+ pre = &first_reset_entry;
+ while (*pre != NULL)
+ pre = &(*pre)->next;
+ re = qemu_mallocz(sizeof(QEMUResetEntry));
+ re->func = func;
+ re->opaque = opaque;
+ re->next = NULL;
+ *pre = re;
+}
+
+void qemu_system_reset(void)
+{
+ QEMUResetEntry *re;
+
+ /* reset all devices */
+ for(re = first_reset_entry; re != NULL; re = re->next) {
+ re->func(re->opaque);
+ }
+}
+
+void qemu_system_reset_request(void)
+{
+ reset_requested = 1;
+ if (cpu_single_env)
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+void qemu_system_shutdown_request(void)
+{
+ shutdown_requested = 1;
+ if (cpu_single_env)
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+void qemu_system_powerdown_request(void)
+{
+ powerdown_requested = 1;
+ if (cpu_single_env)
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+void main_loop_wait(int timeout)
+{
+ IOHandlerRecord *ioh, *ioh_next;
+ fd_set rfds, wfds, xfds;
+ int ret, nfds;
+ struct timeval tv;
+ PollingEntry *pe;
+
+
+ /* XXX: need to suppress polling by better using win32 events */
+ ret = 0;
+ for(pe = first_polling_entry; pe != NULL; pe = pe->next) {
+ ret |= pe->func(pe->opaque);
+ }
+#ifdef _WIN32
+ if (ret == 0 && timeout > 0) {
+ int err;
+ WaitObjects *w = &wait_objects;
+
+ ret = WaitForMultipleObjects(w->num, w->events, FALSE, timeout);
+ if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) {
+ if (w->func[ret - WAIT_OBJECT_0])
+ w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]);
+ } else if (ret == WAIT_TIMEOUT) {
+ } else {
+ err = GetLastError();
+ fprintf(stderr, "Wait error %d %d\n", ret, err);
+ }
+ }
+#endif
+ /* poll any events */
+ /* XXX: separate device handlers from system ones */
+ nfds = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
+ if (ioh->fd_read &&
+ (!ioh->fd_read_poll ||
+ ioh->fd_read_poll(ioh->opaque) != 0)) {
+ FD_SET(ioh->fd, &rfds);
+ if (ioh->fd > nfds)
+ nfds = ioh->fd;
+ }
+ if (ioh->fd_write) {
+ FD_SET(ioh->fd, &wfds);
+ if (ioh->fd > nfds)
+ nfds = ioh->fd;
+ }
+ }
+
+ tv.tv_sec = 0;
+#ifdef _WIN32
+ tv.tv_usec = 0;
+#else
+ tv.tv_usec = timeout * 1000;
+#endif
+#if defined(CONFIG_SLIRP)
+ if (slirp_inited) {
+ slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+ }
+#endif
+ ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+ if (ret > 0) {
+ /* XXX: better handling of removal */
+ for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) {
+ ioh_next = ioh->next;
+ if (FD_ISSET(ioh->fd, &rfds)) {
+ ioh->fd_read(ioh->opaque);
+ }
+ if (FD_ISSET(ioh->fd, &wfds)) {
+ ioh->fd_write(ioh->opaque);
+ }
+ }
+ }
+#if defined(CONFIG_SLIRP)
+ if (slirp_inited) {
+ if (ret < 0) {
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ }
+ slirp_select_poll(&rfds, &wfds, &xfds);
+ }
+#endif
+#ifdef _WIN32
+ tap_win32_poll();
+#endif
+
+ if (vm_running) {
+ qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],
+ qemu_get_clock(vm_clock));
+ /* run dma transfers, if any */
+ DMA_run();
+ }
+
+ /* real time timers */
+ qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME],
+ qemu_get_clock(rt_clock));
+}
+
+static CPUState *cur_cpu;
+
+int main_loop(void)
+{
+ int ret, timeout;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+ CPUState *env;
+
+ cur_cpu = first_cpu;
+ for(;;) {
+ if (vm_running) {
+
+ env = cur_cpu;
+ for(;;) {
+ /* get next cpu */
+ env = env->next_cpu;
+ if (!env)
+ env = first_cpu;
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+ ret = cpu_exec(env);
+#ifdef CONFIG_PROFILER
+ qemu_time += profile_getclock() - ti;
+#endif
+ if (ret != EXCP_HALTED)
+ break;
+ /* all CPUs are halted ? */
+ if (env == cur_cpu) {
+ ret = EXCP_HLT;
+ break;
+ }
+ }
+ cur_cpu = env;
+
+ if (shutdown_requested) {
+ ret = EXCP_INTERRUPT;
+ break;
+ }
+ if (reset_requested) {
+ reset_requested = 0;
+ qemu_system_reset();
+ ret = EXCP_INTERRUPT;
+ }
+ if (powerdown_requested) {
+ powerdown_requested = 0;
+ qemu_system_powerdown();
+ ret = EXCP_INTERRUPT;
+ }
+ if (ret == EXCP_DEBUG) {
+ vm_stop(EXCP_DEBUG);
+ }
+ /* if hlt instruction, we wait until the next IRQ */
+ /* XXX: use timeout computed from timers */
+ if (ret == EXCP_HLT)
+ timeout = 10;
+ else
+ timeout = 0;
+ } else {
+ timeout = 10;
+ }
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+ main_loop_wait(timeout);
+#ifdef CONFIG_PROFILER
+ dev_time += profile_getclock() - ti;
+#endif
+ }
+ cpu_disable_ticks();
+ return ret;
+}
+
+void help(void)
+{
+ printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n"
+ "usage: %s [options] [disk_image]\n"
+ "\n"
+ "'disk_image' is a raw hard image image for IDE hard disk 0\n"
+ "\n"
+ "Standard options:\n"
+ "-M machine select emulated machine (-M ? for list)\n"
+ "-fda/-fdb file use 'file' as floppy disk 0/1 image\n"
+ "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n"
+ "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n"
+ "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n"
+ "-boot [a|c|d] boot on floppy (a), hard disk (c) or CD-ROM (d)\n"
+ "-snapshot write to temporary files instead of disk image files\n"
+#ifdef TARGET_I386
+ "-no-fd-bootchk disable boot signature checking for floppy disks\n"
+#endif
+ "-m megs set virtual RAM size to megs MB [default=%d]\n"
+ "-smp n set the number of CPUs to 'n' [default=1]\n"
+ "-nographic disable graphical output and redirect serial I/Os to console\n"
+#ifndef _WIN32
+ "-k language use keyboard layout (for example \"fr\" for French)\n"
+#endif
+#ifdef HAS_AUDIO
+ "-audio-help print list of audio drivers and their options\n"
+ "-soundhw c1,... enable audio support\n"
+ " and only specified sound cards (comma separated list)\n"
+ " use -soundhw ? to get the list of supported cards\n"
+ " use -soundhw all to enable all of them\n"
+#endif
+ "-localtime set the real time clock to local time [default=utc]\n"
+ "-full-screen start in full screen\n"
+#ifdef TARGET_I386
+ "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n"
+#endif
+ "-usb enable the USB driver (will be the default soon)\n"
+ "-usbdevice name add the host or guest USB device 'name'\n"
+#if defined(TARGET_PPC) || defined(TARGET_SPARC)
+ "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n"
+#endif
+ "\n"
+ "Network options:\n"
+ "-net nic[,vlan=n][,macaddr=addr][,model=type]\n"
+ " create a new Network Interface Card and connect it to VLAN 'n'\n"
+#ifdef CONFIG_SLIRP
+ "-net user[,vlan=n][,hostname=host]\n"
+ " connect the user mode network stack to VLAN 'n' and send\n"
+ " hostname 'host' to DHCP clients\n"
+#endif
+#ifdef _WIN32
+ "-net tap[,vlan=n],ifname=name\n"
+ " connect the host TAP network interface to VLAN 'n'\n"
+#else
+ "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file]\n"
+ " connect the host TAP network interface to VLAN 'n' and use\n"
+ " the network script 'file' (default=%s);\n"
+ " use 'fd=h' to connect to an already opened TAP interface\n"
+#endif
+ "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
+ " connect the vlan 'n' to another VLAN using a socket connection\n"
+ "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
+ " connect the vlan 'n' to multicast maddr and port\n"
+ "-net none use it alone to have zero network devices; if no -net option\n"
+ " is provided, the default is '-net nic -net user'\n"
+ "\n"
+#ifdef CONFIG_SLIRP
+ "-tftp prefix allow tftp access to files starting with prefix [-net user]\n"
+#ifndef _WIN32
+ "-smb dir allow SMB access to files in 'dir' [-net user]\n"
+#endif
+ "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
+ " redirect TCP or UDP connections from host to guest [-net user]\n"
+#endif
+ "\n"
+ "Linux boot specific:\n"
+ "-kernel bzImage use 'bzImage' as kernel image\n"
+ "-append cmdline use 'cmdline' as kernel command line\n"
+ "-initrd file use 'file' as initial ram disk\n"
+ "\n"
+ "Debug/Expert options:\n"
+ "-monitor dev redirect the monitor to char device 'dev'\n"
+ "-serial dev redirect the serial port to char device 'dev'\n"
+ "-parallel dev redirect the parallel port to char device 'dev'\n"
+ "-pidfile file Write PID to 'file'\n"
+ "-S freeze CPU at startup (use 'c' to start execution)\n"
+ "-s wait gdb connection to port %d\n"
+ "-p port change gdb connection port\n"
+ "-d item1,... output log to %s (use -d ? for a list of log items)\n"
+ "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n"
+ " translation (t=none or lba) (usually qemu can guess them)\n"
+ "-L path set the directory for the BIOS and VGA BIOS\n"
+#ifdef USE_KQEMU
+ "-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n"
+ "-no-kqemu disable KQEMU kernel module usage\n"
+#endif
+#ifdef USE_CODE_COPY
+ "-no-code-copy disable code copy acceleration\n"
+#endif
+#ifdef TARGET_I386
+ "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n"
+ " (default is CL-GD5446 PCI VGA)\n"
+ "-no-acpi disable ACPI\n"
+#endif
+ "-loadvm file start right away with a saved state (loadvm in monitor)\n"
+ "-vnc display start a VNC server on display\n"
+ "\n"
+ "During emulation, the following keys are useful:\n"
+ "ctrl-alt-f toggle full screen\n"
+ "ctrl-alt-n switch to virtual console 'n'\n"
+ "ctrl-alt toggle mouse and keyboard grab\n"
+ "\n"
+ "When using -nographic, press 'ctrl-a h' to get some help.\n"
+ ,
+ "qemu",
+ DEFAULT_RAM_SIZE,
+#ifndef _WIN32
+ DEFAULT_NETWORK_SCRIPT,
+#endif
+ DEFAULT_GDBSTUB_PORT,
+ "/tmp/qemu.log");
+ exit(1);
+}
+
+#define HAS_ARG 0x0001
+
+enum {
+ QEMU_OPTION_h,
+
+ QEMU_OPTION_M,
+ QEMU_OPTION_fda,
+ QEMU_OPTION_fdb,
+ QEMU_OPTION_hda,
+ QEMU_OPTION_hdb,
+ QEMU_OPTION_hdc,
+ QEMU_OPTION_hdd,
+ QEMU_OPTION_cdrom,
+ QEMU_OPTION_boot,
+ QEMU_OPTION_snapshot,
+#ifdef TARGET_I386
+ QEMU_OPTION_no_fd_bootchk,
+#endif
+ QEMU_OPTION_m,
+ QEMU_OPTION_nographic,
+#ifdef HAS_AUDIO
+ QEMU_OPTION_audio_help,
+ QEMU_OPTION_soundhw,
+#endif
+
+ QEMU_OPTION_net,
+ QEMU_OPTION_tftp,
+ QEMU_OPTION_smb,
+ QEMU_OPTION_redir,
+
+ QEMU_OPTION_kernel,
+ QEMU_OPTION_append,
+ QEMU_OPTION_initrd,
+
+ QEMU_OPTION_S,
+ QEMU_OPTION_s,
+ QEMU_OPTION_p,
+ QEMU_OPTION_d,
+ QEMU_OPTION_hdachs,
+ QEMU_OPTION_L,
+ QEMU_OPTION_no_code_copy,
+ QEMU_OPTION_k,
+ QEMU_OPTION_localtime,
+ QEMU_OPTION_cirrusvga,
+ QEMU_OPTION_g,
+ QEMU_OPTION_std_vga,
+ QEMU_OPTION_monitor,
+ QEMU_OPTION_serial,
+ QEMU_OPTION_parallel,
+ QEMU_OPTION_loadvm,
+ QEMU_OPTION_full_screen,
+ QEMU_OPTION_pidfile,
+ QEMU_OPTION_no_kqemu,
+ QEMU_OPTION_kernel_kqemu,
+ QEMU_OPTION_win2k_hack,
+ QEMU_OPTION_usb,
+ QEMU_OPTION_usbdevice,
+ QEMU_OPTION_smp,
+ QEMU_OPTION_vnc,
+ QEMU_OPTION_no_acpi,
+};
+
+typedef struct QEMUOption {
+ const char *name;
+ int flags;
+ int index;
+} QEMUOption;
+
+const QEMUOption qemu_options[] = {
+ { "h", 0, QEMU_OPTION_h },
+
+ { "M", HAS_ARG, QEMU_OPTION_M },
+ { "fda", HAS_ARG, QEMU_OPTION_fda },
+ { "fdb", HAS_ARG, QEMU_OPTION_fdb },
+ { "hda", HAS_ARG, QEMU_OPTION_hda },
+ { "hdb", HAS_ARG, QEMU_OPTION_hdb },
+ { "hdc", HAS_ARG, QEMU_OPTION_hdc },
+ { "hdd", HAS_ARG, QEMU_OPTION_hdd },
+ { "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
+ { "boot", HAS_ARG, QEMU_OPTION_boot },
+ { "snapshot", 0, QEMU_OPTION_snapshot },
+#ifdef TARGET_I386
+ { "no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk },
+#endif
+ { "m", HAS_ARG, QEMU_OPTION_m },
+ { "nographic", 0, QEMU_OPTION_nographic },
+ { "k", HAS_ARG, QEMU_OPTION_k },
+#ifdef HAS_AUDIO
+ { "audio-help", 0, QEMU_OPTION_audio_help },
+ { "soundhw", HAS_ARG, QEMU_OPTION_soundhw },
+#endif
+
+ { "net", HAS_ARG, QEMU_OPTION_net},
+#ifdef CONFIG_SLIRP
+ { "tftp", HAS_ARG, QEMU_OPTION_tftp },
+#ifndef _WIN32
+ { "smb", HAS_ARG, QEMU_OPTION_smb },
+#endif
+ { "redir", HAS_ARG, QEMU_OPTION_redir },
+#endif
+
+ { "kernel", HAS_ARG, QEMU_OPTION_kernel },
+ { "append", HAS_ARG, QEMU_OPTION_append },
+ { "initrd", HAS_ARG, QEMU_OPTION_initrd },
+
+ { "S", 0, QEMU_OPTION_S },
+ { "s", 0, QEMU_OPTION_s },
+ { "p", HAS_ARG, QEMU_OPTION_p },
+ { "d", HAS_ARG, QEMU_OPTION_d },
+ { "hdachs", HAS_ARG, QEMU_OPTION_hdachs },
+ { "L", HAS_ARG, QEMU_OPTION_L },
+ { "no-code-copy", 0, QEMU_OPTION_no_code_copy },
+#ifdef USE_KQEMU
+ { "no-kqemu", 0, QEMU_OPTION_no_kqemu },
+ { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu },
+#endif
+#if defined(TARGET_PPC) || defined(TARGET_SPARC)
+ { "g", 1, QEMU_OPTION_g },
+#endif
+ { "localtime", 0, QEMU_OPTION_localtime },
+ { "std-vga", 0, QEMU_OPTION_std_vga },
+ { "monitor", 1, QEMU_OPTION_monitor },
+ { "serial", 1, QEMU_OPTION_serial },
+ { "parallel", 1, QEMU_OPTION_parallel },
+ { "loadvm", HAS_ARG, QEMU_OPTION_loadvm },
+ { "full-screen", 0, QEMU_OPTION_full_screen },
+ { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
+ { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
+ { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
+ { "smp", HAS_ARG, QEMU_OPTION_smp },
+ { "vnc", HAS_ARG, QEMU_OPTION_vnc },
+
+ /* temporary options */
+ { "usb", 0, QEMU_OPTION_usb },
+ { "cirrusvga", 0, QEMU_OPTION_cirrusvga },
+ { "no-acpi", 0, QEMU_OPTION_no_acpi },
+ { NULL },
+};
+
+#if defined (TARGET_I386) && defined(USE_CODE_COPY)
+
+/* this stack is only used during signal handling */
+#define SIGNAL_STACK_SIZE 32768
+
+static uint8_t *signal_stack;
+
+#endif
+
+/* password input */
+
+static BlockDriverState *get_bdrv(int index)
+{
+ BlockDriverState *bs;
+
+ if (index < 4) {
+ bs = bs_table[index];
+ } else if (index < 6) {
+ bs = fd_table[index - 4];
+ } else {
+ bs = NULL;
+ }
+ return bs;
+}
+
+static void read_passwords(void)
+{
+ BlockDriverState *bs;
+ int i, j;
+ char password[256];
+
+ for(i = 0; i < 6; i++) {
+ bs = get_bdrv(i);
+ if (bs && bdrv_is_encrypted(bs)) {
+ term_printf("%s is encrypted.\n", bdrv_get_device_name(bs));
+ for(j = 0; j < 3; j++) {
+ monitor_readline("Password: ",
+ 1, password, sizeof(password));
+ if (bdrv_set_key(bs, password) == 0)
+ break;
+ term_printf("invalid password\n");
+ }
+ }
+ }
+}
+
+/* XXX: currently we cannot use simultaneously different CPUs */
+void register_machines(void)
+{
+#if defined(TARGET_I386)
+ qemu_register_machine(&pc_machine);
+ qemu_register_machine(&isapc_machine);
+#elif defined(TARGET_PPC)
+ qemu_register_machine(&heathrow_machine);
+ qemu_register_machine(&core99_machine);
+ qemu_register_machine(&prep_machine);
+#elif defined(TARGET_MIPS)
+ qemu_register_machine(&mips_machine);
+#elif defined(TARGET_SPARC)
+#ifdef TARGET_SPARC64
+ qemu_register_machine(&sun4u_machine);
+#else
+ qemu_register_machine(&sun4m_machine);
+#endif
+#elif defined(TARGET_ARM)
+ qemu_register_machine(&integratorcp926_machine);
+ qemu_register_machine(&integratorcp1026_machine);
+ qemu_register_machine(&versatilepb_machine);
+ qemu_register_machine(&versatileab_machine);
+#elif defined(TARGET_SH4)
+ qemu_register_machine(&shix_machine);
+#else
+#error unsupported CPU
+#endif
+}
+
+#ifdef HAS_AUDIO
+struct soundhw soundhw[] = {
+#ifdef TARGET_I386
+ {
+ "pcspk",
+ "PC speaker",
+ 0,
+ 1,
+ { .init_isa = pcspk_audio_init }
+ },
+#endif
+ {
+ "sb16",
+ "Creative Sound Blaster 16",
+ 0,
+ 1,
+ { .init_isa = SB16_init }
+ },
+
+#ifdef CONFIG_ADLIB
+ {
+ "adlib",
+#ifdef HAS_YMF262
+ "Yamaha YMF262 (OPL3)",
+#else
+ "Yamaha YM3812 (OPL2)",
+#endif
+ 0,
+ 1,
+ { .init_isa = Adlib_init }
+ },
+#endif
+
+#ifdef CONFIG_GUS
+ {
+ "gus",
+ "Gravis Ultrasound GF1",
+ 0,
+ 1,
+ { .init_isa = GUS_init }
+ },
+#endif
+
+ {
+ "es1370",
+ "ENSONIQ AudioPCI ES1370",
+ 0,
+ 0,
+ { .init_pci = es1370_init }
+ },
+
+ { NULL, NULL, 0, 0, { NULL } }
+};
+
+static void select_soundhw (const char *optarg)
+{
+ struct soundhw *c;
+
+ if (*optarg == '?') {
+ show_valid_cards:
+
+ printf ("Valid sound card names (comma separated):\n");
+ for (c = soundhw; c->name; ++c) {
+ printf ("%-11s %s\n", c->name, c->descr);
+ }
+ printf ("\n-soundhw all will enable all of the above\n");
+ exit (*optarg != '?');
+ }
+ else {
+ size_t l;
+ const char *p;
+ char *e;
+ int bad_card = 0;
+
+ if (!strcmp (optarg, "all")) {
+ for (c = soundhw; c->name; ++c) {
+ c->enabled = 1;
+ }
+ return;
+ }
+
+ p = optarg;
+ while (*p) {
+ e = strchr (p, ',');
+ l = !e ? strlen (p) : (size_t) (e - p);
+
+ for (c = soundhw; c->name; ++c) {
+ if (!strncmp (c->name, p, l)) {
+ c->enabled = 1;
+ break;
+ }
+ }
+
+ if (!c->name) {
+ if (l > 80) {
+ fprintf (stderr,
+ "Unknown sound card name (too big to show)\n");
+ }
+ else {
+ fprintf (stderr, "Unknown sound card name `%.*s'\n",
+ (int) l, p);
+ }
+ bad_card = 1;
+ }
+ p += l + (e != NULL);
+ }
+
+ if (bad_card)
+ goto show_valid_cards;
+ }
+}
+#endif
+
+#ifdef _WIN32
+static BOOL WINAPI qemu_ctrl_handler(DWORD type)
+{
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+#endif
+
+#define MAX_NET_CLIENTS 32
+
+int main(int argc, char **argv)
+{
+#ifdef CONFIG_GDBSTUB
+ int use_gdbstub, gdbstub_port;
+#endif
+ int i, cdrom_index;
+ int snapshot, linux_boot;
+ const char *initrd_filename;
+ const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD];
+ const char *kernel_filename, *kernel_cmdline;
+ DisplayState *ds = &display_state;
+ int cyls, heads, secs, translation;
+ int start_emulation = 1;
+ char net_clients[MAX_NET_CLIENTS][256];
+ int nb_net_clients;
+ int optind;
+ const char *r, *optarg;
+ CharDriverState *monitor_hd;
+ char monitor_device[128];
+ char serial_devices[MAX_SERIAL_PORTS][128];
+ int serial_device_index;
+ char parallel_devices[MAX_PARALLEL_PORTS][128];
+ int parallel_device_index;
+ const char *loadvm = NULL;
+ QEMUMachine *machine;
+ char usb_devices[MAX_USB_CMDLINE][128];
+ int usb_devices_index;
+
+ LIST_INIT (&vm_change_state_head);
+#ifndef _WIN32
+ {
+ struct sigaction act;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, NULL);
+ }
+#else
+ SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE);
+ /* Note: cpu_interrupt() is currently not SMP safe, so we force
+ QEMU to run on a single CPU */
+ {
+ HANDLE h;
+ DWORD mask, smask;
+ int i;
+ h = GetCurrentProcess();
+ if (GetProcessAffinityMask(h, &mask, &smask)) {
+ for(i = 0; i < 32; i++) {
+ if (mask & (1 << i))
+ break;
+ }
+ if (i != 32) {
+ mask = 1 << i;
+ SetProcessAffinityMask(h, mask);
+ }
+ }
+ }
+#endif
+
+ register_machines();
+ machine = first_machine;
+ initrd_filename = NULL;
+ for(i = 0; i < MAX_FD; i++)
+ fd_filename[i] = NULL;
+ for(i = 0; i < MAX_DISKS; i++)
+ hd_filename[i] = NULL;
+ ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;
+ vga_ram_size = VGA_RAM_SIZE;
+ bios_size = BIOS_SIZE;
+#ifdef CONFIG_GDBSTUB
+ use_gdbstub = 0;
+ gdbstub_port = DEFAULT_GDBSTUB_PORT;
+#endif
+ snapshot = 0;
+ nographic = 0;
+ kernel_filename = NULL;
+ kernel_cmdline = "";
+#ifdef TARGET_PPC
+ cdrom_index = 1;
+#else
+ cdrom_index = 2;
+#endif
+ cyls = heads = secs = 0;
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ pstrcpy(monitor_device, sizeof(monitor_device), "vc");
+
+ pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "vc");
+ for(i = 1; i < MAX_SERIAL_PORTS; i++)
+ serial_devices[i][0] = '\0';
+ serial_device_index = 0;
+
+ pstrcpy(parallel_devices[0], sizeof(parallel_devices[0]), "vc");
+ for(i = 1; i < MAX_PARALLEL_PORTS; i++)
+ parallel_devices[i][0] = '\0';
+ parallel_device_index = 0;
+
+ usb_devices_index = 0;
+
+ nb_net_clients = 0;
+
+ nb_nics = 0;
+ /* default mac address of the first network interface */
+
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ r = argv[optind];
+ if (r[0] != '-') {
+ hd_filename[0] = argv[optind++];
+ } else {
+ const QEMUOption *popt;
+
+ optind++;
+ popt = qemu_options;
+ for(;;) {
+ if (!popt->name) {
+ fprintf(stderr, "%s: invalid option -- '%s'\n",
+ argv[0], r);
+ exit(1);
+ }
+ if (!strcmp(popt->name, r + 1))
+ break;
+ popt++;
+ }
+ if (popt->flags & HAS_ARG) {
+ if (optind >= argc) {
+ fprintf(stderr, "%s: option '%s' requires an argument\n",
+ argv[0], r);
+ exit(1);
+ }
+ optarg = argv[optind++];
+ } else {
+ optarg = NULL;
+ }
+
+ switch(popt->index) {
+ case QEMU_OPTION_M:
+ machine = find_machine(optarg);
+ if (!machine) {
+ QEMUMachine *m;
+ printf("Supported machines are:\n");
+ for(m = first_machine; m != NULL; m = m->next) {
+ printf("%-10s %s%s\n",
+ m->name, m->desc,
+ m == first_machine ? " (default)" : "");
+ }
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_initrd:
+ initrd_filename = optarg;
+ break;
+ case QEMU_OPTION_hda:
+ case QEMU_OPTION_hdb:
+ case QEMU_OPTION_hdc:
+ case QEMU_OPTION_hdd:
+ {
+ int hd_index;
+ hd_index = popt->index - QEMU_OPTION_hda;
+ hd_filename[hd_index] = optarg;
+ if (hd_index == cdrom_index)
+ cdrom_index = -1;
+ }
+ break;
+ case QEMU_OPTION_snapshot:
+ snapshot = 1;
+ break;
+ case QEMU_OPTION_hdachs:
+ {
+ const char *p;
+ p = optarg;
+ cyls = strtol(p, (char **)&p, 0);
+ if (cyls < 1 || cyls > 16383)
+ goto chs_fail;
+ if (*p != ',')
+ goto chs_fail;
+ p++;
+ heads = strtol(p, (char **)&p, 0);
+ if (heads < 1 || heads > 16)
+ goto chs_fail;
+ if (*p != ',')
+ goto chs_fail;
+ p++;
+ secs = strtol(p, (char **)&p, 0);
+ if (secs < 1 || secs > 63)
+ goto chs_fail;
+ if (*p == ',') {
+ p++;
+ if (!strcmp(p, "none"))
+ translation = BIOS_ATA_TRANSLATION_NONE;
+ else if (!strcmp(p, "lba"))
+ translation = BIOS_ATA_TRANSLATION_LBA;
+ else if (!strcmp(p, "auto"))
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ else
+ goto chs_fail;
+ } else if (*p != '\0') {
+ chs_fail:
+ fprintf(stderr, "qemu: invalid physical CHS format\n");
+ exit(1);
+ }
+ }
+ break;
+ case QEMU_OPTION_nographic:
+ pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
+ pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio");
+ nographic = 1;
+ break;
+ case QEMU_OPTION_kernel:
+ kernel_filename = optarg;
+ break;
+ case QEMU_OPTION_append:
+ kernel_cmdline = optarg;
+ break;
+ case QEMU_OPTION_cdrom:
+ if (cdrom_index >= 0) {
+ hd_filename[cdrom_index] = optarg;
+ }
+ break;
+ case QEMU_OPTION_boot:
+ boot_device = optarg[0];
+ if (boot_device != 'a' &&
+#ifdef TARGET_SPARC
+ // Network boot
+ boot_device != 'n' &&
+#endif
+ boot_device != 'c' && boot_device != 'd') {
+ fprintf(stderr, "qemu: invalid boot device '%c'\n", boot_device);
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_fda:
+ fd_filename[0] = optarg;
+ break;
+ case QEMU_OPTION_fdb:
+ fd_filename[1] = optarg;
+ break;
+#ifdef TARGET_I386
+ case QEMU_OPTION_no_fd_bootchk:
+ fd_bootchk = 0;
+ break;
+#endif
+ case QEMU_OPTION_no_code_copy:
+ code_copy_enabled = 0;
+ break;
+ case QEMU_OPTION_net:
+ if (nb_net_clients >= MAX_NET_CLIENTS) {
+ fprintf(stderr, "qemu: too many network clients\n");
+ exit(1);
+ }
+ pstrcpy(net_clients[nb_net_clients],
+ sizeof(net_clients[0]),
+ optarg);
+ nb_net_clients++;
+ break;
+#ifdef CONFIG_SLIRP
+ case QEMU_OPTION_tftp:
+ tftp_prefix = optarg;
+ break;
+#ifndef _WIN32
+ case QEMU_OPTION_smb:
+ net_slirp_smb(optarg);
+ break;
+#endif
+ case QEMU_OPTION_redir:
+ net_slirp_redir(optarg);
+ break;
+#endif
+#ifdef HAS_AUDIO
+ case QEMU_OPTION_audio_help:
+ AUD_help ();
+ exit (0);
+ break;
+ case QEMU_OPTION_soundhw:
+ select_soundhw (optarg);
+ break;
+#endif
+ case QEMU_OPTION_h:
+ help();
+ break;
+ case QEMU_OPTION_m:
+ ram_size = atoi(optarg) * 1024 * 1024;
+ if (ram_size <= 0)
+ help();
+ if (ram_size > PHYS_RAM_MAX_SIZE) {
+ fprintf(stderr, "qemu: at most %d MB RAM can be simulated\n",
+ PHYS_RAM_MAX_SIZE / (1024 * 1024));
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_d:
+ {
+ int mask;
+ CPULogItem *item;
+
+ mask = cpu_str_to_log_mask(optarg);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ }
+ break;
+#ifdef CONFIG_GDBSTUB
+ case QEMU_OPTION_s:
+ use_gdbstub = 1;
+ break;
+ case QEMU_OPTION_p:
+ gdbstub_port = atoi(optarg);
+ break;
+#endif
+ case QEMU_OPTION_L:
+ bios_dir = optarg;
+ break;
+ case QEMU_OPTION_S:
+ start_emulation = 0;
+ break;
+ case QEMU_OPTION_k:
+ keyboard_layout = optarg;
+ break;
+ case QEMU_OPTION_localtime:
+ rtc_utc = 0;
+ break;
+ case QEMU_OPTION_cirrusvga:
+ cirrus_vga_enabled = 1;
+ break;
+ case QEMU_OPTION_std_vga:
+ cirrus_vga_enabled = 0;
+ break;
+ case QEMU_OPTION_g:
+ {
+ const char *p;
+ int w, h, depth;
+ p = optarg;
+ w = strtol(p, (char **)&p, 10);
+ if (w <= 0) {
+ graphic_error:
+ fprintf(stderr, "qemu: invalid resolution or depth\n");
+ exit(1);
+ }
+ if (*p != 'x')
+ goto graphic_error;
+ p++;
+ h = strtol(p, (char **)&p, 10);
+ if (h <= 0)
+ goto graphic_error;
+ if (*p == 'x') {
+ p++;
+ depth = strtol(p, (char **)&p, 10);
+ if (depth != 8 && depth != 15 && depth != 16 &&
+ depth != 24 && depth != 32)
+ goto graphic_error;
+ } else if (*p == '\0') {
+ depth = graphic_depth;
+ } else {
+ goto graphic_error;
+ }
+
+ graphic_width = w;
+ graphic_height = h;
+ graphic_depth = depth;
+ }
+ break;
+ case QEMU_OPTION_monitor:
+ pstrcpy(monitor_device, sizeof(monitor_device), optarg);
+ break;
+ case QEMU_OPTION_serial:
+ if (serial_device_index >= MAX_SERIAL_PORTS) {
+ fprintf(stderr, "qemu: too many serial ports\n");
+ exit(1);
+ }
+ pstrcpy(serial_devices[serial_device_index],
+ sizeof(serial_devices[0]), optarg);
+ serial_device_index++;
+ break;
+ case QEMU_OPTION_parallel:
+ if (parallel_device_index >= MAX_PARALLEL_PORTS) {
+ fprintf(stderr, "qemu: too many parallel ports\n");
+ exit(1);
+ }
+ pstrcpy(parallel_devices[parallel_device_index],
+ sizeof(parallel_devices[0]), optarg);
+ parallel_device_index++;
+ break;
+ case QEMU_OPTION_loadvm:
+ loadvm = optarg;
+ break;
+ case QEMU_OPTION_full_screen:
+ full_screen = 1;
+ break;
+ case QEMU_OPTION_pidfile:
+ create_pidfile(optarg);
+ break;
+#ifdef TARGET_I386
+ case QEMU_OPTION_win2k_hack:
+ win2k_install_hack = 1;
+ break;
+#endif
+#ifdef USE_KQEMU
+ case QEMU_OPTION_no_kqemu:
+ kqemu_allowed = 0;
+ break;
+ case QEMU_OPTION_kernel_kqemu:
+ kqemu_allowed = 2;
+ break;
+#endif
+ case QEMU_OPTION_usb:
+ usb_enabled = 1;
+ break;
+ case QEMU_OPTION_usbdevice:
+ usb_enabled = 1;
+ if (usb_devices_index >= MAX_USB_CMDLINE) {
+ fprintf(stderr, "Too many USB devices\n");
+ exit(1);
+ }
+ pstrcpy(usb_devices[usb_devices_index],
+ sizeof(usb_devices[usb_devices_index]),
+ optarg);
+ usb_devices_index++;
+ break;
+ case QEMU_OPTION_smp:
+ smp_cpus = atoi(optarg);
+ if (smp_cpus < 1 || smp_cpus > MAX_CPUS) {
+ fprintf(stderr, "Invalid number of CPUs\n");
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_vnc:
+ vnc_display = atoi(optarg);
+ if (vnc_display < 0) {
+ fprintf(stderr, "Invalid VNC display\n");
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_no_acpi:
+ acpi_enabled = 0;
+ break;
+ }
+ }
+ }
+
+#ifdef USE_KQEMU
+ if (smp_cpus > 1)
+ kqemu_allowed = 0;
+#endif
+ linux_boot = (kernel_filename != NULL);
+
+ if (!linux_boot &&
+ hd_filename[0] == '\0' &&
+ (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') &&
+ fd_filename[0] == '\0')
+ help();
+
+ /* boot to cd by default if no hard disk */
+ if (hd_filename[0] == '\0' && boot_device == 'c') {
+ if (fd_filename[0] != '\0')
+ boot_device = 'a';
+ else
+ boot_device = 'd';
+ }
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ init_timers();
+ init_timer_alarm();
+
+#ifdef _WIN32
+ socket_init();
+#endif
+
+ /* init network clients */
+ if (nb_net_clients == 0) {
+ /* if no clients, we use a default config */
+ pstrcpy(net_clients[0], sizeof(net_clients[0]),
+ "nic");
+ pstrcpy(net_clients[1], sizeof(net_clients[0]),
+ "user");
+ nb_net_clients = 2;
+ }
+
+ for(i = 0;i < nb_net_clients; i++) {
+ if (net_client_init(net_clients[i]) < 0)
+ exit(1);
+ }
+
+ /* init the memory */
+ phys_ram_size = ram_size + vga_ram_size + bios_size;
+
+ phys_ram_base = qemu_vmalloc(phys_ram_size);
+ if (!phys_ram_base) {
+ fprintf(stderr, "Could not allocate physical memory\n");
+ exit(1);
+ }
+
+ /* we always create the cdrom drive, even if no disk is there */
+ bdrv_init();
+ if (cdrom_index >= 0) {
+ bs_table[cdrom_index] = bdrv_new("cdrom");
+ bdrv_set_type_hint(bs_table[cdrom_index], BDRV_TYPE_CDROM);
+ }
+
+ /* open the virtual block devices */
+ for(i = 0; i < MAX_DISKS; i++) {
+ if (hd_filename[i]) {
+ if (!bs_table[i]) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "hd%c", i + 'a');
+ bs_table[i] = bdrv_new(buf);
+ }
+ if (bdrv_open(bs_table[i], hd_filename[i], snapshot) < 0) {
+ fprintf(stderr, "qemu: could not open hard disk image '%s'\n",
+ hd_filename[i]);
+ exit(1);
+ }
+ if (i == 0 && cyls != 0) {
+ bdrv_set_geometry_hint(bs_table[i], cyls, heads, secs);
+ bdrv_set_translation_hint(bs_table[i], translation);
+ }
+ }
+ }
+
+ /* we always create at least one floppy disk */
+ fd_table[0] = bdrv_new("fda");
+ bdrv_set_type_hint(fd_table[0], BDRV_TYPE_FLOPPY);
+
+ for(i = 0; i < MAX_FD; i++) {
+ if (fd_filename[i]) {
+ if (!fd_table[i]) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "fd%c", i + 'a');
+ fd_table[i] = bdrv_new(buf);
+ bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY);
+ }
+ if (fd_filename[i] != '\0') {
+ if (bdrv_open(fd_table[i], fd_filename[i], snapshot) < 0) {
+ fprintf(stderr, "qemu: could not open floppy disk image '%s'\n",
+ fd_filename[i]);
+ exit(1);
+ }
+ }
+ }
+ }
+
+ register_savevm("timer", 0, 1, timer_save, timer_load, NULL);
+ register_savevm("ram", 0, 1, ram_save, ram_load, NULL);
+
+ init_ioports();
+
+ /* terminal init */
+ if (nographic) {
+ dumb_display_init(ds);
+ } else if (vnc_display != -1) {
+ vnc_display_init(ds, vnc_display);
+ } else {
+#if defined(CONFIG_SDL)
+ sdl_display_init(ds, full_screen);
+#elif defined(CONFIG_COCOA)
+ cocoa_display_init(ds, full_screen);
+#else
+ dumb_display_init(ds);
+#endif
+ }
+
+ monitor_hd = qemu_chr_open(monitor_device);
+ if (!monitor_hd) {
+ fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
+ exit(1);
+ }
+ monitor_init(monitor_hd, !nographic);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_devices[i][0] != '\0') {
+ serial_hds[i] = qemu_chr_open(serial_devices[i]);
+ if (!serial_hds[i]) {
+ fprintf(stderr, "qemu: could not open serial device '%s'\n",
+ serial_devices[i]);
+ exit(1);
+ }
+ if (!strcmp(serial_devices[i], "vc"))
+ qemu_chr_printf(serial_hds[i], "serial%d console\r\n", i);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_devices[i][0] != '\0') {
+ parallel_hds[i] = qemu_chr_open(parallel_devices[i]);
+ if (!parallel_hds[i]) {
+ fprintf(stderr, "qemu: could not open parallel device '%s'\n",
+ parallel_devices[i]);
+ exit(1);
+ }
+ if (!strcmp(parallel_devices[i], "vc"))
+ qemu_chr_printf(parallel_hds[i], "parallel%d console\r\n", i);
+ }
+ }
+
+ machine->init(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline, initrd_filename);
+
+ /* init USB devices */
+ if (usb_enabled) {
+ for(i = 0; i < usb_devices_index; i++) {
+ if (usb_device_add(usb_devices[i]) < 0) {
+ fprintf(stderr, "Warning: could not add USB device %s\n",
+ usb_devices[i]);
+ }
+ }
+ }
+
+ gui_timer = qemu_new_timer(rt_clock, gui_update, NULL);
+ qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock));
+
+#ifdef CONFIG_GDBSTUB
+ if (use_gdbstub) {
+ if (gdbserver_start(gdbstub_port) < 0) {
+ fprintf(stderr, "Could not open gdbserver socket on port %d\n",
+ gdbstub_port);
+ exit(1);
+ } else {
+ printf("Waiting gdb connection on port %d\n", gdbstub_port);
+ }
+ } else
+#endif
+ if (loadvm)
+ qemu_loadvm(loadvm);
+
+ {
+ /* XXX: simplify init */
+ read_passwords();
+ if (start_emulation) {
+ vm_start();
+ }
+ }
+ main_loop();
+ quit_timers();
+ return 0;
+}
diff --git a/vl.h b/vl.h
new file mode 100644
index 0000000..d97f485
--- /dev/null
+++ b/vl.h
@@ -0,0 +1,1180 @@
+/*
+ * QEMU System Emulator header
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef VL_H
+#define VL_H
+
+/* we put basic includes here to avoid repeating them in device drivers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#define fsync _commit
+#define lseek _lseeki64
+#define ENOTSUP 4096
+extern int qemu_ftruncate64(int, int64_t);
+#define ftruncate qemu_ftruncate64
+
+
+static inline char *realpath(const char *path, char *resolved_path)
+{
+ _fullpath(resolved_path, path, _MAX_PATH);
+ return resolved_path;
+}
+
+#define PRId64 "I64d"
+#define PRIx64 "I64x"
+#define PRIu64 "I64u"
+#define PRIo64 "I64o"
+#endif
+
+#ifdef QEMU_TOOL
+
+/* we use QEMU_TOOL in the command line tools which do not depend on
+ the target CPU type */
+#include "config-host.h"
+#include <setjmp.h>
+#include "osdep.h"
+#include "bswap.h"
+
+#else
+
+#include "audio/audio.h"
+#include "cpu.h"
+#include "gdbstub.h"
+
+#endif /* !defined(QEMU_TOOL) */
+
+#ifndef glue
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/* vl.c */
+uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
+
+void hw_error(const char *fmt, ...);
+
+extern const char *bios_dir;
+
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+int strstart(const char *str, const char *val, const char **ptr);
+
+extern int vm_running;
+
+typedef struct vm_change_state_entry VMChangeStateEntry;
+typedef void VMChangeStateHandler(void *opaque, int running);
+typedef void VMStopHandler(void *opaque, int reason);
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+ void *opaque);
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e);
+
+int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque);
+void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque);
+
+void vm_start(void);
+void vm_stop(int reason);
+
+typedef void QEMUResetHandler(void *opaque);
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque);
+void qemu_system_reset_request(void);
+void qemu_system_shutdown_request(void);
+void qemu_system_powerdown_request(void);
+#if !defined(TARGET_SPARC)
+// Please implement a power failure function to signal the OS
+#define qemu_system_powerdown() do{}while(0)
+#else
+void qemu_system_powerdown(void);
+#endif
+
+void main_loop_wait(int timeout);
+
+extern int ram_size;
+extern int bios_size;
+extern int rtc_utc;
+extern int cirrus_vga_enabled;
+extern int graphic_width;
+extern int graphic_height;
+extern int graphic_depth;
+extern const char *keyboard_layout;
+extern int kqemu_allowed;
+extern int win2k_install_hack;
+extern int usb_enabled;
+extern int smp_cpus;
+
+/* XXX: make it dynamic */
+#if defined (TARGET_PPC) || defined (TARGET_SPARC64)
+#define BIOS_SIZE ((512 + 32) * 1024)
+#elif defined(TARGET_MIPS)
+#define BIOS_SIZE (128 * 1024)
+#else
+#define BIOS_SIZE ((256 + 64) * 1024)
+#endif
+
+/* keyboard/mouse support */
+
+#define MOUSE_EVENT_LBUTTON 0x01
+#define MOUSE_EVENT_RBUTTON 0x02
+#define MOUSE_EVENT_MBUTTON 0x04
+
+typedef void QEMUPutKBDEvent(void *opaque, int keycode);
+typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state);
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque);
+void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute);
+
+void kbd_put_keycode(int keycode);
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state);
+int kbd_mouse_is_absolute(void);
+
+/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
+ constants) */
+#define QEMU_KEY_ESC1(c) ((c) | 0xe100)
+#define QEMU_KEY_BACKSPACE 0x007f
+#define QEMU_KEY_UP QEMU_KEY_ESC1('A')
+#define QEMU_KEY_DOWN QEMU_KEY_ESC1('B')
+#define QEMU_KEY_RIGHT QEMU_KEY_ESC1('C')
+#define QEMU_KEY_LEFT QEMU_KEY_ESC1('D')
+#define QEMU_KEY_HOME QEMU_KEY_ESC1(1)
+#define QEMU_KEY_END QEMU_KEY_ESC1(4)
+#define QEMU_KEY_PAGEUP QEMU_KEY_ESC1(5)
+#define QEMU_KEY_PAGEDOWN QEMU_KEY_ESC1(6)
+#define QEMU_KEY_DELETE QEMU_KEY_ESC1(3)
+
+#define QEMU_KEY_CTRL_UP 0xe400
+#define QEMU_KEY_CTRL_DOWN 0xe401
+#define QEMU_KEY_CTRL_LEFT 0xe402
+#define QEMU_KEY_CTRL_RIGHT 0xe403
+#define QEMU_KEY_CTRL_HOME 0xe404
+#define QEMU_KEY_CTRL_END 0xe405
+#define QEMU_KEY_CTRL_PAGEUP 0xe406
+#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
+
+void kbd_put_keysym(int keysym);
+
+/* async I/O support */
+
+typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
+typedef int IOCanRWHandler(void *opaque);
+typedef void IOHandler(void *opaque);
+
+int qemu_set_fd_handler2(int fd,
+ IOCanRWHandler *fd_read_poll,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque);
+int qemu_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque);
+
+/* Polling handling */
+
+/* return TRUE if no sleep should be done afterwards */
+typedef int PollingFunc(void *opaque);
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque);
+void qemu_del_polling_cb(PollingFunc *func, void *opaque);
+
+#ifdef _WIN32
+/* Wait objects handling */
+typedef void WaitObjectFunc(void *opaque);
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+#endif
+
+/* character device */
+
+#define CHR_EVENT_BREAK 0 /* serial break char */
+#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */
+
+
+
+#define CHR_IOCTL_SERIAL_SET_PARAMS 1
+typedef struct {
+ int speed;
+ int parity;
+ int data_bits;
+ int stop_bits;
+} QEMUSerialSetParams;
+
+#define CHR_IOCTL_SERIAL_SET_BREAK 2
+
+#define CHR_IOCTL_PP_READ_DATA 3
+#define CHR_IOCTL_PP_WRITE_DATA 4
+#define CHR_IOCTL_PP_READ_CONTROL 5
+#define CHR_IOCTL_PP_WRITE_CONTROL 6
+#define CHR_IOCTL_PP_READ_STATUS 7
+
+typedef void IOEventHandler(void *opaque, int event);
+
+typedef struct CharDriverState {
+ int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+ void (*chr_add_read_handler)(struct CharDriverState *s,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque);
+ int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
+ IOEventHandler *chr_event;
+ void (*chr_send_event)(struct CharDriverState *chr, int event);
+ void (*chr_close)(struct CharDriverState *chr);
+ void *opaque;
+} CharDriverState;
+
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
+void qemu_chr_send_event(CharDriverState *s, int event);
+void qemu_chr_add_read_handler(CharDriverState *s,
+ IOCanRWHandler *fd_can_read,
+ IOReadHandler *fd_read, void *opaque);
+void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event);
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg);
+
+/* consoles */
+
+typedef struct DisplayState DisplayState;
+typedef struct TextConsole TextConsole;
+
+typedef void (*vga_hw_update_ptr)(void *);
+typedef void (*vga_hw_invalidate_ptr)(void *);
+typedef void (*vga_hw_screen_dump_ptr)(void *, const char *);
+
+TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
+ vga_hw_invalidate_ptr invalidate,
+ vga_hw_screen_dump_ptr screen_dump,
+ void *opaque);
+void vga_hw_update(void);
+void vga_hw_invalidate(void);
+void vga_hw_screen_dump(const char *filename);
+
+int is_graphic_console(void);
+CharDriverState *text_console_init(DisplayState *ds);
+void console_select(unsigned int index);
+
+/* serial ports */
+
+#define MAX_SERIAL_PORTS 4
+
+extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+
+/* parallel ports */
+
+#define MAX_PARALLEL_PORTS 3
+
+extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+
+/* VLANs support */
+
+typedef struct VLANClientState VLANClientState;
+
+struct VLANClientState {
+ IOReadHandler *fd_read;
+ /* Packets may still be sent if this returns zero. It's used to
+ rate-limit the slirp code. */
+ IOCanRWHandler *fd_can_read;
+ void *opaque;
+ struct VLANClientState *next;
+ struct VLANState *vlan;
+ char info_str[256];
+};
+
+typedef struct VLANState {
+ int id;
+ VLANClientState *first_client;
+ struct VLANState *next;
+} VLANState;
+
+VLANState *qemu_find_vlan(int id);
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+ IOReadHandler *fd_read,
+ IOCanRWHandler *fd_can_read,
+ void *opaque);
+int qemu_can_send_packet(VLANClientState *vc);
+void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+void qemu_handler_true(void *opaque);
+
+void do_info_network(void);
+
+/* TAP win32 */
+int tap_win32_init(VLANState *vlan, const char *ifname);
+void tap_win32_poll(void);
+
+/* NIC info */
+
+#define MAX_NICS 8
+
+typedef struct NICInfo {
+ uint8_t macaddr[6];
+ const char *model;
+ VLANState *vlan;
+} NICInfo;
+
+extern int nb_nics;
+extern NICInfo nd_table[MAX_NICS];
+
+/* timers */
+
+typedef struct QEMUClock QEMUClock;
+typedef struct QEMUTimer QEMUTimer;
+typedef void QEMUTimerCB(void *opaque);
+
+/* The real time clock should be used only for stuff which does not
+ change the virtual machine state, as it is run even if the virtual
+ machine is stopped. The real time clock has a frequency of 1000
+ Hz. */
+extern QEMUClock *rt_clock;
+
+/* The virtual clock is only run during the emulation. It is stopped
+ when the virtual machine is stopped. Virtual timers use a high
+ precision clock, usually cpu cycles (use ticks_per_sec). */
+extern QEMUClock *vm_clock;
+
+int64_t qemu_get_clock(QEMUClock *clock);
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque);
+void qemu_free_timer(QEMUTimer *ts);
+void qemu_del_timer(QEMUTimer *ts);
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
+int qemu_timer_pending(QEMUTimer *ts);
+
+extern int64_t ticks_per_sec;
+extern int pit_min_timer_count;
+
+int64_t cpu_get_ticks(void);
+void cpu_enable_ticks(void);
+void cpu_disable_ticks(void);
+
+/* VM Load/Save */
+
+typedef FILE QEMUFile;
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
+void qemu_put_byte(QEMUFile *f, int v);
+void qemu_put_be16(QEMUFile *f, unsigned int v);
+void qemu_put_be32(QEMUFile *f, unsigned int v);
+void qemu_put_be64(QEMUFile *f, uint64_t v);
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+int qemu_get_byte(QEMUFile *f);
+unsigned int qemu_get_be16(QEMUFile *f);
+unsigned int qemu_get_be32(QEMUFile *f);
+uint64_t qemu_get_be64(QEMUFile *f);
+
+static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
+{
+ qemu_put_be64(f, *pv);
+}
+
+static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv)
+{
+ qemu_put_be32(f, *pv);
+}
+
+static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv)
+{
+ qemu_put_be16(f, *pv);
+}
+
+static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv)
+{
+ qemu_put_byte(f, *pv);
+}
+
+static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv)
+{
+ *pv = qemu_get_be64(f);
+}
+
+static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv)
+{
+ *pv = qemu_get_be32(f);
+}
+
+static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv)
+{
+ *pv = qemu_get_be16(f);
+}
+
+static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv)
+{
+ *pv = qemu_get_byte(f);
+}
+
+#if TARGET_LONG_BITS == 64
+#define qemu_put_betl qemu_put_be64
+#define qemu_get_betl qemu_get_be64
+#define qemu_put_betls qemu_put_be64s
+#define qemu_get_betls qemu_get_be64s
+#else
+#define qemu_put_betl qemu_put_be32
+#define qemu_get_betl qemu_get_be32
+#define qemu_put_betls qemu_put_be32s
+#define qemu_get_betls qemu_get_be32s
+#endif
+
+int64_t qemu_ftell(QEMUFile *f);
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+int qemu_loadvm(const char *filename);
+int qemu_savevm(const char *filename);
+int register_savevm(const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts);
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts);
+
+void cpu_save(QEMUFile *f, void *opaque);
+int cpu_load(QEMUFile *f, void *opaque, int version_id);
+
+/* block.c */
+typedef struct BlockDriverState BlockDriverState;
+typedef struct BlockDriver BlockDriver;
+
+extern BlockDriver bdrv_raw;
+extern BlockDriver bdrv_cow;
+extern BlockDriver bdrv_qcow;
+extern BlockDriver bdrv_vmdk;
+extern BlockDriver bdrv_cloop;
+extern BlockDriver bdrv_dmg;
+extern BlockDriver bdrv_bochs;
+extern BlockDriver bdrv_vpc;
+extern BlockDriver bdrv_vvfat;
+
+void bdrv_init(void);
+BlockDriver *bdrv_find_format(const char *format_name);
+int bdrv_create(BlockDriver *drv,
+ const char *filename, int64_t size_in_sectors,
+ const char *backing_file, int flags);
+BlockDriverState *bdrv_new(const char *device_name);
+void bdrv_delete(BlockDriverState *bs);
+int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot);
+int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot,
+ BlockDriver *drv);
+void bdrv_close(BlockDriverState *bs);
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors);
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr);
+int bdrv_commit(BlockDriverState *bs);
+void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size);
+/* Ensure contents are flushed to disk. */
+void bdrv_flush(BlockDriverState *bs);
+
+#define BDRV_TYPE_HD 0
+#define BDRV_TYPE_CDROM 1
+#define BDRV_TYPE_FLOPPY 2
+#define BIOS_ATA_TRANSLATION_AUTO 0
+#define BIOS_ATA_TRANSLATION_NONE 1
+#define BIOS_ATA_TRANSLATION_LBA 2
+
+void bdrv_set_geometry_hint(BlockDriverState *bs,
+ int cyls, int heads, int secs);
+void bdrv_set_type_hint(BlockDriverState *bs, int type);
+void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+ int *pcyls, int *pheads, int *psecs);
+int bdrv_get_type_hint(BlockDriverState *bs);
+int bdrv_get_translation_hint(BlockDriverState *bs);
+int bdrv_is_removable(BlockDriverState *bs);
+int bdrv_is_read_only(BlockDriverState *bs);
+int bdrv_is_inserted(BlockDriverState *bs);
+int bdrv_is_locked(BlockDriverState *bs);
+void bdrv_set_locked(BlockDriverState *bs, int locked);
+void bdrv_set_change_cb(BlockDriverState *bs,
+ void (*change_cb)(void *opaque), void *opaque);
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
+void bdrv_info(void);
+BlockDriverState *bdrv_find(const char *name);
+void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque);
+int bdrv_is_encrypted(BlockDriverState *bs);
+int bdrv_set_key(BlockDriverState *bs, const char *key);
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+ void *opaque);
+const char *bdrv_get_device_name(BlockDriverState *bs);
+
+int qcow_get_cluster_size(BlockDriverState *bs);
+int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf);
+
+#ifndef QEMU_TOOL
+
+typedef void QEMUMachineInitFunc(int ram_size, int vga_ram_size,
+ int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename);
+
+typedef struct QEMUMachine {
+ const char *name;
+ const char *desc;
+ QEMUMachineInitFunc *init;
+ struct QEMUMachine *next;
+} QEMUMachine;
+
+int qemu_register_machine(QEMUMachine *m);
+
+typedef void SetIRQFunc(void *opaque, int irq_num, int level);
+typedef void IRQRequestFunc(void *opaque, int level);
+
+/* ISA bus */
+
+extern target_phys_addr_t isa_mem_base;
+
+typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
+typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
+
+int register_ioport_read(int start, int length, int size,
+ IOPortReadFunc *func, void *opaque);
+int register_ioport_write(int start, int length, int size,
+ IOPortWriteFunc *func, void *opaque);
+void isa_unassign_ioport(int start, int length);
+
+/* PCI bus */
+
+extern target_phys_addr_t pci_mem_base;
+
+typedef struct PCIBus PCIBus;
+typedef struct PCIDevice PCIDevice;
+
+typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
+ uint32_t address, uint32_t data, int len);
+typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
+ uint32_t address, int len);
+typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type);
+
+#define PCI_ADDRESS_SPACE_MEM 0x00
+#define PCI_ADDRESS_SPACE_IO 0x01
+#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08
+
+typedef struct PCIIORegion {
+ uint32_t addr; /* current PCI mapping address. -1 means not mapped */
+ uint32_t size;
+ uint8_t type;
+ PCIMapIORegionFunc *map_func;
+} PCIIORegion;
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+#define PCI_DEVICES_MAX 64
+
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+struct PCIDevice {
+ /* PCI config space */
+ uint8_t config[256];
+
+ /* the following fields are read only */
+ PCIBus *bus;
+ int devfn;
+ char name[64];
+ PCIIORegion io_regions[PCI_NUM_REGIONS];
+
+ /* do not access the following fields */
+ PCIConfigReadFunc *config_read;
+ PCIConfigWriteFunc *config_write;
+ /* ??? This is a PC-specific hack, and should be removed. */
+ int irq_index;
+};
+
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write);
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+ uint32_t size, int type,
+ PCIMapIORegionFunc *map_func);
+
+void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level);
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len);
+void pci_default_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len);
+void generic_pci_save(QEMUFile* f, void *opaque);
+int generic_pci_load(QEMUFile* f, void *opaque, int version_id);
+
+typedef void (*pci_set_irq_fn)(PCIDevice *pci_dev, void *pic,
+ int irq_num, int level);
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, void *pic, int devfn_min);
+
+void pci_nic_init(PCIBus *bus, NICInfo *nd);
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len);
+int pci_bus_num(PCIBus *s);
+void pci_for_each_device(void (*fn)(PCIDevice *d));
+
+void pci_info(void);
+
+/* prep_pci.c */
+PCIBus *pci_prep_init(void);
+
+/* grackle_pci.c */
+PCIBus *pci_grackle_init(uint32_t base, void *pic);
+
+/* unin_pci.c */
+PCIBus *pci_pmac_init(void *pic);
+
+/* apb_pci.c */
+PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base,
+ void *pic);
+
+PCIBus *pci_vpb_init(void *pic);
+
+/* piix_pci.c */
+PCIBus *i440fx_init(void);
+int piix3_init(PCIBus *bus);
+void pci_bios_init(void);
+
+/* openpic.c */
+typedef struct openpic_t openpic_t;
+void openpic_set_irq(void *opaque, int n_IRQ, int level);
+openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
+ CPUState **envp);
+
+/* heathrow_pic.c */
+typedef struct HeathrowPICS HeathrowPICS;
+void heathrow_pic_set_irq(void *opaque, int num, int level);
+HeathrowPICS *heathrow_pic_init(int *pmem_index);
+
+#ifdef HAS_AUDIO
+struct soundhw {
+ const char *name;
+ const char *descr;
+ int enabled;
+ int isa;
+ union {
+ int (*init_isa) (AudioState *s);
+ int (*init_pci) (PCIBus *bus, AudioState *s);
+ } init;
+};
+
+extern struct soundhw soundhw[];
+#endif
+
+/* vga.c */
+
+#define VGA_RAM_SIZE (8192 * 1024)
+
+struct DisplayState {
+ uint8_t *data;
+ int linesize;
+ int depth;
+ int bgr; /* BGR color order instead of RGB. Only valid for depth == 32 */
+ int width;
+ int height;
+ void *opaque;
+
+ void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h);
+ void (*dpy_resize)(struct DisplayState *s, int w, int h);
+ void (*dpy_refresh)(struct DisplayState *s);
+ void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y, int dst_x, int dst_y, int w, int h);
+};
+
+static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
+{
+ s->dpy_update(s, x, y, w, h);
+}
+
+static inline void dpy_resize(DisplayState *s, int w, int h)
+{
+ s->dpy_resize(s, w, h);
+}
+
+int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size,
+ unsigned long vga_bios_offset, int vga_bios_size);
+
+/* cirrus_vga.c */
+void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+
+/* sdl.c */
+void sdl_display_init(DisplayState *ds, int full_screen);
+
+/* cocoa.m */
+void cocoa_display_init(DisplayState *ds, int full_screen);
+
+/* vnc.c */
+void vnc_display_init(DisplayState *ds, int display);
+
+/* ide.c */
+#define MAX_DISKS 4
+
+extern BlockDriverState *bs_table[MAX_DISKS];
+
+void isa_ide_init(int iobase, int iobase2, int irq,
+ BlockDriverState *hd0, BlockDriverState *hd1);
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+ int secondary_ide_enabled);
+void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn);
+int pmac_ide_init (BlockDriverState **hd_table,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq);
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+/* es1370.c */
+int es1370_init (PCIBus *bus, AudioState *s);
+
+/* sb16.c */
+int SB16_init (AudioState *s);
+
+/* adlib.c */
+int Adlib_init (AudioState *s);
+
+/* gus.c */
+int GUS_init (AudioState *s);
+
+/* dma.c */
+typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size);
+int DMA_get_channel_mode (int nchan);
+int DMA_read_memory (int nchan, void *buf, int pos, int size);
+int DMA_write_memory (int nchan, void *buf, int pos, int size);
+void DMA_hold_DREQ (int nchan);
+void DMA_release_DREQ (int nchan);
+void DMA_schedule(int nchan);
+void DMA_run (void);
+void DMA_init (int high_page_enable);
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque);
+/* fdc.c */
+#define MAX_FD 2
+extern BlockDriverState *fd_table[MAX_FD];
+
+typedef struct fdctrl_t fdctrl_t;
+
+fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
+ uint32_t io_base,
+ BlockDriverState **fds);
+int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);
+
+/* ne2000.c */
+
+void isa_ne2000_init(int base, int irq, NICInfo *nd);
+void pci_ne2000_init(PCIBus *bus, NICInfo *nd);
+
+/* rtl8139.c */
+
+void pci_rtl8139_init(PCIBus *bus, NICInfo *nd);
+
+/* pcnet.c */
+
+void pci_pcnet_init(PCIBus *bus, NICInfo *nd);
+
+/* pckbd.c */
+
+void kbd_init(void);
+
+/* mc146818rtc.c */
+
+typedef struct RTCState RTCState;
+
+RTCState *rtc_init(int base, int irq);
+void rtc_set_memory(RTCState *s, int addr, int val);
+void rtc_set_date(RTCState *s, const struct tm *tm);
+
+/* serial.c */
+
+typedef struct SerialState SerialState;
+SerialState *serial_init(SetIRQFunc *set_irq, void *opaque,
+ int base, int irq, CharDriverState *chr);
+SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque,
+ target_ulong base, int it_shift,
+ int irq, CharDriverState *chr);
+
+/* parallel.c */
+
+typedef struct ParallelState ParallelState;
+ParallelState *parallel_init(int base, int irq, CharDriverState *chr);
+
+/* i8259.c */
+
+typedef struct PicState2 PicState2;
+extern PicState2 *isa_pic;
+void pic_set_irq(int irq, int level);
+void pic_set_irq_new(void *opaque, int irq, int level);
+PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque);
+void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
+ void *alt_irq_opaque);
+int pic_read_irq(PicState2 *s);
+void pic_update_irq(PicState2 *s);
+uint32_t pic_intack_read(PicState2 *s);
+void pic_info(void);
+void irq_info(void);
+
+/* APIC */
+typedef struct IOAPICState IOAPICState;
+
+int apic_init(CPUState *env);
+int apic_get_interrupt(CPUState *env);
+IOAPICState *ioapic_init(void);
+void ioapic_set_irq(void *opaque, int vector, int level);
+
+/* i8254.c */
+
+#define PIT_FREQ 1193182
+
+typedef struct PITState PITState;
+
+PITState *pit_init(int base, int irq);
+void pit_set_gate(PITState *pit, int channel, int val);
+int pit_get_gate(PITState *pit, int channel);
+int pit_get_initial_count(PITState *pit, int channel);
+int pit_get_mode(PITState *pit, int channel);
+int pit_get_out(PITState *pit, int channel, int64_t current_time);
+
+/* pcspk.c */
+void pcspk_init(PITState *);
+int pcspk_audio_init(AudioState *);
+
+/* acpi.c */
+extern int acpi_enabled;
+void piix4_pm_init(PCIBus *bus, int devfn);
+void acpi_bios_init(void);
+
+/* pc.c */
+extern QEMUMachine pc_machine;
+extern QEMUMachine isapc_machine;
+extern int fd_bootchk;
+
+void ioport_set_a20(int enable);
+int ioport_get_a20(void);
+
+/* ppc.c */
+extern QEMUMachine prep_machine;
+extern QEMUMachine core99_machine;
+extern QEMUMachine heathrow_machine;
+
+/* mips_r4k.c */
+extern QEMUMachine mips_machine;
+
+/* shix.c */
+extern QEMUMachine shix_machine;
+
+#ifdef TARGET_PPC
+ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq);
+#endif
+void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val);
+
+extern CPUWriteMemoryFunc *PPC_io_write[];
+extern CPUReadMemoryFunc *PPC_io_read[];
+void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val);
+
+/* sun4m.c */
+extern QEMUMachine sun4m_machine;
+uint32_t iommu_translate(uint32_t addr);
+void pic_set_irq_cpu(int irq, int level, unsigned int cpu);
+
+/* iommu.c */
+void *iommu_init(uint32_t addr);
+uint32_t iommu_translate_local(void *opaque, uint32_t addr);
+
+/* lance.c */
+void lance_init(NICInfo *nd, int irq, uint32_t leaddr, uint32_t ledaddr);
+
+/* tcx.c */
+void tcx_init(DisplayState *ds, uint32_t addr, uint8_t *vram_base,
+ unsigned long vram_offset, int vram_size, int width, int height);
+
+/* slavio_intctl.c */
+void *slavio_intctl_init();
+void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env);
+void slavio_pic_info(void *opaque);
+void slavio_irq_info(void *opaque);
+void slavio_pic_set_irq(void *opaque, int irq, int level);
+void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu);
+
+/* loader.c */
+int get_image_size(const char *filename);
+int load_image(const char *filename, uint8_t *addr);
+int load_elf(const char *filename, int64_t virt_to_phys_addend, uint64_t *pentry);
+int load_aout(const char *filename, uint8_t *addr);
+
+/* slavio_timer.c */
+void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu);
+
+/* slavio_serial.c */
+SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2);
+void slavio_serial_ms_kbd_init(int base, int irq);
+
+/* slavio_misc.c */
+void *slavio_misc_init(uint32_t base, int irq);
+void slavio_set_power_fail(void *opaque, int power_failing);
+
+/* esp.c */
+void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr);
+
+/* sun4u.c */
+extern QEMUMachine sun4u_machine;
+
+/* NVRAM helpers */
+#include "hw/m48t59.h"
+
+void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value);
+uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr);
+void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value);
+uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr);
+void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value);
+uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr);
+void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
+ const unsigned char *str, uint32_t max);
+int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max);
+void NVRAM_set_crc (m48t59_t *nvram, uint32_t addr,
+ uint32_t start, uint32_t count);
+int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
+ const unsigned char *arch,
+ uint32_t RAM_size, int boot_device,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth);
+
+/* adb.c */
+
+#define MAX_ADB_DEVICES 16
+
+#define ADB_MAX_OUT_LEN 16
+
+typedef struct ADBDevice ADBDevice;
+
+/* buf = NULL means polling */
+typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+typedef int ADBDeviceReset(ADBDevice *d);
+
+struct ADBDevice {
+ struct ADBBusState *bus;
+ int devaddr;
+ int handler;
+ ADBDeviceRequest *devreq;
+ ADBDeviceReset *devreset;
+ void *opaque;
+};
+
+typedef struct ADBBusState {
+ ADBDevice devices[MAX_ADB_DEVICES];
+ int nb_devices;
+ int poll_index;
+} ADBBusState;
+
+int adb_request(ADBBusState *s, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+int adb_poll(ADBBusState *s, uint8_t *buf_out);
+
+ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
+ ADBDeviceRequest *devreq,
+ ADBDeviceReset *devreset,
+ void *opaque);
+void adb_kbd_init(ADBBusState *bus);
+void adb_mouse_init(ADBBusState *bus);
+
+/* cuda.c */
+
+extern ADBBusState adb_bus;
+int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
+
+#include "hw/usb.h"
+
+/* usb ports of the VM */
+
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+ usb_attachfn attach);
+
+#define VM_USB_HUB_SIZE 8
+
+void do_usb_add(const char *devname);
+void do_usb_del(const char *devname);
+void usb_info(void);
+
+/* scsi-disk.c */
+typedef struct SCSIDevice SCSIDevice;
+typedef void (*scsi_completionfn)(void *, uint32_t, int);
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
+ scsi_completionfn completion,
+ void *opaque);
+void scsi_disk_destroy(SCSIDevice *s);
+
+int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun);
+int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len);
+int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len);
+
+/* lsi53c895a.c */
+void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id);
+void *lsi_scsi_init(PCIBus *bus, int devfn);
+
+/* integratorcp.c */
+extern QEMUMachine integratorcp926_machine;
+extern QEMUMachine integratorcp1026_machine;
+
+/* versatilepb.c */
+extern QEMUMachine versatilepb_machine;
+extern QEMUMachine versatileab_machine;
+
+/* ps2.c */
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
+void ps2_write_mouse(void *, int val);
+void ps2_write_keyboard(void *, int val);
+uint32_t ps2_read_data(void *);
+void ps2_queue(void *, int b);
+void ps2_keyboard_set_translation(void *opaque, int mode);
+
+/* smc91c111.c */
+void smc91c111_init(NICInfo *, uint32_t, void *, int);
+
+/* pl110.c */
+void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int);
+
+/* pl011.c */
+void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr);
+
+/* pl050.c */
+void pl050_init(uint32_t base, void *pic, int irq, int is_mouse);
+
+/* pl080.c */
+void *pl080_init(uint32_t base, void *pic, int irq);
+
+/* pl190.c */
+void *pl190_init(uint32_t base, void *parent, int irq, int fiq);
+
+/* arm-timer.c */
+void sp804_init(uint32_t base, void *pic, int irq);
+void icp_pit_init(uint32_t base, void *pic, int irq);
+
+/* arm_boot.c */
+
+void arm_load_kernel(int ram_size, const char *kernel_filename,
+ const char *kernel_cmdline, const char *initrd_filename,
+ int board_id);
+
+/* sh7750.c */
+struct SH7750State;
+
+struct SH7750State *sh7750_init(CPUState * cpu);
+
+typedef struct {
+ /* The callback will be triggered if any of the designated lines change */
+ uint16_t portamask_trigger;
+ uint16_t portbmask_trigger;
+ /* Return 0 if no action was taken */
+ int (*port_change_cb) (uint16_t porta, uint16_t portb,
+ uint16_t * periph_pdtra,
+ uint16_t * periph_portdira,
+ uint16_t * periph_pdtrb,
+ uint16_t * periph_portdirb);
+} sh7750_io_device;
+
+int sh7750_register_io_device(struct SH7750State *s,
+ sh7750_io_device * device);
+/* tc58128.c */
+int tc58128_init(struct SH7750State *s, char *zone1, char *zone2);
+
+/* NOR flash devices */
+typedef struct pflash_t pflash_t;
+
+pflash_t *pflash_register (target_ulong base, ram_addr_t off,
+ BlockDriverState *bs,
+ target_ulong sector_len, int nb_blocs, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3);
+
+#endif /* defined(QEMU_TOOL) */
+
+/* monitor.c */
+void monitor_init(CharDriverState *hd, int show_banner);
+void term_puts(const char *str);
+void term_vprintf(const char *fmt, va_list ap);
+void term_printf(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+void term_flush(void);
+void term_print_help(void);
+void monitor_readline(const char *prompt, int is_password,
+ char *buf, int buf_size);
+
+/* readline.c */
+typedef void ReadLineFunc(void *opaque, const char *str);
+
+extern int completion_index;
+void add_completion(const char *str);
+void readline_handle_byte(int ch);
+void readline_find_completion(const char *cmdline);
+const char *readline_get_history(unsigned int index);
+void readline_start(const char *prompt, int is_password,
+ ReadLineFunc *readline_func, void *opaque);
+
+void kqemu_record_dump(void);
+
+#endif /* VL_H */
diff --git a/vnc.c b/vnc.c
new file mode 100644
index 0000000..1bea966
--- /dev/null
+++ b/vnc.c
@@ -0,0 +1,1085 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+#include "qemu_socket.h"
+
+#define VNC_REFRESH_INTERVAL (1000 / 30)
+
+#include "vnc_keysym.h"
+#include "keymaps.c"
+
+typedef struct Buffer
+{
+ size_t capacity;
+ size_t offset;
+ char *buffer;
+} Buffer;
+
+typedef struct VncState VncState;
+
+typedef int VncReadEvent(VncState *vs, char *data, size_t len);
+
+typedef void VncWritePixels(VncState *vs, void *data, int size);
+
+typedef void VncSendHextileTile(VncState *vs,
+ int x, int y, int w, int h,
+ uint32_t *last_bg,
+ uint32_t *last_fg,
+ int *has_bg, int *has_fg);
+
+#define VNC_MAX_WIDTH 2048
+#define VNC_MAX_HEIGHT 2048
+#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
+
+struct VncState
+{
+ QEMUTimer *timer;
+ int lsock;
+ int csock;
+ DisplayState *ds;
+ int need_update;
+ int width;
+ int height;
+ uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+ char *old_data;
+ int depth; /* internal VNC frame buffer byte per pixel */
+ int has_resize;
+ int has_hextile;
+ Buffer output;
+ Buffer input;
+ kbd_layout_t *kbd_layout;
+ /* current output mode information */
+ VncWritePixels *write_pixels;
+ VncSendHextileTile *send_hextile_tile;
+ int pix_bpp, pix_big_endian;
+ int red_shift, red_max, red_shift1;
+ int green_shift, green_max, green_shift1;
+ int blue_shift, blue_max, blue_shift1;
+
+ VncReadEvent *read_handler;
+ size_t read_handler_expect;
+};
+
+/* TODO
+ 1) Get the queue working for IO.
+ 2) there is some weirdness when using the -S option (the screen is grey
+ and not totally invalidated
+ 3) resolutions > 1024
+*/
+
+static void vnc_write(VncState *vs, const void *data, size_t len);
+static void vnc_write_u32(VncState *vs, uint32_t value);
+static void vnc_write_s32(VncState *vs, int32_t value);
+static void vnc_write_u16(VncState *vs, uint16_t value);
+static void vnc_write_u8(VncState *vs, uint8_t value);
+static void vnc_flush(VncState *vs);
+static void vnc_update_client(void *opaque);
+static void vnc_client_read(void *opaque);
+
+static inline void vnc_set_bit(uint32_t *d, int k)
+{
+ d[k >> 5] |= 1 << (k & 0x1f);
+}
+
+static inline void vnc_clear_bit(uint32_t *d, int k)
+{
+ d[k >> 5] &= ~(1 << (k & 0x1f));
+}
+
+static inline void vnc_set_bits(uint32_t *d, int n, int nb_words)
+{
+ int j;
+
+ j = 0;
+ while (n >= 32) {
+ d[j++] = -1;
+ n -= 32;
+ }
+ if (n > 0)
+ d[j++] = (1 << n) - 1;
+ while (j < nb_words)
+ d[j++] = 0;
+}
+
+static inline int vnc_get_bit(const uint32_t *d, int k)
+{
+ return (d[k >> 5] >> (k & 0x1f)) & 1;
+}
+
+static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
+ int nb_words)
+{
+ int i;
+ for(i = 0; i < nb_words; i++) {
+ if ((d1[i] & d2[i]) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ VncState *vs = ds->opaque;
+ int i;
+
+ h += y;
+
+ for (; y < h; y++)
+ for (i = 0; i < w; i += 16)
+ vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
+}
+
+static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+ int32_t encoding)
+{
+ vnc_write_u16(vs, x);
+ vnc_write_u16(vs, y);
+ vnc_write_u16(vs, w);
+ vnc_write_u16(vs, h);
+
+ vnc_write_s32(vs, encoding);
+}
+
+static void vnc_dpy_resize(DisplayState *ds, int w, int h)
+{
+ VncState *vs = ds->opaque;
+
+ ds->data = realloc(ds->data, w * h * vs->depth);
+ vs->old_data = realloc(vs->old_data, w * h * vs->depth);
+
+ if (ds->data == NULL || vs->old_data == NULL) {
+ fprintf(stderr, "vnc: memory allocation failed\n");
+ exit(1);
+ }
+
+ ds->depth = vs->depth * 8;
+ ds->width = w;
+ ds->height = h;
+ ds->linesize = w * vs->depth;
+ if (vs->csock != -1 && vs->has_resize) {
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
+ vnc_flush(vs);
+ vs->width = ds->width;
+ vs->height = ds->height;
+ }
+}
+
+/* fastest code */
+static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
+{
+ vnc_write(vs, pixels, size);
+}
+
+/* slowest but generic code. */
+static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
+{
+ unsigned int r, g, b;
+
+ r = (v >> vs->red_shift1) & vs->red_max;
+ g = (v >> vs->green_shift1) & vs->green_max;
+ b = (v >> vs->blue_shift1) & vs->blue_max;
+ v = (r << vs->red_shift) |
+ (g << vs->green_shift) |
+ (b << vs->blue_shift);
+ switch(vs->pix_bpp) {
+ case 1:
+ buf[0] = v;
+ break;
+ case 2:
+ if (vs->pix_big_endian) {
+ buf[0] = v >> 8;
+ buf[1] = v;
+ } else {
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ default:
+ case 4:
+ if (vs->pix_big_endian) {
+ buf[0] = v >> 24;
+ buf[1] = v >> 16;
+ buf[2] = v >> 8;
+ buf[3] = v;
+ } else {
+ buf[3] = v >> 24;
+ buf[2] = v >> 16;
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ }
+}
+
+static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
+{
+ uint32_t *pixels = pixels1;
+ uint8_t buf[4];
+ int n, i;
+
+ n = size >> 2;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->pix_bpp);
+ }
+}
+
+static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h)
+{
+ int i;
+ char *row;
+
+ vnc_framebuffer_update(vs, x, y, w, h, 0);
+
+ row = vs->ds->data + y * vs->ds->linesize + x * vs->depth;
+ for (i = 0; i < h; i++) {
+ vs->write_pixels(vs, row, w * vs->depth);
+ row += vs->ds->linesize;
+ }
+}
+
+static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
+{
+ ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F);
+ ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F);
+}
+
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+
+#define GENERIC
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h)
+{
+ int i, j;
+ int has_fg, has_bg;
+ uint32_t last_fg32, last_bg32;
+
+ vnc_framebuffer_update(vs, x, y, w, h, 5);
+
+ has_fg = has_bg = 0;
+ for (j = y; j < (y + h); j += 16) {
+ for (i = x; i < (x + w); i += 16) {
+ vs->send_hextile_tile(vs, i, j,
+ MIN(16, x + w - i), MIN(16, y + h - j),
+ &last_bg32, &last_fg32, &has_bg, &has_fg);
+ }
+ }
+}
+
+static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ if (vs->has_hextile)
+ send_framebuffer_update_hextile(vs, x, y, w, h);
+ else
+ send_framebuffer_update_raw(vs, x, y, w, h);
+}
+
+static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ int src, dst;
+ char *src_row;
+ char *dst_row;
+ char *old_row;
+ int y = 0;
+ int pitch = ds->linesize;
+ VncState *vs = ds->opaque;
+
+ vnc_update_client(vs);
+
+ if (dst_y > src_y) {
+ y = h - 1;
+ pitch = -pitch;
+ }
+
+ src = (ds->linesize * (src_y + y) + vs->depth * src_x);
+ dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x);
+
+ src_row = ds->data + src;
+ dst_row = ds->data + dst;
+ old_row = vs->old_data + dst;
+
+ for (y = 0; y < h; y++) {
+ memmove(old_row, src_row, w * vs->depth);
+ memmove(dst_row, src_row, w * vs->depth);
+ src_row += pitch;
+ dst_row += pitch;
+ old_row += pitch;
+ }
+
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
+ vnc_write_u16(vs, src_x);
+ vnc_write_u16(vs, src_y);
+ vnc_flush(vs);
+}
+
+static int find_dirty_height(VncState *vs, int y, int last_x, int x)
+{
+ int h;
+
+ for (h = 1; h < (vs->height - y); h++) {
+ int tmp_x;
+ if (!vnc_get_bit(vs->dirty_row[y + h], last_x))
+ break;
+ for (tmp_x = last_x; tmp_x < x; tmp_x++)
+ vnc_clear_bit(vs->dirty_row[y + h], tmp_x);
+ }
+
+ return h;
+}
+
+static void vnc_update_client(void *opaque)
+{
+ VncState *vs = opaque;
+
+ if (vs->need_update && vs->csock != -1) {
+ int y;
+ char *row;
+ char *old_row;
+ uint32_t width_mask[VNC_DIRTY_WORDS];
+ int n_rectangles;
+ int saved_offset;
+ int has_dirty = 0;
+
+ vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS);
+
+ /* Walk through the dirty map and eliminate tiles that
+ really aren't dirty */
+ row = vs->ds->data;
+ old_row = vs->old_data;
+
+ for (y = 0; y < vs->height; y++) {
+ if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) {
+ int x;
+ char *ptr, *old_ptr;
+
+ ptr = row;
+ old_ptr = old_row;
+
+ for (x = 0; x < vs->ds->width; x += 16) {
+ if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) {
+ vnc_clear_bit(vs->dirty_row[y], (x / 16));
+ } else {
+ has_dirty = 1;
+ memcpy(old_ptr, ptr, 16 * vs->depth);
+ }
+
+ ptr += 16 * vs->depth;
+ old_ptr += 16 * vs->depth;
+ }
+ }
+
+ row += vs->ds->linesize;
+ old_row += vs->ds->linesize;
+ }
+
+ if (!has_dirty) {
+ qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+ return;
+ }
+
+ /* Count rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(vs, 0); /* msg id */
+ vnc_write_u8(vs, 0);
+ saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+
+ for (y = 0; y < vs->height; y++) {
+ int x;
+ int last_x = -1;
+ for (x = 0; x < vs->width / 16; x++) {
+ if (vnc_get_bit(vs->dirty_row[y], x)) {
+ if (last_x == -1) {
+ last_x = x;
+ }
+ vnc_clear_bit(vs->dirty_row[y], x);
+ } else {
+ if (last_x != -1) {
+ int h = find_dirty_height(vs, y, last_x, x);
+ send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+ n_rectangles++;
+ }
+ last_x = -1;
+ }
+ }
+ if (last_x != -1) {
+ int h = find_dirty_height(vs, y, last_x, x);
+ send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+ n_rectangles++;
+ }
+ }
+ vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+ vnc_flush(vs);
+
+ }
+ qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+}
+
+static void vnc_timer_init(VncState *vs)
+{
+ if (vs->timer == NULL) {
+ vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+ qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock));
+ }
+}
+
+static void vnc_dpy_refresh(DisplayState *ds)
+{
+ VncState *vs = ds->opaque;
+ vnc_timer_init(vs);
+ vga_hw_update();
+}
+
+static int vnc_listen_poll(void *opaque)
+{
+ VncState *vs = opaque;
+ if (vs->csock == -1)
+ return 1;
+ return 0;
+}
+
+static void buffer_reserve(Buffer *buffer, size_t len)
+{
+ if ((buffer->capacity - buffer->offset) < len) {
+ buffer->capacity += (len + 1024);
+ buffer->buffer = realloc(buffer->buffer, buffer->capacity);
+ if (buffer->buffer == NULL) {
+ fprintf(stderr, "vnc: out of memory\n");
+ exit(1);
+ }
+ }
+}
+
+static int buffer_empty(Buffer *buffer)
+{
+ return buffer->offset == 0;
+}
+
+static char *buffer_end(Buffer *buffer)
+{
+ return buffer->buffer + buffer->offset;
+}
+
+static void buffer_reset(Buffer *buffer)
+{
+ buffer->offset = 0;
+}
+
+static void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+ memcpy(buffer->buffer + buffer->offset, data, len);
+ buffer->offset += len;
+}
+
+static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+{
+ if (ret == 0 || ret == -1) {
+ if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN))
+ return 0;
+
+ qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+ closesocket(vs->csock);
+ vs->csock = -1;
+ buffer_reset(&vs->input);
+ buffer_reset(&vs->output);
+ vs->need_update = 0;
+ return 0;
+ }
+ return ret;
+}
+
+static void vnc_client_error(VncState *vs)
+{
+ vnc_client_io_error(vs, -1, EINVAL);
+}
+
+static void vnc_client_write(void *opaque)
+{
+ long ret;
+ VncState *vs = opaque;
+
+ ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
+ ret = vnc_client_io_error(vs, ret, socket_error());
+ if (!ret)
+ return;
+
+ memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
+ vs->output.offset -= ret;
+
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+}
+
+static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+{
+ vs->read_handler = func;
+ vs->read_handler_expect = expecting;
+}
+
+static void vnc_client_read(void *opaque)
+{
+ VncState *vs = opaque;
+ long ret;
+
+ buffer_reserve(&vs->input, 4096);
+
+ ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
+ ret = vnc_client_io_error(vs, ret, socket_error());
+ if (!ret)
+ return;
+
+ vs->input.offset += ret;
+
+ while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+ size_t len = vs->read_handler_expect;
+ int ret;
+
+ ret = vs->read_handler(vs, vs->input.buffer, len);
+ if (vs->csock == -1)
+ return;
+
+ if (!ret) {
+ memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
+ vs->input.offset -= len;
+ } else {
+ vs->read_handler_expect = ret;
+ }
+ }
+}
+
+static void vnc_write(VncState *vs, const void *data, size_t len)
+{
+ buffer_reserve(&vs->output, len);
+
+ if (buffer_empty(&vs->output)) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+ }
+
+ buffer_append(&vs->output, data, len);
+}
+
+static void vnc_write_s32(VncState *vs, int32_t value)
+{
+ vnc_write_u32(vs, *(uint32_t *)&value);
+}
+
+static void vnc_write_u32(VncState *vs, uint32_t value)
+{
+ uint8_t buf[4];
+
+ buf[0] = (value >> 24) & 0xFF;
+ buf[1] = (value >> 16) & 0xFF;
+ buf[2] = (value >> 8) & 0xFF;
+ buf[3] = value & 0xFF;
+
+ vnc_write(vs, buf, 4);
+}
+
+static void vnc_write_u16(VncState *vs, uint16_t value)
+{
+ char buf[2];
+
+ buf[0] = (value >> 8) & 0xFF;
+ buf[1] = value & 0xFF;
+
+ vnc_write(vs, buf, 2);
+}
+
+static void vnc_write_u8(VncState *vs, uint8_t value)
+{
+ vnc_write(vs, (char *)&value, 1);
+}
+
+static void vnc_flush(VncState *vs)
+{
+ if (vs->output.offset)
+ vnc_client_write(vs);
+}
+
+static uint8_t read_u8(char *data, size_t offset)
+{
+ return data[offset];
+}
+
+static uint16_t read_u16(char *data, size_t offset)
+{
+ return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
+}
+
+static int32_t read_s32(char *data, size_t offset)
+{
+ return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static uint32_t read_u32(char *data, size_t offset)
+{
+ return ((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static void client_cut_text(VncState *vs, size_t len, char *text)
+{
+}
+
+static void pointer_event(VncState *vs, int button_mask, int x, int y)
+{
+ int buttons = 0;
+ int dz = 0;
+
+ if (button_mask & 0x01)
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (button_mask & 0x02)
+ buttons |= MOUSE_EVENT_MBUTTON;
+ if (button_mask & 0x04)
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (button_mask & 0x08)
+ dz = -1;
+ if (button_mask & 0x10)
+ dz = 1;
+
+ if (kbd_mouse_is_absolute()) {
+ kbd_mouse_event(x * 0x7FFF / vs->ds->width,
+ y * 0x7FFF / vs->ds->height,
+ dz, buttons);
+ } else {
+ static int last_x = -1;
+ static int last_y = -1;
+
+ if (last_x != -1)
+ kbd_mouse_event(x - last_x, y - last_y, dz, buttons);
+
+ last_x = x;
+ last_y = y;
+ }
+}
+
+static void do_key_event(VncState *vs, int down, uint32_t sym)
+{
+ int keycode;
+
+ keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (down)
+ kbd_put_keycode(keycode & 0x7f);
+ else
+ kbd_put_keycode(keycode | 0x80);
+}
+
+static void key_event(VncState *vs, int down, uint32_t sym)
+{
+ if (sym >= 'A' && sym <= 'Z')
+ sym = sym - 'A' + 'a';
+ do_key_event(vs, down, sym);
+}
+
+static void framebuffer_update_request(VncState *vs, int incremental,
+ int x_position, int y_position,
+ int w, int h)
+{
+ int i;
+ vs->need_update = 1;
+ if (!incremental) {
+ char *old_row = vs->old_data + y_position * vs->ds->linesize;
+
+ for (i = 0; i < h; i++) {
+ vnc_set_bits(vs->dirty_row[y_position + i],
+ (vs->ds->width / 16), VNC_DIRTY_WORDS);
+ memset(old_row, 42, vs->ds->width * vs->depth);
+ old_row += vs->ds->linesize;
+ }
+ }
+}
+
+static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
+{
+ int i;
+
+ vs->has_hextile = 0;
+ vs->has_resize = 0;
+ vs->ds->dpy_copy = NULL;
+
+ for (i = n_encodings - 1; i >= 0; i--) {
+ switch (encodings[i]) {
+ case 0: /* Raw */
+ vs->has_hextile = 0;
+ break;
+ case 1: /* CopyRect */
+ vs->ds->dpy_copy = vnc_copy;
+ break;
+ case 5: /* Hextile */
+ vs->has_hextile = 1;
+ break;
+ case -223: /* DesktopResize */
+ vs->has_resize = 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int compute_nbits(unsigned int val)
+{
+ int n;
+ n = 0;
+ while (val != 0) {
+ n++;
+ val >>= 1;
+ }
+ return n;
+}
+
+static void set_pixel_format(VncState *vs,
+ int bits_per_pixel, int depth,
+ int big_endian_flag, int true_color_flag,
+ int red_max, int green_max, int blue_max,
+ int red_shift, int green_shift, int blue_shift)
+{
+ int host_big_endian_flag;
+
+#ifdef WORDS_BIGENDIAN
+ host_big_endian_flag = 1;
+#else
+ host_big_endian_flag = 0;
+#endif
+ if (!true_color_flag) {
+ fail:
+ vnc_client_error(vs);
+ return;
+ }
+ if (bits_per_pixel == 32 &&
+ host_big_endian_flag == big_endian_flag &&
+ red_max == 0xff && green_max == 0xff && blue_max == 0xff &&
+ red_shift == 16 && green_shift == 8 && blue_shift == 0) {
+ vs->depth = 4;
+ vs->write_pixels = vnc_write_pixels_copy;
+ vs->send_hextile_tile = send_hextile_tile_32;
+ } else
+ if (bits_per_pixel == 16 &&
+ host_big_endian_flag == big_endian_flag &&
+ red_max == 31 && green_max == 63 && blue_max == 31 &&
+ red_shift == 11 && green_shift == 5 && blue_shift == 0) {
+ vs->depth = 2;
+ vs->write_pixels = vnc_write_pixels_copy;
+ vs->send_hextile_tile = send_hextile_tile_16;
+ } else
+ if (bits_per_pixel == 8 &&
+ red_max == 7 && green_max == 7 && blue_max == 3 &&
+ red_shift == 5 && green_shift == 2 && blue_shift == 0) {
+ vs->depth = 1;
+ vs->write_pixels = vnc_write_pixels_copy;
+ vs->send_hextile_tile = send_hextile_tile_8;
+ } else
+ {
+ /* generic and slower case */
+ if (bits_per_pixel != 8 &&
+ bits_per_pixel != 16 &&
+ bits_per_pixel != 32)
+ goto fail;
+ vs->depth = 4;
+ vs->red_shift = red_shift;
+ vs->red_max = red_max;
+ vs->red_shift1 = 24 - compute_nbits(red_max);
+ vs->green_shift = green_shift;
+ vs->green_max = green_max;
+ vs->green_shift1 = 16 - compute_nbits(green_max);
+ vs->blue_shift = blue_shift;
+ vs->blue_max = blue_max;
+ vs->blue_shift1 = 8 - compute_nbits(blue_max);
+ vs->pix_bpp = bits_per_pixel / 8;
+ vs->pix_big_endian = big_endian_flag;
+ vs->write_pixels = vnc_write_pixels_generic;
+ vs->send_hextile_tile = send_hextile_tile_generic;
+ }
+
+ vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height);
+ memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+ memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height);
+
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static int protocol_client_msg(VncState *vs, char *data, size_t len)
+{
+ int i;
+ uint16_t limit;
+
+ switch (data[0]) {
+ case 0:
+ if (len == 1)
+ return 20;
+
+ set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+ read_u8(data, 6), read_u8(data, 7),
+ read_u16(data, 8), read_u16(data, 10),
+ read_u16(data, 12), read_u8(data, 14),
+ read_u8(data, 15), read_u8(data, 16));
+ break;
+ case 2:
+ if (len == 1)
+ return 4;
+
+ if (len == 4)
+ return 4 + (read_u16(data, 2) * 4);
+
+ limit = read_u16(data, 2);
+ for (i = 0; i < limit; i++) {
+ int32_t val = read_s32(data, 4 + (i * 4));
+ memcpy(data + 4 + (i * 4), &val, sizeof(val));
+ }
+
+ set_encodings(vs, (int32_t *)(data + 4), limit);
+ break;
+ case 3:
+ if (len == 1)
+ return 10;
+
+ framebuffer_update_request(vs,
+ read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
+ read_u16(data, 6), read_u16(data, 8));
+ break;
+ case 4:
+ if (len == 1)
+ return 8;
+
+ key_event(vs, read_u8(data, 1), read_u32(data, 4));
+ break;
+ case 5:
+ if (len == 1)
+ return 6;
+
+ pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
+ break;
+ case 6:
+ if (len == 1)
+ return 8;
+
+ if (len == 8)
+ return 8 + read_u32(data, 4);
+
+ client_cut_text(vs, read_u32(data, 4), data + 8);
+ break;
+ default:
+ printf("Msg: %d\n", data[0]);
+ vnc_client_error(vs);
+ break;
+ }
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+ return 0;
+}
+
+static int protocol_client_init(VncState *vs, char *data, size_t len)
+{
+ char pad[3] = { 0, 0, 0 };
+
+ vs->width = vs->ds->width;
+ vs->height = vs->ds->height;
+ vnc_write_u16(vs, vs->ds->width);
+ vnc_write_u16(vs, vs->ds->height);
+
+ vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */
+ vnc_write_u8(vs, vs->depth * 8); /* depth */
+#ifdef WORDS_BIGENDIAN
+ vnc_write_u8(vs, 1); /* big-endian-flag */
+#else
+ vnc_write_u8(vs, 0); /* big-endian-flag */
+#endif
+ vnc_write_u8(vs, 1); /* true-color-flag */
+ if (vs->depth == 4) {
+ vnc_write_u16(vs, 0xFF); /* red-max */
+ vnc_write_u16(vs, 0xFF); /* green-max */
+ vnc_write_u16(vs, 0xFF); /* blue-max */
+ vnc_write_u8(vs, 16); /* red-shift */
+ vnc_write_u8(vs, 8); /* green-shift */
+ vnc_write_u8(vs, 0); /* blue-shift */
+ vs->send_hextile_tile = send_hextile_tile_32;
+ } else if (vs->depth == 2) {
+ vnc_write_u16(vs, 31); /* red-max */
+ vnc_write_u16(vs, 63); /* green-max */
+ vnc_write_u16(vs, 31); /* blue-max */
+ vnc_write_u8(vs, 11); /* red-shift */
+ vnc_write_u8(vs, 5); /* green-shift */
+ vnc_write_u8(vs, 0); /* blue-shift */
+ vs->send_hextile_tile = send_hextile_tile_16;
+ } else if (vs->depth == 1) {
+ /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */
+ vnc_write_u16(vs, 7); /* red-max */
+ vnc_write_u16(vs, 7); /* green-max */
+ vnc_write_u16(vs, 3); /* blue-max */
+ vnc_write_u8(vs, 5); /* red-shift */
+ vnc_write_u8(vs, 2); /* green-shift */
+ vnc_write_u8(vs, 0); /* blue-shift */
+ vs->send_hextile_tile = send_hextile_tile_8;
+ }
+ vs->write_pixels = vnc_write_pixels_copy;
+
+ vnc_write(vs, pad, 3); /* padding */
+
+ vnc_write_u32(vs, 4);
+ vnc_write(vs, "QEMU", 4);
+ vnc_flush(vs);
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+
+ return 0;
+}
+
+static int protocol_version(VncState *vs, char *version, size_t len)
+{
+ char local[13];
+ int maj, min;
+
+ memcpy(local, version, 12);
+ local[12] = 0;
+
+ if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) {
+ vnc_client_error(vs);
+ return 0;
+ }
+
+ vnc_write_u32(vs, 1); /* None */
+ vnc_flush(vs);
+
+ vnc_read_when(vs, protocol_client_init, 1);
+
+ return 0;
+}
+
+static void vnc_listen_read(void *opaque)
+{
+ VncState *vs = opaque;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+
+ vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (vs->csock != -1) {
+ socket_set_nonblock(vs->csock);
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
+ vnc_write(vs, "RFB 003.003\n", 12);
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_version, 12);
+ memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
+ memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+ vs->has_resize = 0;
+ vs->has_hextile = 0;
+ vs->ds->dpy_copy = NULL;
+ }
+}
+
+void vnc_display_init(DisplayState *ds, int display)
+{
+ struct sockaddr_in addr;
+ int reuse_addr, ret;
+ VncState *vs;
+
+ vs = qemu_mallocz(sizeof(VncState));
+ if (!vs)
+ exit(1);
+
+ ds->opaque = vs;
+
+ vs->lsock = -1;
+ vs->csock = -1;
+ vs->depth = 4;
+
+ vs->ds = ds;
+
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+
+ vs->kbd_layout = init_keyboard_layout(keyboard_layout);
+ if (!vs->kbd_layout)
+ exit(1);
+
+ vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
+ if (vs->lsock == -1) {
+ fprintf(stderr, "Could not create socket\n");
+ exit(1);
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(5900 + display);
+ memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
+
+ reuse_addr = 1;
+ ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&reuse_addr, sizeof(reuse_addr));
+ if (ret == -1) {
+ fprintf(stderr, "setsockopt() failed\n");
+ exit(1);
+ }
+
+ if (bind(vs->lsock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ fprintf(stderr, "bind() failed\n");
+ exit(1);
+ }
+
+ if (listen(vs->lsock, 1) == -1) {
+ fprintf(stderr, "listen() failed\n");
+ exit(1);
+ }
+
+ ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+ if (ret == -1) {
+ exit(1);
+ }
+
+ vs->ds->data = NULL;
+ vs->ds->dpy_update = vnc_dpy_update;
+ vs->ds->dpy_resize = vnc_dpy_resize;
+ vs->ds->dpy_refresh = vnc_dpy_refresh;
+
+ memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+
+ vnc_dpy_resize(vs->ds, 640, 400);
+}
diff --git a/vnc_keysym.h b/vnc_keysym.h
new file mode 100644
index 0000000..a4ac688
--- /dev/null
+++ b/vnc_keysym.h
@@ -0,0 +1,275 @@
+typedef struct {
+ const char* name;
+ int keysym;
+} name2keysym_t;
+static name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", 0x20ac}, /* XK_EuroSign */
+
+ /* modifiers */
+{"Control_L", 0xffe3}, /* XK_Control_L */
+{"Control_R", 0xffe4}, /* XK_Control_R */
+{"Alt_L", 0xffe9}, /* XK_Alt_L */
+{"Alt_R", 0xffea}, /* XK_Alt_R */
+{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */
+{"Meta_L", 0xffe7}, /* XK_Meta_L */
+{"Meta_R", 0xffe8}, /* XK_Meta_R */
+{"Shift_L", 0xffe1}, /* XK_Shift_L */
+{"Shift_R", 0xffe2}, /* XK_Shift_R */
+{"Super_L", 0xffeb}, /* XK_Super_L */
+{"Super_R", 0xffec}, /* XK_Super_R */
+
+ /* special keys */
+{"BackSpace", 0xff08}, /* XK_BackSpace */
+{"Tab", 0xff09}, /* XK_Tab */
+{"Return", 0xff0d}, /* XK_Return */
+{"Right", 0xff53}, /* XK_Right */
+{"Left", 0xff51}, /* XK_Left */
+{"Up", 0xff52}, /* XK_Up */
+{"Down", 0xff54}, /* XK_Down */
+{"Page_Down", 0xff56}, /* XK_Page_Down */
+{"Page_Up", 0xff55}, /* XK_Page_Up */
+{"Insert", 0xff63}, /* XK_Insert */
+{"Delete", 0xffff}, /* XK_Delete */
+{"Home", 0xff50}, /* XK_Home */
+{"End", 0xff57}, /* XK_End */
+{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"F1", 0xffbe}, /* XK_F1 */
+{"F2", 0xffbf}, /* XK_F2 */
+{"F3", 0xffc0}, /* XK_F3 */
+{"F4", 0xffc1}, /* XK_F4 */
+{"F5", 0xffc2}, /* XK_F5 */
+{"F6", 0xffc3}, /* XK_F6 */
+{"F7", 0xffc4}, /* XK_F7 */
+{"F8", 0xffc5}, /* XK_F8 */
+{"F9", 0xffc6}, /* XK_F9 */
+{"F10", 0xffc7}, /* XK_F10 */
+{"F11", 0xffc8}, /* XK_F11 */
+{"F12", 0xffc9}, /* XK_F12 */
+{"F13", 0xffca}, /* XK_F13 */
+{"F14", 0xffcb}, /* XK_F14 */
+{"F15", 0xffcc}, /* XK_F15 */
+{"Sys_Req", 0xff15}, /* XK_Sys_Req */
+{"KP_0", 0xffb0}, /* XK_KP_0 */
+{"KP_1", 0xffb1}, /* XK_KP_1 */
+{"KP_2", 0xffb2}, /* XK_KP_2 */
+{"KP_3", 0xffb3}, /* XK_KP_3 */
+{"KP_4", 0xffb4}, /* XK_KP_4 */
+{"KP_5", 0xffb5}, /* XK_KP_5 */
+{"KP_6", 0xffb6}, /* XK_KP_6 */
+{"KP_7", 0xffb7}, /* XK_KP_7 */
+{"KP_8", 0xffb8}, /* XK_KP_8 */
+{"KP_9", 0xffb9}, /* XK_KP_9 */
+{"KP_Add", 0xffab}, /* XK_KP_Add */
+{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */
+{"KP_Divide", 0xffaf}, /* XK_KP_Divide */
+{"KP_Enter", 0xff8d}, /* XK_KP_Enter */
+{"KP_Equal", 0xffbd}, /* XK_KP_Equal */
+{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */
+{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */
+{"help", 0xff6a}, /* XK_Help */
+{"Menu", 0xff67}, /* XK_Menu */
+{"Print", 0xff61}, /* XK_Print */
+{"Mode_switch", 0xff7e}, /* XK_Mode_switch */
+{"Num_Lock", 0xff7f}, /* XK_Num_Lock */
+{"Pause", 0xff13}, /* XK_Pause */
+{"Escape", 0xff1b}, /* XK_Escape */
+{0,0},
+};
diff --git a/vnchextile.h b/vnchextile.h
new file mode 100644
index 0000000..16a354d
--- /dev/null
+++ b/vnchextile.h
@@ -0,0 +1,209 @@
+#define CONCAT_I(a, b) a ## b
+#define CONCAT(a, b) CONCAT_I(a, b)
+#define pixel_t CONCAT(uint, CONCAT(BPP, _t))
+#ifdef GENERIC
+#define NAME generic
+#else
+#define NAME BPP
+#endif
+
+static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
+ int x, int y, int w, int h,
+ uint32_t *last_bg32,
+ uint32_t *last_fg32,
+ int *has_bg, int *has_fg)
+{
+ char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth);
+ pixel_t *irow = (pixel_t *)row;
+ int j, i;
+ pixel_t *last_bg = (pixel_t *)last_bg32;
+ pixel_t *last_fg = (pixel_t *)last_fg32;
+ pixel_t bg = 0;
+ pixel_t fg = 0;
+ int n_colors = 0;
+ int bg_count = 0;
+ int fg_count = 0;
+ int flags = 0;
+ uint8_t data[(sizeof(pixel_t) + 2) * 16 * 16];
+ int n_data = 0;
+ int n_subtiles = 0;
+
+ for (j = 0; j < h; j++) {
+ for (i = 0; i < w; i++) {
+ switch (n_colors) {
+ case 0:
+ bg = irow[i];
+ n_colors = 1;
+ break;
+ case 1:
+ if (irow[i] != bg) {
+ fg = irow[i];
+ n_colors = 2;
+ }
+ break;
+ case 2:
+ if (irow[i] != bg && irow[i] != fg) {
+ n_colors = 3;
+ } else {
+ if (irow[i] == bg)
+ bg_count++;
+ else if (irow[i] == fg)
+ fg_count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (n_colors > 2)
+ break;
+ irow += vs->ds->linesize / sizeof(pixel_t);
+ }
+
+ if (n_colors > 1 && fg_count > bg_count) {
+ pixel_t tmp = fg;
+ fg = bg;
+ bg = tmp;
+ }
+
+ if (!*has_bg || *last_bg != bg) {
+ flags |= 0x02;
+ *has_bg = 1;
+ *last_bg = bg;
+ }
+
+ if (!*has_fg || *last_fg != fg) {
+ flags |= 0x04;
+ *has_fg = 1;
+ *last_fg = fg;
+ }
+
+ switch (n_colors) {
+ case 1:
+ n_data = 0;
+ break;
+ case 2:
+ flags |= 0x08;
+
+ irow = (pixel_t *)row;
+
+ for (j = 0; j < h; j++) {
+ int min_x = -1;
+ for (i = 0; i < w; i++) {
+ if (irow[i] == fg) {
+ if (min_x == -1)
+ min_x = i;
+ } else if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ min_x = -1;
+ }
+ }
+ if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += vs->ds->linesize / sizeof(pixel_t);
+ }
+ break;
+ case 3:
+ flags |= 0x18;
+
+ irow = (pixel_t *)row;
+
+ if (!*has_bg || *last_bg != bg)
+ flags |= 0x02;
+
+ for (j = 0; j < h; j++) {
+ int has_color = 0;
+ int min_x = -1;
+ pixel_t color;
+
+ for (i = 0; i < w; i++) {
+ if (!has_color) {
+ if (irow[i] == bg)
+ continue;
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ } else if (irow[i] != color) {
+ has_color = 0;
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->pix_bpp;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+
+ min_x = -1;
+ if (irow[i] != bg) {
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ }
+ }
+ }
+ if (has_color) {
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->pix_bpp;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += vs->ds->linesize / sizeof(pixel_t);
+ }
+
+ /* A SubrectsColoured subtile invalidates the foreground color */
+ *has_fg = 0;
+ if (n_data > (w * h * sizeof(pixel_t))) {
+ n_colors = 4;
+ flags = 0x01;
+ *has_bg = 0;
+
+ /* we really don't have to invalidate either the bg or fg
+ but we've lost the old values. oh well. */
+ }
+ default:
+ break;
+ }
+
+ if (n_colors > 3) {
+ flags = 0x01;
+ *has_fg = 0;
+ *has_bg = 0;
+ n_colors = 4;
+ }
+
+ vnc_write_u8(vs, flags);
+ if (n_colors < 4) {
+ if (flags & 0x02)
+ vs->write_pixels(vs, last_bg, sizeof(pixel_t));
+ if (flags & 0x04)
+ vs->write_pixels(vs, last_fg, sizeof(pixel_t));
+ if (n_subtiles) {
+ vnc_write_u8(vs, n_subtiles);
+ vnc_write(vs, data, n_data);
+ }
+ } else {
+ for (j = 0; j < h; j++) {
+ vs->write_pixels(vs, row, w * vs->depth);
+ row += vs->ds->linesize;
+ }
+ }
+}
+
+#undef NAME
+#undef pixel_t
+#undef CONCAT_I
+#undef CONCAT
diff --git a/x86_64.ld b/x86_64.ld
new file mode 100644
index 0000000..878dafb
--- /dev/null
+++ b/x86_64.ld
@@ -0,0 +1,171 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib64");
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x90909090
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x90909090
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x90909090
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN (0x100000) - ((0x100000 - .) & (0x100000 - 1)); . = DATA_SEGMENT_ALIGN (0x100000, 0x1000);
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(64 / 8);
+ }
+ . = ALIGN(64 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}